Skip to content

Commit 5d89947

Browse files
authored
Merge pull request #450 from jrbourbeau/abstracts
Add support for `abc.abstract*` methods
2 parents 7ddb089 + 2f97c6d commit 5d89947

File tree

3 files changed

+113
-1
lines changed

3 files changed

+113
-1
lines changed

CHANGES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
2.1.0 (in development)
22
======================
33

4-
4+
- Support for pickling `abc.abstractproperty`, `abc.abstractclassmethod`,
5+
and `abc.abstractstaticmethod`.
6+
([PR #450](https://github.com/cloudpipe/cloudpickle/pull/450))
57

68
2.0.0
79
=====

cloudpickle/cloudpickle_fast.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,10 @@ class CloudPickler(Pickler):
534534
_dispatch_table[type(OrderedDict().keys())] = _odict_keys_reduce
535535
_dispatch_table[type(OrderedDict().values())] = _odict_values_reduce
536536
_dispatch_table[type(OrderedDict().items())] = _odict_items_reduce
537+
_dispatch_table[abc.abstractmethod] = _classmethod_reduce
538+
_dispatch_table[abc.abstractclassmethod] = _classmethod_reduce
539+
_dispatch_table[abc.abstractstaticmethod] = _classmethod_reduce
540+
_dispatch_table[abc.abstractproperty] = _property_reduce
537541

538542

539543
dispatch_table = ChainMap(_dispatch_table, copyreg.dispatch_table)

tests/cloudpickle_test.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,101 @@ def some_classmethod(cls):
11841184
def some_staticmethod():
11851185
"""A staticmethod"""
11861186

1187+
@property
1188+
@abc.abstractmethod
1189+
def some_property():
1190+
"""A property"""
1191+
1192+
class ConcreteClass(AbstractClass):
1193+
def some_method(self):
1194+
return 'it works!'
1195+
1196+
@classmethod
1197+
def some_classmethod(cls):
1198+
assert cls == ConcreteClass
1199+
return 'it works!'
1200+
1201+
@staticmethod
1202+
def some_staticmethod():
1203+
return 'it works!'
1204+
1205+
@property
1206+
def some_property(self):
1207+
return 'it works!'
1208+
1209+
# This abstract class is locally defined so we can safely register
1210+
# tuple in it to verify the unpickled class also register tuple.
1211+
AbstractClass.register(tuple)
1212+
1213+
concrete_instance = ConcreteClass()
1214+
depickled_base = pickle_depickle(AbstractClass, protocol=self.protocol)
1215+
depickled_class = pickle_depickle(ConcreteClass,
1216+
protocol=self.protocol)
1217+
depickled_instance = pickle_depickle(concrete_instance)
1218+
1219+
assert issubclass(tuple, AbstractClass)
1220+
assert issubclass(tuple, depickled_base)
1221+
1222+
self.assertEqual(depickled_class().some_method(), 'it works!')
1223+
self.assertEqual(depickled_instance.some_method(), 'it works!')
1224+
1225+
self.assertEqual(depickled_class.some_classmethod(), 'it works!')
1226+
self.assertEqual(depickled_instance.some_classmethod(), 'it works!')
1227+
1228+
self.assertEqual(depickled_class().some_staticmethod(), 'it works!')
1229+
self.assertEqual(depickled_instance.some_staticmethod(), 'it works!')
1230+
1231+
self.assertEqual(depickled_class().some_property, 'it works!')
1232+
self.assertEqual(depickled_instance.some_property, 'it works!')
1233+
self.assertRaises(TypeError, depickled_base)
1234+
1235+
class DepickledBaseSubclass(depickled_base):
1236+
def some_method(self):
1237+
return 'it works for realz!'
1238+
1239+
@classmethod
1240+
def some_classmethod(cls):
1241+
assert cls == DepickledBaseSubclass
1242+
return 'it works for realz!'
1243+
1244+
@staticmethod
1245+
def some_staticmethod():
1246+
return 'it works for realz!'
1247+
1248+
@property
1249+
def some_property():
1250+
return 'it works for realz!'
1251+
1252+
self.assertEqual(DepickledBaseSubclass().some_method(),
1253+
'it works for realz!')
1254+
1255+
class IncompleteBaseSubclass(depickled_base):
1256+
def some_method(self):
1257+
return 'this class lacks some concrete methods'
1258+
1259+
self.assertRaises(TypeError, IncompleteBaseSubclass)
1260+
1261+
def test_abstracts(self):
1262+
# Same as `test_abc` but using deprecated `abc.abstract*` methods.
1263+
# See https://github.com/cloudpipe/cloudpickle/issues/367
1264+
1265+
class AbstractClass(abc.ABC):
1266+
@abc.abstractmethod
1267+
def some_method(self):
1268+
"""A method"""
1269+
1270+
@abc.abstractclassmethod
1271+
def some_classmethod(cls):
1272+
"""A classmethod"""
1273+
1274+
@abc.abstractstaticmethod
1275+
def some_staticmethod():
1276+
"""A staticmethod"""
1277+
1278+
@abc.abstractproperty
1279+
def some_property(self):
1280+
"""A property"""
1281+
11871282
class ConcreteClass(AbstractClass):
11881283
def some_method(self):
11891284
return 'it works!'
@@ -1197,6 +1292,10 @@ def some_classmethod(cls):
11971292
def some_staticmethod():
11981293
return 'it works!'
11991294

1295+
@property
1296+
def some_property(self):
1297+
return 'it works!'
1298+
12001299
# This abstract class is locally defined so we can safely register
12011300
# tuple in it to verify the unpickled class also register tuple.
12021301
AbstractClass.register(tuple)
@@ -1218,6 +1317,9 @@ def some_staticmethod():
12181317

12191318
self.assertEqual(depickled_class().some_staticmethod(), 'it works!')
12201319
self.assertEqual(depickled_instance.some_staticmethod(), 'it works!')
1320+
1321+
self.assertEqual(depickled_class().some_property, 'it works!')
1322+
self.assertEqual(depickled_instance.some_property, 'it works!')
12211323
self.assertRaises(TypeError, depickled_base)
12221324

12231325
class DepickledBaseSubclass(depickled_base):
@@ -1233,6 +1335,10 @@ def some_classmethod(cls):
12331335
def some_staticmethod():
12341336
return 'it works for realz!'
12351337

1338+
@property
1339+
def some_property(self):
1340+
return 'it works for realz!'
1341+
12361342
self.assertEqual(DepickledBaseSubclass().some_method(),
12371343
'it works for realz!')
12381344

0 commit comments

Comments
 (0)