[cffLib] Fix RecursionError in BaseDict.__getattr__ when unpickling

We need to raise AttributeError for non-existing dunder methods like
'__deepcopy__' or '__getstate__', because deepcopy() and pickle.load()
test for these on the instance using getattr() and treat the resulting
AttributeError as a signal that the object doesn't implement these custom
hooks. If we don't do that, we enter an infinite recursion as we attempt
to look up the missing dunder methods in the 'rawDict' dictionary,
because 'rawDict' is set inside __init__, but __init__ is not invoked
while unpickling (only __new__ is); thus self.rawDict is also missing
and __getattr__ is invoked with argument 'rawDict' again and again until
it crashes with RecursionError. Phew.

Fixes https://github.com/fonttools/fonttools/pull/1488
This commit is contained in:
Cosimo Lupo 2019-02-06 01:01:54 +01:00
parent 8832062c40
commit 649dc49dba

View File

@ -2242,6 +2242,12 @@ class BaseDict(object):
return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
def __getattr__(self, name):
if name[:2] == name[-2:] == "__":
# to make deepcopy() and pickle.load() work, we need to signal with
# AttributeError that dunder methods like '__deepcopy__' or '__getstate__'
# aren't implemented. For more details, see:
# https://github.com/fonttools/fonttools/pull/1488
raise AttributeError(name)
value = self.rawDict.get(name, None)
if value is None:
value = self.defaults.get(name)