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,63 @@ 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 :
1707- obj_name = getattr (obj , '__qualname__' , getattr (obj , '__name__' , None ))
17081749 _byref = getattr (pickler , '_byref' , None )
17091750 obj_recursive = id (obj ) in getattr (pickler , '_postproc' , ())
17101751 incorrectly_named = not _locate_function (obj , pickler )
17111752 if not _byref and not obj_recursive and incorrectly_named : # not a function, but the name was held over
1753+ if postproc_list is None :
1754+ postproc_list = []
1755+
17121756 # thanks to Tom Stepleton pointing out pickler._session unneeded
17131757 logger .trace (pickler , "T2: %s" , obj )
1714- _dict = obj .__dict__ .copy () # convert dictproxy to dict
1758+ _dict , attrs = _get_typedict_type (obj , obj .__dict__ .copy (), None , postproc_list ) # copy dict proxy to a dict
1759+
17151760 #print (_dict)
17161761 #print ("%s\n%s" % (type(obj), obj.__name__))
17171762 #print ("%s\n%s" % (obj.__bases__, obj.__dict__))
17181763 slots = _dict .get ('__slots__' , ())
1719- if type (slots ) == str : slots = (slots ,) # __slots__ accepts a single string
1764+ if type (slots ) == str :
1765+ # __slots__ accepts a single string
1766+ slots = (slots ,)
1767+
17201768 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 )
1769+ _dict .pop (name , None )
1770+
1771+ qualname = getattr (obj , '__qualname__' , None )
1772+ if attrs is not None :
1773+ for k , v in attrs .items ():
1774+ postproc_list .append ((setattr , (obj , k , v )))
1775+ # TODO: Consider using the state argument to save_reduce?
1776+ if qualname is not None :
1777+ postproc_list .append ((setattr , (obj , '__qualname__' , qualname )))
1778+
1779+ if not hasattr (obj , '__orig_bases__' ):
1780+ _save_with_postproc (pickler , (_create_type , (
1781+ type (obj ), obj .__name__ , obj .__bases__ , _dict
1782+ )), obj = obj , postproc_list = postproc_list )
1783+ else :
1784+ # This case will always work, but might be overkill.
1785+ _metadict = {
1786+ 'metaclass' : type (obj )
1787+ }
1788+
1789+ if _dict :
1790+ _dict_update = PartialType (_setitems , source = _dict )
1791+ else :
1792+ _dict_update = None
1793+
1794+ _save_with_postproc (pickler , (new_class , (
1795+ obj .__name__ , obj .__orig_bases__ , _metadict , _dict_update
1796+ )), obj = obj , postproc_list = postproc_list )
17321797 logger .trace (pickler , "# T2" )
17331798 else :
1799+ obj_name = getattr (obj , '__qualname__' , getattr (obj , '__name__' , None ))
17341800 logger .trace (pickler , "T4: %s" , obj )
17351801 if incorrectly_named :
17361802 warnings .warn (
@@ -1753,14 +1819,17 @@ def save_type(pickler, obj, postproc_list=None):
17531819 return
17541820
17551821@register (property )
1822+ @register (abc .abstractproperty )
17561823def save_property (pickler , obj ):
17571824 logger .trace (pickler , "Pr: %s" , obj )
1758- pickler .save_reduce (property , (obj .fget , obj .fset , obj .fdel , obj .__doc__ ),
1825+ pickler .save_reduce (type ( obj ) , (obj .fget , obj .fset , obj .fdel , obj .__doc__ ),
17591826 obj = obj )
17601827 logger .trace (pickler , "# Pr" )
17611828
17621829@register (staticmethod )
17631830@register (classmethod )
1831+ @register (abc .abstractstaticmethod )
1832+ @register (abc .abstractclassmethod )
17641833def save_classmethod (pickler , obj ):
17651834 logger .trace (pickler , "Cm: %s" , obj )
17661835 orig_func = obj .__func__
0 commit comments