classification
Title: SimpleNamespace accepts non-string keyword names
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.7, Python 3.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: eric.snow, r.david.murray, rhettinger, serhiy.storchaka, terry.reedy, vstinner
Priority: normal Keywords: patch

Created on 2017-10-01 06:36 by serhiy.storchaka, last changed 2017-10-07 20:54 by serhiy.storchaka. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 3909 merged serhiy.storchaka, 2017-10-06 19:32
PR 3920 merged python-dev, 2017-10-07 20:00
Messages (8)
msg303450 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-01 06:36
Usually non-string keyword names are rejected.

>>> def f(**kwargs): pass                                                                                                                                                                                                                    
...                                                                                                                                                                                                                                          
>>> f(**{0:0})
Traceback (most recent call last):                                                                                                                                                                                                           
  File "<stdin>", line 1, in <module>                                                                                                                                                                                                        
TypeError: f() keywords must be strings                                                                                                                                                                                                      
>>> dict(**{0:0})
Traceback (most recent call last):                                                                                                                                                                                                           
  File "<stdin>", line 1, in <module>                                                                                                                                                                                                        
TypeError: keywords must be strings                                                                                                                                                                                                          

There are checks in multiple places that satisfy this: in _PyEval_EvalCodeWithName(), PyArg_ValidateKeywordArguments(), PyArg_ParseTupleAndKeywords(), etc.

But SimpleNamespace is an exception.

>>> from types import SimpleNamespace
>>> SimpleNamespace(**{0:0})
namespace()

Non-string keys are omitted in the repr. Wouldn't be better to add also the check that keyword names for SimpleNamespace constructor are strings?

I don't know how classify this issue, as a bug or an enhancement.

Based on the StackOverflow question: https://stackoverflow.com/questions/46164770/accepting-integers-as-keys-of-kwargs.
msg303461 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-10-01 14:29
Well, it would be an "unenhancement" (reducing functionality :).  

What I think it would be classed as is an API bug fix, and those generally can be made only in feature releases.  I can imagine code depending on this ability, since we have had code in the wild that depends on the ability to put non-string keys in an object's __dict__ even before SimpleNamespace existed.

(By the way, dir is broken if an object's __dict__ contains non-string keys.)
msg303463 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-10-01 14:33
Sorry, not broken, it just raises a type error, which is appropriate :)
msg303467 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-10-01 16:09
> Wouldn't be better to add also the check that keyword names
> for SimpleNamespace constructor are strings?

Yes.

> I don't know how classify this issue, as a bug or an enhancement.

It is arguably a bug fix.  +0 for putting this in Python 3.6.
msg303521 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-02 09:57
Please reject non-string keys. I don't see any good reason to use non-string, and it's painful to support such CPython implementation details in other Python implementations.
msg303843 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-10-06 18:52
After reading the doc entry for SimpleNamespace, I see running 'SimpleNamespace(**{0:0})' as a bug because doing so results in an object than contradicts the doc.

1. "A simple object subclass that provides attribute access to its namespace, as well as a meaningful repr. Unlike ... you can ... delete attributes."

But, after 'sn = SimpleNamespace(**{0:0})', sn.0 is a SyntaxError and getattr(sn, 0) raises 'TypeError: getattr(): attribute name must be string'.  As already noted, the 'attribute' does not appear in repr(sn).  'del sn.0' and 'delattr(sn, 0)' also fail.  If this is a feature, it is extremely buggy.

2. "The type is roughly equivalent to the following code:"

class SimpleNamespace:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
        keys = sorted(self.__dict__)
        items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
        return "{}({})".format(type(self).__name__, ", ".join(items))

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

With this definition, SimpleNamespace(**{0:0}) raises TypeError.  To me, running versus raising is outside the bounds of 'roughly equivalent'.
msg303891 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-07 19:59
New changeset 79ba471488b936abda5ba5234b1ea90cbc94cae6 by Serhiy Storchaka in branch 'master':
bpo-31655: Validate keyword names in SimpleNamespace constructor. (#3909)
https://github.com/python/cpython/commit/79ba471488b936abda5ba5234b1ea90cbc94cae6
msg303892 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-07 20:53
New changeset cae6e4775b37c412609d3a0d303c0831ff0012ff by Serhiy Storchaka (Miss Islington (bot)) in branch '3.6':
[3.6] bpo-31655: Validate keyword names in SimpleNamespace constructor. (GH-3909) (#3920)
https://github.com/python/cpython/commit/cae6e4775b37c412609d3a0d303c0831ff0012ff
History
Date User Action Args
2017-10-07 20:54:37serhiy.storchakasetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2017-10-07 20:53:00serhiy.storchakasetmessages: + msg303892
2017-10-07 20:00:48python-devsetpull_requests: + pull_request3893
2017-10-07 19:59:39serhiy.storchakasetmessages: + msg303891
2017-10-06 19:33:42serhiy.storchakasettype: behavior
versions: + Python 3.6
2017-10-06 19:32:24serhiy.storchakasetkeywords: + patch
stage: patch review
pull_requests: + pull_request3881
2017-10-06 18:52:51terry.reedysetnosy: + terry.reedy
messages: + msg303843
2017-10-02 09:57:13vstinnersetmessages: + msg303521
versions: + Python 3.7
2017-10-01 16:09:10rhettingersetnosy: + rhettinger
messages: + msg303467
2017-10-01 14:33:56r.david.murraysetmessages: + msg303463
2017-10-01 14:30:06r.david.murraysetnosy: + eric.snow
2017-10-01 14:29:30r.david.murraysetnosy: + r.david.murray
messages: + msg303461
2017-10-01 06:36:41serhiy.storchakacreate