--------------------------------------------------------------------------- RecursionError Traceback (most recent call last) /var/folders/vv/syvp2m7117s_1y8m9bwlj28m0000gq/T/ipykernel_45310/3176737333.py in <module> 9 10 ---> 11 t = Table('tbl') 12 t.column /var/folders/vv/syvp2m7117s_1y8m9bwlj28m0000gq/T/ipykernel_45310/3176737333.py in __init__(self, name) 3 class Table: 4 def __init__(self, name) -> None: ----> 5 store_attr() 6 7 def __getattr__(self, __name: str): /Applications/Anaconda3/envs/py37/lib/python3.7/site-packages/fastcore/basics.py in store_attr(names, self, but, cast, store_args, **attrs) 397 if self: args = ('self', *args) 398 else: self = fr.f_locals[args[0]] --> 399 if store_args is None: store_args = not hasattr(self,'__slots__') 400 if store_args and not hasattr(self, '__stored_args__'): self.__stored_args__ = {} 401 anno = annotations(self) if cast else {} /var/folders/vv/syvp2m7117s_1y8m9bwlj28m0000gq/T/ipykernel_45310/3176737333.py in __getattr__(self, _Table__name) 6 7 def __getattr__(self, __name: str): ----> 8 return f"{self.name}.{__name}" 9 10 ... last 1 frames repeated, from the frame below ... /var/folders/vv/syvp2m7117s_1y8m9bwlj28m0000gq/T/ipykernel_45310/3176737333.py in __getattr__(self, _Table__name) 6 7 def __getattr__(self, __name: str): ----> 8 return f"{self.name}.{__name}" 9 10 RecursionError: maximum recursion depth exceeded while calling a Python object
Infinite Recursion When Using store_attr and Overwriting __getattr__
Python
Fastai
An infinite recursion when overwriting
__getattr__ while store_attr is used.
If we take a look at store_attr, we see that the problem occurs at the step where store_attr() calls hasattr(self, '__slots__'), which calls __getattr__ when __slots__ is not available. In fact, store_attr calls a few attributes that start with __.
def store_attr(names=None, self=None, but='', cast=False, store_args=None, **attrs):
"Store params named in comma-separated `names` from calling context into attrs in `self`"
fr = sys._getframe(1)
args = argnames(fr, True)
if self: args = ('self', *args)
else: self = fr.f_locals[args[0]]
if store_args is None: store_args = not hasattr(self,'__slots__')
if store_args and not hasattr(self, '__stored_args__'): self.__stored_args__ = {}
anno = annotations(self) if cast else {}
if names and isinstance(names,str): names = re.split(', *', names)
ns = names if names is not None else getattr(self, '__slots__', args[1:])
added = {n:fr.f_locals[n] for n in ns}
attrs = {**attrs, **added}
if isinstance(but,str): but = re.split(', *', but)
attrs = {k:v for k,v in attrs.items() if k not in but}
return _store_attr(self, anno, **attrs)Therefore, if we want to use store_attr(), when overwriting __getattr__, we need to protect those called by store_attr(), otherwise there will be an infinite loop.