Skip to content

Commit 7dca9a2

Browse files
committed
Add support for pickling proxy objects. The pickled object will be the object be the object that the proxy points to (__wrapped__). Closes ionelmc#7.
1 parent 335b032 commit 7dca9a2

File tree

6 files changed

+146
-28
lines changed

6 files changed

+146
-28
lines changed

src/lazy_object_proxy/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,19 @@
22

33
__all__ = "Proxy",
44

5+
try:
6+
import copy_reg as copyreg
7+
except ImportError:
8+
import copyreg
9+
10+
from .utils import identity
11+
12+
copyreg.constructor(identity)
13+
514
try:
615
from .cext import Proxy
16+
from .cext import identity
717
except ImportError:
818
from .slots import Proxy
19+
else:
20+
copyreg.constructor(identity)

src/lazy_object_proxy/cext.c

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@ typedef struct {
3333

3434
PyTypeObject Proxy_Type;
3535

36+
37+
/* ------------------------------------------------------------------------- */
38+
39+
static PyObject *identity_ref = NULL;
40+
static PyObject *
41+
identity(PyObject *self, PyObject *value)
42+
{
43+
Py_INCREF(value);
44+
return value;
45+
}
46+
47+
/* ------------------------------------------------------------------------- */
48+
49+
PyDoc_STRVAR(identity_doc, "Indentity function: returns the single argument.");
50+
51+
static struct PyMethodDef module_functions[] = {
52+
{"identity", identity, METH_O, identity_doc},
53+
{NULL, NULL}
54+
};
55+
3656
/* ------------------------------------------------------------------------- */
3757

3858
static PyObject *Proxy__ensure_wrapped(ProxyObject *self)
@@ -844,6 +864,15 @@ static PyObject *Proxy_reversed(
844864
self->wrapped, NULL);
845865
}
846866

867+
/* ------------------------------------------------------------------------- */
868+
static PyObject *Proxy_reduce(
869+
ProxyObject *self, PyObject *args)
870+
{
871+
Proxy__ENSURE_WRAPPED_OR_RETURN_NULL(self);
872+
873+
return Py_BuildValue("(O(O))", identity_ref, self->wrapped);
874+
}
875+
847876
/* ------------------------------------------------------------------------- */
848877

849878
#if PY_MAJOR_VERSION >= 3
@@ -1227,6 +1256,8 @@ static PyMethodDef Proxy_methods[] = {
12271256
METH_VARARGS , 0 },
12281257
{ "__bytes__", (PyCFunction)Proxy_bytes, METH_NOARGS, 0 },
12291258
{ "__reversed__", (PyCFunction)Proxy_reversed, METH_NOARGS, 0 },
1259+
{ "__reduce__", (PyCFunction)Proxy_reduce, METH_NOARGS, 0 },
1260+
{ "__reduce_ex__", (PyCFunction)Proxy_reduce, METH_O, 0 },
12301261
#if PY_MAJOR_VERSION >= 3
12311262
{ "__round__", (PyCFunction)Proxy_round, METH_NOARGS, 0 },
12321263
#endif
@@ -1308,26 +1339,27 @@ PyTypeObject Proxy_Type = {
13081339
#if PY_MAJOR_VERSION >= 3
13091340
static struct PyModuleDef moduledef = {
13101341
PyModuleDef_HEAD_INIT,
1311-
"cext", /* m_name */
1312-
NULL, /* m_doc */
1313-
-1, /* m_size */
1314-
NULL, /* m_methods */
1315-
NULL, /* m_reload */
1316-
NULL, /* m_traverse */
1317-
NULL, /* m_clear */
1318-
NULL, /* m_free */
1342+
"lazy_object_proxy.cext", /* m_name */
1343+
NULL, /* m_doc */
1344+
-1, /* m_size */
1345+
module_functions, /* m_methods */
1346+
NULL, /* m_reload */
1347+
NULL, /* m_traverse */
1348+
NULL, /* m_clear */
1349+
NULL, /* m_free */
13191350
};
13201351
#endif
13211352

13221353
static PyObject *
13231354
moduleinit(void)
13241355
{
13251356
PyObject *module;
1357+
PyObject *dict;
13261358

13271359
#if PY_MAJOR_VERSION >= 3
13281360
module = PyModule_Create(&moduledef);
13291361
#else
1330-
module = Py_InitModule3("cext", NULL, NULL);
1362+
module = Py_InitModule3("lazy_object_proxy.cext", module_functions, NULL);
13311363
#endif
13321364

13331365
if (module == NULL)
@@ -1339,6 +1371,14 @@ moduleinit(void)
13391371
if (PyType_Ready(&Proxy_Type) < 0)
13401372
return NULL;
13411373

1374+
dict = PyModule_GetDict(module);
1375+
if (dict == NULL)
1376+
return NULL;
1377+
identity_ref = PyDict_GetItemString(dict, "identity");
1378+
if (identity_ref == NULL)
1379+
return NULL;
1380+
Py_INCREF(identity_ref);
1381+
13421382
Py_INCREF(&Proxy_Type);
13431383
PyModule_AddObject(module, "Proxy",
13441384
(PyObject *)&Proxy_Type);

src/lazy_object_proxy/simple.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,14 @@
33
from .compat import PY2
44
from .compat import PY3
55
from .compat import with_metaclass
6-
7-
8-
class cached_property(object):
9-
def __init__(self, func):
10-
self.func = func
11-
12-
def __get__(self, obj, cls):
13-
if obj is None:
14-
return self
15-
value = obj.__dict__[self.func.__name__] = self.func(obj)
16-
return value
6+
from .utils import cached_property
7+
from .utils import identity
178

189

1910
def make_proxy_method(code):
2011
def proxy_wrapper(self, *args):
2112
return code(self.__wrapped__, *args)
13+
2214
return proxy_wrapper
2315

2416

@@ -86,7 +78,6 @@ def __wrapped__(self):
8678
else:
8779
raise ValueError("Proxy hasn't been initiated: __factory__ is missing.")
8880

89-
9081
__name__ = property(make_proxy_method(operator.attrgetter('__name__')))
9182
__class__ = property(make_proxy_method(operator.attrgetter('__class__')))
9283
__annotations__ = property(make_proxy_method(operator.attrgetter('__anotations__')))
@@ -240,3 +231,9 @@ def __exit__(self, *args, **kwargs):
240231

241232
def __call__(self, *args, **kwargs):
242233
return self.__wrapped__(*args, **kwargs)
234+
235+
def __reduce__(self):
236+
return identity, (self.__wrapped__,)
237+
238+
def __reduce_ex__(self, protocol):
239+
return identity, (self.__wrapped__,)

src/lazy_object_proxy/slots.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .compat import PY2
44
from .compat import PY3
55
from .compat import with_metaclass
6+
from .utils import identity
67

78

89
class _ProxyMethods(object):
@@ -81,7 +82,8 @@ def __init__(self, factory):
8182
object.__setattr__(self, '__factory__', factory)
8283

8384
@property
84-
def __wrapped__(self, __getattr__=object.__getattribute__, __setattr__=object.__setattr__, __delattr__=object.__delattr__):
85+
def __wrapped__(self, __getattr__=object.__getattribute__, __setattr__=object.__setattr__,
86+
__delattr__=object.__delattr__):
8587
try:
8688
return __getattr__(self, '__target__')
8789
except AttributeError:
@@ -395,3 +397,9 @@ def __iter__(self):
395397

396398
def __call__(self, *args, **kwargs):
397399
return self.__wrapped__(*args, **kwargs)
400+
401+
def __reduce__(self):
402+
return identity, (self.__wrapped__,)
403+
404+
def __reduce_ex__(self, protocol):
405+
return identity, (self.__wrapped__,)

src/lazy_object_proxy/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def identity(obj):
2+
return obj
3+
4+
5+
class cached_property(object):
6+
def __init__(self, func):
7+
self.func = func
8+
9+
def __get__(self, obj, cls):
10+
if obj is None:
11+
return self
12+
value = obj.__dict__[self.func.__name__] = self.func(obj)
13+
return value

0 commit comments

Comments
 (0)