6565SliceType = slice
6666TypeType = type # 'new-style' classes #XXX: unregistered
6767XRangeType = range
68- from types import MappingProxyType as DictProxyType
68+ from types import MappingProxyType as DictProxyType , new_class
6969from pickle import DEFAULT_PROTOCOL , HIGHEST_PROTOCOL , PickleError , PicklingError , UnpicklingError
7070import __main__ as _main_module
7171import marshal
7272import gc
7373# import zlib
74+ import abc
7475import dataclasses
7576from weakref import ReferenceType , ProxyType , CallableProxyType
7677from collections import OrderedDict
78+ from enum import Enum , EnumMeta
7779from functools import partial
7880from operator import itemgetter , attrgetter
7981GENERATOR_FAIL = False
@@ -1669,6 +1671,35 @@ def save_module(pickler, obj):
16691671 logger .trace (pickler , "# M2" )
16701672 return
16711673
1674+ # The following function is based on '_extract_class_dict' from 'cloudpickle'
1675+ # Copyright (c) 2012, Regents of the University of California.
1676+ # Copyright (c) 2009 `PiCloud, Inc. <http://www.picloud.com>`_.
1677+ # License: https://github.com/cloudpipe/cloudpickle/blob/master/LICENSE
1678+ def _get_typedict_type (cls , clsdict , attrs , postproc_list ):
1679+ """Retrieve a copy of the dict of a class without the inherited methods"""
1680+ if len (cls .__bases__ ) == 1 :
1681+ inherited_dict = cls .__bases__ [0 ].__dict__
1682+ else :
1683+ inherited_dict = {}
1684+ for base in reversed (cls .__bases__ ):
1685+ inherited_dict .update (base .__dict__ )
1686+ to_remove = []
1687+ for name , value in dict .items (clsdict ):
1688+ try :
1689+ base_value = inherited_dict [name ]
1690+ if value is base_value :
1691+ to_remove .append (name )
1692+ except KeyError :
1693+ pass
1694+ for name in to_remove :
1695+ dict .pop (clsdict , name )
1696+
1697+ if issubclass (type (cls ), type ):
1698+ clsdict .pop ('__dict__' , None )
1699+ clsdict .pop ('__weakref__' , None )
1700+ # clsdict.pop('__prepare__', None)
1701+ return clsdict , attrs
1702+
16721703@register (TypeType )
16731704def save_type (pickler , obj , postproc_list = None ):
16741705 if obj in _typemap :
@@ -1680,15 +1711,22 @@ def save_type(pickler, obj, postproc_list=None):
16801711 elif obj .__bases__ == (tuple ,) and all ([hasattr (obj , attr ) for attr in ('_fields' ,'_asdict' ,'_make' ,'_replace' )]):
16811712 # special case: namedtuples
16821713 logger .trace (pickler , "T6: %s" , obj )
1714+
1715+ obj_name = getattr (obj , '__qualname__' , getattr (obj , '__name__' , None ))
1716+ if obj .__name__ != obj_name :
1717+ if postproc_list is None :
1718+ postproc_list = []
1719+ postproc_list .append ((setattr , (obj , '__qualname__' , obj_name )))
1720+
16831721 if not obj ._field_defaults :
1684- pickler . save_reduce (_create_namedtuple , (obj .__name__ , obj ._fields , obj .__module__ ), obj = obj )
1722+ _save_with_postproc ( pickler , (_create_namedtuple , (obj .__name__ , obj ._fields , obj .__module__ )) , obj = obj , postproc_list = postproc_list )
16851723 else :
16861724 defaults = [obj ._field_defaults [field ] for field in obj ._fields if field in obj ._field_defaults ]
1687- pickler . save_reduce (_create_namedtuple , (obj .__name__ , obj ._fields , obj .__module__ , defaults ), obj = obj )
1725+ _save_with_postproc ( pickler , (_create_namedtuple , (obj .__name__ , obj ._fields , obj .__module__ , defaults )) , obj = obj , postproc_list = postproc_list )
16881726 logger .trace (pickler , "# T6" )
16891727 return
16901728
1691- # special cases: NoneType, NotImplementedType, EllipsisType
1729+ # special cases: NoneType, NotImplementedType, EllipsisType, EnumMeta
16921730 elif obj is type (None ):
16931731 logger .trace (pickler , "T7: %s" , obj )
16941732 #XXX: pickler.save_reduce(type, (None,), obj=obj)
@@ -1702,35 +1740,64 @@ def save_type(pickler, obj, postproc_list=None):
17021740 logger .trace (pickler , "T7: %s" , obj )
17031741 pickler .save_reduce (type , (Ellipsis ,), obj = obj )
17041742 logger .trace (pickler , "# T7" )
1743+ elif obj is EnumMeta :
1744+ logger .trace (pickler , "T7: %s" , obj )
1745+ pickler .write (GLOBAL + b'enum\n EnumMeta\n ' )
1746+ logger .trace (pickler , "# T7" )
17051747
17061748 else :
17071749 obj_name = getattr (obj , '__qualname__' , getattr (obj , '__name__' , None ))
17081750 _byref = getattr (pickler , '_byref' , None )
17091751 obj_recursive = id (obj ) in getattr (pickler , '_postproc' , ())
17101752 incorrectly_named = not _locate_function (obj , pickler )
17111753 if not _byref and not obj_recursive and incorrectly_named : # not a function, but the name was held over
1754+ if postproc_list is None :
1755+ postproc_list = []
1756+
17121757 # thanks to Tom Stepleton pointing out pickler._session unneeded
17131758 logger .trace (pickler , "T2: %s" , obj )
1714- _dict = obj .__dict__ .copy () # convert dictproxy to dict
1759+ _dict , attrs = _get_typedict_type (obj , obj .__dict__ .copy (), None , postproc_list ) # copy dict proxy to a dict
1760+
17151761 #print (_dict)
17161762 #print ("%s\n%s" % (type(obj), obj.__name__))
17171763 #print ("%s\n%s" % (obj.__bases__, obj.__dict__))
17181764 slots = _dict .get ('__slots__' , ())
1719- if type (slots ) == str : slots = (slots ,) # __slots__ accepts a single string
1765+ if type (slots ) == str :
1766+ # __slots__ accepts a single string
1767+ slots = (slots ,)
1768+
17201769 for name in slots :
1721- del _dict [name ]
1722- _dict .pop ('__dict__' , None )
1723- _dict .pop ('__weakref__' , None )
1724- _dict .pop ('__prepare__' , None )
1725- if obj_name != obj .__name__ :
1726- if postproc_list is None :
1727- postproc_list = []
1728- postproc_list .append ((setattr , (obj , '__qualname__' , obj_name )))
1729- _save_with_postproc (pickler , (_create_type , (
1730- type (obj ), obj .__name__ , obj .__bases__ , _dict
1731- )), obj = obj , postproc_list = postproc_list )
1770+ _dict .pop (name , None )
1771+
1772+ qualname = getattr (obj , '__qualname__' , None )
1773+ if attrs is not None :
1774+ for k , v in attrs .items ():
1775+ postproc_list .append ((setattr , (obj , k , v )))
1776+ # TODO: Consider using the state argument to save_reduce?
1777+ if qualname is not None :
1778+ postproc_list .append ((setattr , (obj , '__qualname__' , qualname )))
1779+
1780+ if not hasattr (obj , '__orig_bases__' ):
1781+ _save_with_postproc (pickler , (_create_type , (
1782+ type (obj ), obj .__name__ , obj .__bases__ , _dict
1783+ )), obj = obj , postproc_list = postproc_list )
1784+ else :
1785+ # This case will always work, but might be overkill.
1786+ _metadict = {
1787+ 'metaclass' : type (obj )
1788+ }
1789+
1790+ if _dict :
1791+ _dict_update = PartialType (_setitems , source = _dict )
1792+ else :
1793+ _dict_update = None
1794+
1795+ _save_with_postproc (pickler , (new_class , (
1796+ obj .__name__ , obj .__orig_bases__ , _metadict , _dict_update
1797+ )), obj = obj , postproc_list = postproc_list )
17321798 logger .trace (pickler , "# T2" )
17331799 else :
1800+ obj_name = getattr (obj , '__qualname__' , getattr (obj , '__name__' , None ))
17341801 logger .trace (pickler , "T4: %s" , obj )
17351802 if incorrectly_named :
17361803 warnings .warn (
@@ -1753,14 +1820,17 @@ def save_type(pickler, obj, postproc_list=None):
17531820 return
17541821
17551822@register (property )
1823+ @register (abc .abstractproperty )
17561824def save_property (pickler , obj ):
17571825 logger .trace (pickler , "Pr: %s" , obj )
1758- pickler .save_reduce (property , (obj .fget , obj .fset , obj .fdel , obj .__doc__ ),
1826+ pickler .save_reduce (type ( obj ) , (obj .fget , obj .fset , obj .fdel , obj .__doc__ ),
17591827 obj = obj )
17601828 logger .trace (pickler , "# Pr" )
17611829
17621830@register (staticmethod )
17631831@register (classmethod )
1832+ @register (abc .abstractstaticmethod )
1833+ @register (abc .abstractclassmethod )
17641834def save_classmethod (pickler , obj ):
17651835 logger .trace (pickler , "Cm: %s" , obj )
17661836 orig_func = obj .__func__
0 commit comments