@@ -642,10 +642,23 @@ def save_dynamic_class(self, obj):
642642 for k in obj .__slots__ :
643643 clsdict .pop (k , None )
644644
645- # If type overrides __dict__ as a property, include it in the type
646- # kwargs. In Python 2, we can't set this attribute after construction.
645+ # A class __dict__ is part of the class state. At unpickling time, it
646+ # must be *initialized* (in an empty state) during class creation and
647+ # updated during class re-hydratation.
648+ # However, a class __dict__ is read-only, and does not support direct
649+ # item assignement. Instead, way to update a class __dict__ is to call
650+ # setattr(k, v) on the underlying class, which has the same effect.
651+ # There is one corner case: if the __dict__ class has itself a
652+ # "__dict__" key (this means that the class likely overrides the
653+ # __dict__ property of its instances), setattr("__dict__", v) will try
654+ # to modify the read-only class __dict__ instead, and fail. As a
655+ # result, if it exists, the class __dict__ must contain its __dict__
656+ # item when it is initialized and fed to the class reconstructor.
647657 __dict__ = clsdict .pop ('__dict__' , None )
648- if isinstance (__dict__ , property ):
658+ if __dict__ is not None :
659+ # Native pickle memoization of dict objects prevents us from
660+ # reference cycles even if __dict__ is now saved before obj is
661+ # memoized.
649662 type_kwargs ['__dict__' ] = __dict__
650663
651664 save = self .save
0 commit comments