DotDict
DotDict copied to clipboard
Broken in Python 3.11
$ pip3 install -U --user attr-dot-dict
Collecting attr-dot-dict
Obtaining dependency information for attr-dot-dict from https://files.pythonhosted.org/packages/af/ab/3c717181e61cfa97b71e6e01d072725c2d323350b408ab43547d02bc19ad/attr_dot_dict-0.1.0-py3-none-any.whl.metadata
Downloading attr_dot_dict-0.1.0-py3-none-any.whl.metadata (42 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 42.0/42.0 kB 1.6 MB/s eta 0:00:00
Downloading attr_dot_dict-0.1.0-py3-none-any.whl (27 kB)
Installing collected packages: attr-dot-dict
Successfully installed attr-dot-dict-0.1.0
$ python3
Python 3.11.4 (main, Jun 8 2023, 19:12:32) [Clang 15.0.7 (Red Hat 15.0.7-2.el9)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from dotdict import DotDict as dd
>>> a = dd()
>>> a.b.c.d = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/chaz/.local/lib/python3.11/site-packages/dotdict.py", line 24, in __getattr__
raise AttributeError("No attribute '%s'" % __name)
AttributeError: No attribute 'c'
>>>
I'm curious to see what exactly changed in 3.11, because after delving deeper into the interpreter and such, I see a lot of "changed in 3.11".
I don't think it was 3.11, but the changes introduced in commit 9893e1a.
Commits prior to that one work as expected running 3.11.
9893e1a fails with a recursion error and the current commit (eed0e84) fails with the attribute error above.
Technically the current commit works up to the depth of c
(a.b.c = 1
), but fails at anything deeper than that like a.b.c.d = 1
.
It appears the not self._temp
condition in the if statement of DotDict.__getattr__
is the culprit:
def __getattr__(self, __name: str) -> Any: if __name not in self.__dict__ and not self._temp: self.__dict__[__name] = DotDict(temp=True, key=__name, parent=self) else: del self._parent.__dict__[self._key] raise AttributeError("No attribute '%s'" % __name) return self.__dict__[__name]
When doing a.b.c.d = 1
, b
gets added to a
, but with temp=True
.
So then when it comes to c
, since self._temp
is True
for b
, the else statement executes and the error is raised.