Are the keys of a kwargs argument to Python function guaranteed to be type string?

A keyword argument passed directly must be a valid Python identifier, and yes it will always be treated as strings. Anything else is a SyntaxError.

f(foo=1) # Works
f($=1) # Fails
f(1=1) # Fails

Although, you can also give keyword arguments through unpacking. In this case, your keyword arguments must be strings still, but they can take any format.

Let's define a dummy function to test this.

def f(**kwargs):
    print(kwargs)

A keyword argument can contain a space or be a string of digits. It can even contain special characters.

f(**{"hello world": 'foo'}) # prints {'hello world': 'foo'}
f(**{"1": 'foo'}) # prints {'1': 'foo'}
f(**{"$": 'foo'}) # prints {'$': 'foo'}

A keyword argument must be a string. Anything else is a TypeError.

f(**{1: 'foo'}) # TypeError: f() keywords must be strings
f(**{b'foo': 1}) # TypeError: f() keywords must be strings

The keywords in kwargs should follow the rules of variable names, full_name is a valid variable name (and a valid keyword), full name is not a valid variable name (and not a valid keyword).

From PEP 362 -- Function Signature Object:

A Parameter object has the following public attributes and methods:
name : str
- The name of the parameter as a string. Must be a valid python identifier name (with the exception of POSITIONAL_ONLY parameters, which can have it set to None.)

And, from the Docs:

2.3. Identifiers and keywords:
... Within the ASCII range (U+0001..U+007F), the valid characters for identifiers are the same as in Python 2.x: the uppercase and lowercase letters A through Z, the underscore _ and, except for the first character, the digits 0 through 9. ...