[docs] [issue24659] dict() built-in fails on iterators with a "keys" attribute
Christian Barcenas
report at bugs.python.org
Sat Jul 18 10:55:00 CEST 2015
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 at python
components: Documentation, Interpreter Core
messages: 246890
nosy: christian.barcenas, docs at 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 <report at bugs.python.org>
<http://bugs.python.org/issue24659>
_______________________________________
More information about the docs
mailing list