Supporting (or not) invalid identifiers in **kwargs

There is a ticket now: Clarify status of non-identifier unpacked keyword arguments · Issue #96397 · python/cpython · GitHub

It’s not clear if the solution is a doc update or a spec change; Guido noted that the SC should decide, so someone should submit a request.

My 2 cents: disallowing everything that doesn’t work in the x(dict(foo=bar)) syntax from the x(**{'foo': bar}) syntax would break a lot of code I have in production :stuck_out_tongue:

3 Likes

@Gouvernathor This is a totally pointless discussion, for two reasons:

  1. If you’re using **kwargs in your function signature, you don’t have any way of accessing its contents except via a dictionary. So having non-identifier keys is an absolute non-issue.
  2. If you’re really bothered by this behaviour, there is a very simple check you can add to your function, and it can easily be implemented as a decorator:
def foo(**kwargs):
    if not all(map(str.isidentifier, kwargs)):
        raise ValueError("keywords must be identifiers")
    print(kwargs)
2 Likes

@b11c

  1. Yes you have, for example passing **kwargs to another function not having a **dict in its signature, and returning the result. Also, your argument would also work to include non-string keys in the kwargs dicts, would you consider that “an absolute non-issue” too ?
    And I said in the question body that adding non-string keys to the dict in the body of the function should stay allowed in any case.
  2. Sure, so ? Just because a developer can do something doesn’t mean the language shouldn’t include any check. From the top of my head, I’d cite the type constraint on the self argument, for methods accessed from the class.
    Besides, the point since quite some time here is not whether or how to add the check in the implementation, but whether to consider an implementation detail a documented feature or not.

@boxed Same, I’m not (now) saying we should break that. But if you wanted to bring your API in accordance with the documentation (or my less-permissive version of it anyway), you could do it the 3113 way, by accepting a kwargs mixed-keys dict argument instead of a **kwargs one, in any function in which you need to accept non-identifier-keyed dicts.
As I said earlier, I’m 99% sure it would even reduce the processing load of the function calls (because it won’t explode your dict and recreate one after).

1 Like

Just saying “send a dict” would not only break my API a lot, it would also make it a lot worse.

My vote would be to explicitly say kwargs are any dict with string keys. Simply document what already is the fact, and that people rely on.

5 Likes

When I look at what’s at the called method set_postfix, it appears the intent is to accept a mapping, not keyword arguments, and the choice to use **kwargs is a poor one. Perhaps I’m being too fastidious, but is there another example of use in the wild, clear of those misgivings?

@EpicWink, you were sure there was.

Azure’s SDK for blob storage response models: azure-sdk-for-python/_models.py at main · Azure/azure-sdk-for-python · GitHub

Although in this instance I think a class-method from_response_headers would have made more sense.

1 Like

Very helpful, thank you. (I’m writing the SC submission. Non-identifier keys in `**` arguments and elsewhere · Issue #142 · python/steering-council · GitHub)

They seem to be using the feature privately, but one can see clearly the __init__ looks for them.

1 Like

The SC ruled that supporting arbitrary strings is a feature of Python.

With that, I’ve started a new thread for discussing the details. See you there!

Oh, and @admins, could you un-flag the first post in this topic?

2 Likes