292 lines
7 KiB
Python
292 lines
7 KiB
Python
|
#!/usr/bin/env python
|
||
|
#
|
||
|
# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
|
||
|
# Copyright (c) 2008-2016 California Institute of Technology.
|
||
|
# Copyright (c) 2016-2023 The Uncertainty Quantification Foundation.
|
||
|
# License: 3-clause BSD. The full license text is available at:
|
||
|
# - https://github.com/uqfoundation/dill/blob/master/LICENSE
|
||
|
|
||
|
import dill
|
||
|
from enum import EnumMeta
|
||
|
import sys
|
||
|
dill.settings['recurse'] = True
|
||
|
|
||
|
# test classdefs
|
||
|
class _class:
|
||
|
def _method(self):
|
||
|
pass
|
||
|
def ok(self):
|
||
|
return True
|
||
|
|
||
|
class _class2:
|
||
|
def __call__(self):
|
||
|
pass
|
||
|
def ok(self):
|
||
|
return True
|
||
|
|
||
|
class _newclass(object):
|
||
|
def _method(self):
|
||
|
pass
|
||
|
def ok(self):
|
||
|
return True
|
||
|
|
||
|
class _newclass2(object):
|
||
|
def __call__(self):
|
||
|
pass
|
||
|
def ok(self):
|
||
|
return True
|
||
|
|
||
|
class _meta(type):
|
||
|
pass
|
||
|
|
||
|
def __call__(self):
|
||
|
pass
|
||
|
def ok(self):
|
||
|
return True
|
||
|
|
||
|
_mclass = _meta("_mclass", (object,), {"__call__": __call__, "ok": ok})
|
||
|
|
||
|
del __call__
|
||
|
del ok
|
||
|
|
||
|
o = _class()
|
||
|
oc = _class2()
|
||
|
n = _newclass()
|
||
|
nc = _newclass2()
|
||
|
m = _mclass()
|
||
|
|
||
|
if sys.hexversion < 0x03090000:
|
||
|
import typing
|
||
|
class customIntList(typing.List[int]):
|
||
|
pass
|
||
|
else:
|
||
|
class customIntList(list[int]):
|
||
|
pass
|
||
|
|
||
|
# test pickles for class instances
|
||
|
def test_class_instances():
|
||
|
assert dill.pickles(o)
|
||
|
assert dill.pickles(oc)
|
||
|
assert dill.pickles(n)
|
||
|
assert dill.pickles(nc)
|
||
|
assert dill.pickles(m)
|
||
|
|
||
|
def test_class_objects():
|
||
|
clslist = [_class,_class2,_newclass,_newclass2,_mclass]
|
||
|
objlist = [o,oc,n,nc,m]
|
||
|
_clslist = [dill.dumps(obj) for obj in clslist]
|
||
|
_objlist = [dill.dumps(obj) for obj in objlist]
|
||
|
|
||
|
for obj in clslist:
|
||
|
globals().pop(obj.__name__)
|
||
|
del clslist
|
||
|
for obj in ['o','oc','n','nc']:
|
||
|
globals().pop(obj)
|
||
|
del objlist
|
||
|
del obj
|
||
|
|
||
|
for obj,cls in zip(_objlist,_clslist):
|
||
|
_cls = dill.loads(cls)
|
||
|
_obj = dill.loads(obj)
|
||
|
assert _obj.ok()
|
||
|
assert _cls.ok(_cls())
|
||
|
if _cls.__name__ == "_mclass":
|
||
|
assert type(_cls).__name__ == "_meta"
|
||
|
|
||
|
# test NoneType
|
||
|
def test_specialtypes():
|
||
|
assert dill.pickles(type(None))
|
||
|
assert dill.pickles(type(NotImplemented))
|
||
|
assert dill.pickles(type(Ellipsis))
|
||
|
assert dill.pickles(type(EnumMeta))
|
||
|
|
||
|
from collections import namedtuple
|
||
|
Z = namedtuple("Z", ['a','b'])
|
||
|
Zi = Z(0,1)
|
||
|
X = namedtuple("Y", ['a','b'])
|
||
|
X.__name__ = "X"
|
||
|
X.__qualname__ = "X" #XXX: name must 'match' or fails to pickle
|
||
|
Xi = X(0,1)
|
||
|
Bad = namedtuple("FakeName", ['a','b'])
|
||
|
Badi = Bad(0,1)
|
||
|
Defaults = namedtuple('Defaults', ['x', 'y'], defaults=[1])
|
||
|
Defaultsi = Defaults(2)
|
||
|
|
||
|
# test namedtuple
|
||
|
def test_namedtuple():
|
||
|
assert Z is dill.loads(dill.dumps(Z))
|
||
|
assert Zi == dill.loads(dill.dumps(Zi))
|
||
|
assert X is dill.loads(dill.dumps(X))
|
||
|
assert Xi == dill.loads(dill.dumps(Xi))
|
||
|
assert Defaults is dill.loads(dill.dumps(Defaults))
|
||
|
assert Defaultsi == dill.loads(dill.dumps(Defaultsi))
|
||
|
assert Bad is not dill.loads(dill.dumps(Bad))
|
||
|
assert Bad._fields == dill.loads(dill.dumps(Bad))._fields
|
||
|
assert tuple(Badi) == tuple(dill.loads(dill.dumps(Badi)))
|
||
|
|
||
|
class A:
|
||
|
class B(namedtuple("C", ["one", "two"])):
|
||
|
'''docstring'''
|
||
|
B.__module__ = 'testing'
|
||
|
|
||
|
a = A()
|
||
|
assert dill.copy(a)
|
||
|
|
||
|
assert dill.copy(A.B).__name__ == 'B'
|
||
|
assert dill.copy(A.B).__qualname__.endswith('.<locals>.A.B')
|
||
|
assert dill.copy(A.B).__doc__ == 'docstring'
|
||
|
assert dill.copy(A.B).__module__ == 'testing'
|
||
|
|
||
|
from typing import NamedTuple
|
||
|
|
||
|
def A():
|
||
|
class B(NamedTuple):
|
||
|
x: int
|
||
|
return B
|
||
|
|
||
|
assert type(dill.copy(A()(8))).__qualname__ == type(A()(8)).__qualname__
|
||
|
|
||
|
def test_dtype():
|
||
|
try:
|
||
|
import numpy as np
|
||
|
|
||
|
dti = np.dtype('int')
|
||
|
assert np.dtype == dill.copy(np.dtype)
|
||
|
assert dti == dill.copy(dti)
|
||
|
except ImportError: pass
|
||
|
|
||
|
|
||
|
def test_array_nested():
|
||
|
try:
|
||
|
import numpy as np
|
||
|
|
||
|
x = np.array([1])
|
||
|
y = (x,)
|
||
|
assert y == dill.copy(y)
|
||
|
|
||
|
except ImportError: pass
|
||
|
|
||
|
|
||
|
def test_array_subclass():
|
||
|
try:
|
||
|
import numpy as np
|
||
|
|
||
|
class TestArray(np.ndarray):
|
||
|
def __new__(cls, input_array, color):
|
||
|
obj = np.asarray(input_array).view(cls)
|
||
|
obj.color = color
|
||
|
return obj
|
||
|
def __array_finalize__(self, obj):
|
||
|
if obj is None:
|
||
|
return
|
||
|
if isinstance(obj, type(self)):
|
||
|
self.color = obj.color
|
||
|
def __getnewargs__(self):
|
||
|
return np.asarray(self), self.color
|
||
|
|
||
|
a1 = TestArray(np.zeros(100), color='green')
|
||
|
if not dill._dill.IS_PYPY:
|
||
|
assert dill.pickles(a1)
|
||
|
assert a1.__dict__ == dill.copy(a1).__dict__
|
||
|
|
||
|
a2 = a1[0:9]
|
||
|
if not dill._dill.IS_PYPY:
|
||
|
assert dill.pickles(a2)
|
||
|
assert a2.__dict__ == dill.copy(a2).__dict__
|
||
|
|
||
|
class TestArray2(np.ndarray):
|
||
|
color = 'blue'
|
||
|
|
||
|
a3 = TestArray2([1,2,3,4,5])
|
||
|
a3.color = 'green'
|
||
|
if not dill._dill.IS_PYPY:
|
||
|
assert dill.pickles(a3)
|
||
|
assert a3.__dict__ == dill.copy(a3).__dict__
|
||
|
|
||
|
except ImportError: pass
|
||
|
|
||
|
|
||
|
def test_method_decorator():
|
||
|
class A(object):
|
||
|
@classmethod
|
||
|
def test(cls):
|
||
|
pass
|
||
|
|
||
|
a = A()
|
||
|
|
||
|
res = dill.dumps(a)
|
||
|
new_obj = dill.loads(res)
|
||
|
new_obj.__class__.test()
|
||
|
|
||
|
# test slots
|
||
|
class Y(object):
|
||
|
__slots__ = ('y', '__weakref__')
|
||
|
def __init__(self, y):
|
||
|
self.y = y
|
||
|
|
||
|
value = 123
|
||
|
y = Y(value)
|
||
|
|
||
|
class Y2(object):
|
||
|
__slots__ = 'y'
|
||
|
def __init__(self, y):
|
||
|
self.y = y
|
||
|
|
||
|
def test_slots():
|
||
|
assert dill.pickles(Y)
|
||
|
assert dill.pickles(y)
|
||
|
assert dill.pickles(Y.y)
|
||
|
assert dill.copy(y).y == value
|
||
|
assert dill.copy(Y2(value)).y == value
|
||
|
|
||
|
def test_origbases():
|
||
|
assert dill.copy(customIntList).__orig_bases__ == customIntList.__orig_bases__
|
||
|
|
||
|
def test_attr():
|
||
|
import attr
|
||
|
@attr.s
|
||
|
class A:
|
||
|
a = attr.ib()
|
||
|
|
||
|
v = A(1)
|
||
|
assert dill.copy(v) == v
|
||
|
|
||
|
def test_metaclass():
|
||
|
class metaclass_with_new(type):
|
||
|
def __new__(mcls, name, bases, ns, **kwds):
|
||
|
cls = super().__new__(mcls, name, bases, ns, **kwds)
|
||
|
assert mcls is not None
|
||
|
assert cls.method(mcls)
|
||
|
return cls
|
||
|
def method(cls, mcls):
|
||
|
return isinstance(cls, mcls)
|
||
|
|
||
|
l = locals()
|
||
|
exec("""class subclass_with_new(metaclass=metaclass_with_new):
|
||
|
def __new__(cls):
|
||
|
self = super().__new__(cls)
|
||
|
return self""", None, l)
|
||
|
subclass_with_new = l['subclass_with_new']
|
||
|
|
||
|
assert dill.copy(subclass_with_new())
|
||
|
|
||
|
def test_enummeta():
|
||
|
from http import HTTPStatus
|
||
|
import enum
|
||
|
assert dill.copy(HTTPStatus.OK) is HTTPStatus.OK
|
||
|
assert dill.copy(enum.EnumMeta) is enum.EnumMeta
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
test_class_instances()
|
||
|
test_class_objects()
|
||
|
test_specialtypes()
|
||
|
test_namedtuple()
|
||
|
test_dtype()
|
||
|
test_array_nested()
|
||
|
test_array_subclass()
|
||
|
test_method_decorator()
|
||
|
test_slots()
|
||
|
test_origbases()
|
||
|
test_metaclass()
|
||
|
test_enummeta()
|