-
-
Notifications
You must be signed in to change notification settings - Fork 30.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dict
and dict.update
treat the first argument as a mapping when it has attribute keys
without attribute __getitem__
#116938
Comments
dict(object)
and dict.update({}, object)
treat object
as mapping when it has at least keys
attributedict(object)
and dict.update({}, object)
treat object
as mapping when it has at least attribute keys
dict(object)
and dict.update({}, object)
treat object
as mapping when it has at least attribute keys
dict
and dict.update
treat the first argument as a mapping when it has attribute keys
without attribute __getitem__
Object is not a mapping, so the exception looks correct, not a bug.
I guess one is expected to know to look and find if one does not know what a mapping is. The first occurrence of |
Object is not a mapping, but it is still an iterable, so I expect
It shows that the presence of |
Since a mapping is iterable, I believe you are claiming that either Class |
Yes, I realized that class It can be fixed replacing class MySetWithKeys(set):
__slots__ = ()
def keys(self, /):
return list(map(repr, self))
my_set = MySetWithKeys([(1, 2), (3, 4)]) Raises A real world example of an iterable with For example, query returns an iterable of records of size 2 where the first index is occupied by ID and the second by the list of connected IDs: query = (
'unwind $source_ids as node_id '
'match (:Node {nodeId: node_id})--(other:Node:Entity) '
'return node_id, collect(other.nodeId) as ids'
)
result: neo4j.Result = self.session.run(query, source_ids=list(source_ids))
return dict(list(result)) Initially I used |
Just in case you don't like using a list: |
I think this can be closed. The OP's description is the actual intended and tested behavior. Here's the spec from
Note that the docstring for
The produces exactly the behavior the OP observed. If non-mapping has So, it looks to me like the behavior the OP observed is exactly what was promised. This spec have been around a very long time and almost certainly there is code that relies on the behavior. Since |
Alright, that evidence is persuasive. At this point I think the docs should be synchronized and fixed.
There, A similar note should be provided for Nethertheless, I find a bit awkward the fact that the behavior depends on a single non-dunder method. For the code below mypy does not provide any warnings to from collections.abc import Iterator, Iterable
from typing import reveal_type
class Object:
def __iter__(self, /) -> Iterator[tuple[str, int]]: ...
def keys(self, /) -> Iterable[str]: ...
d = dict(Object())
reveal_type(d) Maybe adding next overload to @overload
def __init__(self, iterable: SupportKeys, /) -> Never: ... |
We got a report from a Pydantic user facing the same issue. We support from pydantic import BaseModel
class Model(BaseModel):
keys: str
dict(Model(keys="a"))
# TypeError: 'str' object is not callable As OP said (I find a bit awkward the fact that the behavior depends on a single non-dunder method), this is indeed a source of confusion. But the backwards compatibility concerns seems valid as well. Because
And as said in this thread, maybe update the docstring of |
…garding the positional argument they accept
…g the positional argument they accept (#125213) Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
…garding the positional argument they accept (pythonGH-125213) (cherry picked from commit 21ac0a7) Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
…garding the positional argument they accept (pythonGH-125213) (cherry picked from commit 21ac0a7) Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Bug report
How to reproduce
dict(Object())
.d = {}
and thend.update(Object())
.Expected result
At the step 2 an empty dictionary is returned.
At the step 3
d
stays empty.Actual result
Both steps 2 and 3 raise a TypeError
'Object' object is not subscriptable
.CPython versions tested on:
3.10
3.11
Operating systems tested on:
Windows 21H2 (19044.1645)
Ubuntu 20.04.6 LTS
Docs of
dict
state:Unfortunately, there is no link to what is considered as a mapping object.
In typeshed both
dict
anddict.update
acceptSupportsKeysAndGetItem
, i.e., any object with attributeskeys
and__getitem__
.But the experiment above shows that only
keys
is enough. Whiletypeshed
is a bit too restrictive in the case for iterable (only iterables of 2-sized tuples are allowed, butdict
accepts any iterable of 2-sized iterables), I think just checking forkeys
is not enough.In the actual C code there is such comment:
cpython/Include/dictobject.h
Lines 39 to 43 in 2982bdb
Thus, it is intended to check the presence of two attributes.
The error is here:
cpython/Objects/dictobject.c
Lines 3426 to 3441 in 2982bdb
This code evaluates whether attribute
keys
is present. If the answer is true, callsPyDict_Merge
, and callsPyDict_MergeFromSeq2
otherwise.Linked PRs
dict
anddict.update
regarding the positional argument they accept #125213dict
anddict.update
regarding the positional argument they accept (GH-125213) #125336dict
anddict.update
regarding the positional argument they accept (GH-125213) #125337dict.update
docstring and remove redundant period fromdict
web documentation #125421The text was updated successfully, but these errors were encountered: