Commit 2e84def5 authored by Robert Bradshaw's avatar Robert Bradshaw

Prohibit default pickling of classes with non-Python-convertable members.

parent 437fcbe8
......@@ -1568,39 +1568,58 @@ if VALUE is not None:
return node
def _inject_pickle_methods(self, node):
env = self.current_env()
all_members = []
cls = node.entry.type
while cls is not None:
all_members.extend(cls.scope.var_entries)
cls = cls.base_type
all_members.sort(key=lambda e: e.name)
all_members_names = [e.name for e in all_members]
unpickle_func_name = '__pyx_unpickle_%s' % node.class_name
unpickle_func = TreeFragment(u"""
def %(unpickle_func_name)s(%(args)s):
cdef %(class_name)s result
result = %(class_name)s.__new__(%(class_name)s)
%(assignments)s
return result
""" % {
'unpickle_func_name': unpickle_func_name,
'class_name': node.class_name,
'assignments': '; '.join('result.%s = %s' % (v, v) for v in all_members_names),
'args': ','.join(all_members_names),
}, level='module', pipeline=[NormalizeTree(None)]).substitute({})
unpickle_func.analyse_declarations(node.entry.scope)
self.visit(unpickle_func)
self.extra_module_declarations.append(unpickle_func)
pickle_func = TreeFragment(u"""
def __reduce__(self):
return %s, (%s)
""" % (unpickle_func_name, ', '.join('self.%s' % v for v in all_members_names)),
level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
pickle_func.analyse_declarations(node.scope)
self.visit(pickle_func)
node.body.stats.append(pickle_func)
all_members.sort(key=lambda e: e.name)
non_py = [
e for e in all_members
if not e.type.is_pyobject and (not e.type.create_from_py_utility_code(env)
or not e.type.create_to_py_utility_code(env))]
if non_py:
msg = "%s cannot be converted to a Python object" % ','.join("self.%s" % e.name for e in non_py)
pickle_func = TreeFragment(u"""
def __reduce__(self):
raise TypeError("%s")
""" % msg,
level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
pickle_func.analyse_declarations(node.scope)
self.visit(pickle_func)
node.body.stats.append(pickle_func)
else:
all_members_names = [e.name for e in all_members]
unpickle_func_name = '__pyx_unpickle_%s' % node.class_name
unpickle_func = TreeFragment(u"""
def %(unpickle_func_name)s(%(args)s):
cdef %(class_name)s result
result = %(class_name)s.__new__(%(class_name)s)
%(assignments)s
return result
""" % {
'unpickle_func_name': unpickle_func_name,
'class_name': node.class_name,
'assignments': '; '.join('result.%s = %s' % (v, v) for v in all_members_names),
'args': ','.join(all_members_names),
}, level='module', pipeline=[NormalizeTree(None)]).substitute({})
unpickle_func.analyse_declarations(node.entry.scope)
self.visit(unpickle_func)
self.extra_module_declarations.append(unpickle_func)
pickle_func = TreeFragment(u"""
def __reduce__(self):
return %s, (%s)
""" % (unpickle_func_name, ', '.join('self.%s' % v for v in all_members_names)),
level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
pickle_func.analyse_declarations(node.scope)
self.visit(pickle_func)
node.body.stats.append(pickle_func)
def _handle_fused_def_decorators(self, old_decorators, env, node):
"""
......
......@@ -96,3 +96,14 @@ cdef class DefaultReduceSubclass(DefaultReduce):
def __repr__(self):
return "DefaultReduceSubclass(i=%s, s=%r, x=%s)" % (self.i, self.s, self.x)
cdef class NoReduceDueToIntPtr(object):
"""
>>> import pickle
>>> pickle.dumps(NoReduceDueToIntPtr())
Traceback (most recent call last):
...
TypeError: self.int_ptr cannot be converted to a Python object
"""
cdef int* int_ptr
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment