Is there some way I can use a hyphen in the name of a keyword argument as in the example below?
set_cookie("cookieName", "cookieValue", Max-Age=0, Expires="")
Is there some way I can use a hyphen in the name of a keyword argument as in the example below?
set_cookie("cookieName", "cookieValue", Max-Age=0, Expires="")
No. Hyphens are never valid characters in Python names. That includes function names, variable names, and argument names. Use an underscore instead.
Really, you should just follow the PEP 8 naming conventions; theyâre standard across most Python code. That means for argument names, you should use all lowercase with underscores for spaces.
You could have the cookie attributes as a dictionary instead.
That is unfortunate because in the case of http cookies I want to pass optional attributes; one of which may be âMax-Ageâ.
options = {
"Max-Age": 0,
"Expires": ""
}
set_cookie("cookieName", "cookieValue", **options)
@elis.byberi I really wouldnât do this. **kwargs is intended to let you pass keyword arguments, so it seems like a bad idea to expect things that arenât valid keyword arguments.
If you adopted this signature, anyone using set_cookie would have to create a dictionary and unpack it. You might as well save them the step of unpacking by taking a dictionary argument directly.
Yes, I did unpack it erroneously.
options = {
"Max-Age": 0,
"Expires": ""
}
set_cookie("cookieName", "cookieValue", options) # not **options
You will need to format the cookie string later, so it is easier to pass the optional values in a dictionary and simply iterate over them, or use a similar approach.
My workaround for this is usually to allow both. Have an optional positional argument that takes a dictionary context, then allow **kwargs as overrides.
def set_cookie(__cookie: CookieTD | None = None, /, **kwargs: Unpack[CookieTD]):
opts = __cookie or {}
opts.update(kwargs)
... # do something with the resolved cookie options
Note that you will likely need to use a deepcopy if any of the options are mutable containers (list, set, dict, etc), but this pattern has been pretty useful for me in a ton of cases. Especially when Iâm needing to allow per function/method overrides of a whole giant set of parameters. This lets me create some defaults that can be passed as the positional, then allow small changes to be passed as arbitrary keyword arguments.
Plus if the API has something that isnât a valid name, you can still pass it by unpacking it from a dictionary (which is annoying, but better in my opinion than renaming stuff and causing confusion later).
Passing invalid names to **kwargs is a bit of a code smell, I know, but at runtime theyâre just treated as a normal dictionary, so itâs up to you if you want to ignore it or not.
Itâs explicitly ok. From 6. Expressions â Python 3.14.6 documentation :
When
**expressionis used, each key in this mapping must be a string. ⌠A key need not be a Python identifier (e.g."max-temp °F"is acceptable, although it will not match any formal parameter that could be declared).
Of course, there canât be a matching parameter name (in the normal way, at least), so clearly the function signature has to have a ** formal parameter.
The ability to provide arguments by name in a call, using names not explicitly declared in the signature and the id=value syntax, is really just a convenience for loading a dictionary to give to the ** argument of a call. Non-identifier keys turn up in some numerical libraries.
In this particular context, it might be worth looking at http.cookies â HTTP state management â Python 3.14.6 documentation and passing one of those.
The hyphen means subtract so itâs not usable in a name.
a = b-c
So Max-Age is Max subtract Age.
Thatâs what I was referring to as the âcode smellâ. Mainly since I was using an unpacked TypedDict to hint the kwargs. Which means the user will possibly get confusing hints. I donât think this is really an issue if you provide an alternative name for that situation or just require them to unpack a dictionary.