You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the example below, I implemented __getstate__(cls) that returns empty dict
in order not to pickle cls.INSTANCES and cls.LOCK members of metaclass SingletonMeta.
But it seems that cloudpickle still tries to pickle those members
and fails to pickle since threading.RLock is not picklable.
importcloudpicklefromthreadingimportRLockclassSingletonMeta(type):
def__new__(cls, name, bases, namespace):
returnsuper().__new__(cls, name, bases, namespace)
def__init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
cls.INSTANCES= {}
cls.LOCK=RLock()
def__call__(cls, *args, **kwargs):
key=cls.__getkey__(*args, **kwargs)
key=tuple(key.items())
ifkeynotincls.INSTANCES:
withcls.LOCK:
ifkeynotincls.INSTANCES:
cls.INSTANCES[key] =super().__call__(*args, **kwargs)
instance=cls.INSTANCES[key]
returninstancedef__getstate__(cls):
# don't want to pickle cls.INSTANCES and cls.LOCKreturn {}
def__setstate__(cls, state):
passclassSingletonClass(metaclass=SingletonMeta):
def__init__(self, key):
self.key=keyself.transient= [self.key]
def__getstate__(self):
state=self.__dict__.copy()
delstate["transient"]
returnstatedef__setstate__(self, state):
forname, valueinstate.items():
setattr(self, name, value)
self.transient= [self.key]
@classmethoddef__getkey__(cls, key):
return {"key": key}
# singleton implementation works fine as long as metaclass = SingletonMetadeftest_singleton():
singleton_instance1=SingletonClass(0)
singleton_instance2=SingletonClass(0)
singleton_instance3=SingletonClass(1)
assertsingleton_instance1issingleton_instance2assertsingleton_instance1isnotsingleton_instance3# pickling instances works fine if metaclass != SingletonMeta# but raises `TypeError: cannot pickle '_thread.RLock' object` if metaclass = SingletonMetadeftest_singleton_pickle():
singleton_instance1=SingletonClass(0)
singleton_instance1_loaded=cloudpickle.loads(
cloudpickle.dumps(singleton_instance1)
)
assertsingleton_instance1isnotsingleton_instance1_loadedassertsingleton_instance1.key==singleton_instance1_loaded.keyassertsingleton_instance1.transient==singleton_instance1_loaded.transientif__name__=="__main__":
# singleton implementation works fine as long as metaclass = SingletonMetatest_singleton()
# pickling instances works fine if metaclass != SingletonMeta# but raises `TypeError: cannot pickle '_thread.RLock' object` if metaclass = SingletonMetatest_singleton_pickle()
# raises "TypeError: cannot pickle '_thread.RLock' object"cloudpickle.dumps(test_singleton)
The text was updated successfully, but these errors were encountered:
Hi, thanks for the report. The issue lies in the fact that cloudpickle does not override pickles logic of treating classes deriving from custom metaclasses, not as instances of a metaclass, but as traditional classes, for which __getstate__ is not part of any reducing spec (since there is no official spec on reducing dynamically created classes, only an unofficial cloudpickle implementation).
To fix your case, I suspect (my reasoning comes from pickle's save logic, on which cloudpickle relies) you need a custom reducer entry within copyreg.dispatch_table for SingletonMeta instances which can piggy-back on cloudpickle's reducer for dynamically created classes, _dynamic_class_reduce. Only problem is that this function is in theory private, so it might be removed without notice.
In the example below, I implemented
__getstate__(cls)
that returns empty dictin order not to pickle
cls.INSTANCES
andcls.LOCK
members of metaclassSingletonMeta
.But it seems that
cloudpickle
still tries to pickle those membersand fails to pickle since
threading.RLock
is not picklable.The text was updated successfully, but these errors were encountered: