New submission from Christian Barcenas: I noticed an inconsistency today between the dict() documentation vs. implementation.
The documentation for the dict() built-in [1] states that the function accepts an optional positional argument that is either a mapping object [2] or an iterable object [3]. Consider the following: import collections.abc class MyIterable(object): def __init__(self): self._data = [('one', 1), ('two', 2)] def __iter__(self): return iter(self._data) class MyIterableWithKeysMethod(MyIterable): def keys(self): return "And now for something completely different" class MyIterableWithKeysAttribute(MyIterable): keys = "It's just a flesh wound!" assert issubclass(MyIterable, collections.abc.Iterable) assert issubclass(MyIterableWithKeysMethod, collections.abc.Iterable) assert issubclass(MyIterableWithKeysAttribute, collections.abc.Iterable) assert not issubclass(MyIterable, collections.abc.Mapping) assert not issubclass(MyIterableWithKeysMethod, collections.abc.Mapping) assert not issubclass(MyIterableWithKeysAttribute, collections.abc.Mapping) # OK assert dict(MyIterable()) == {'one': 1, 'two': 2} # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # TypeError: 'MyIterableWithKeysMethod' object is not subscriptable assert dict(MyIterableWithKeysMethod()) == {'one': 1, 'two': 2} # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # TypeError: attribute of type 'str' is not callable assert dict(MyIterableWithKeysAttribute()) == {'one': 1, 'two': 2} The last two assertions should not fail, and it appears that the offending code can be found in Objects/dictobject.c's dict_update_common: else if (arg != NULL) { _Py_IDENTIFIER(keys); if (_PyObject_HasAttrId(arg, &PyId_keys)) result = PyDict_Merge(self, arg, 1); else result = PyDict_MergeFromSeq2(self, arg, 1); } PyDict_Merge is used to merge key-value pairs if the optional parameter is a mapping, and PyDict_MergeFromSeq2 is used if the parameter is an iterable. My immediate thought was to substitute the _PyObject_HasAttrId call with PyMapping_Check which I believe would work in 2.7, but due to #5945 I don't think this fix would work in 3.x. Thoughts? [1] https://docs.python.org/3.6/library/stdtypes.html#dict [2] https://docs.python.org/3.6/glossary.html#term-mapping [3] https://docs.python.org/3.6/glossary.html#term-iterable ---------- assignee: docs@python components: Documentation, Interpreter Core messages: 246890 nosy: christian.barcenas, docs@python priority: normal severity: normal status: open title: dict() built-in fails on iterators with a "keys" attribute type: behavior versions: Python 2.7, Python 3.2, Python 3.3, Python 3.4, Python 3.5, Python 3.6 _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue24659> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com