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: ...@@ -1568,39 +1568,58 @@ if VALUE is not None:
return node return node
def _inject_pickle_methods(self, node): def _inject_pickle_methods(self, node):
env = self.current_env()
all_members = [] all_members = []
cls = node.entry.type cls = node.entry.type
while cls is not None: while cls is not None:
all_members.extend(cls.scope.var_entries) all_members.extend(cls.scope.var_entries)
cls = cls.base_type cls = cls.base_type
all_members.sort(key=lambda e: e.name) 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 non_py = [
e for e in all_members
unpickle_func = TreeFragment(u""" if not e.type.is_pyobject and (not e.type.create_from_py_utility_code(env)
def %(unpickle_func_name)s(%(args)s): or not e.type.create_to_py_utility_code(env))]
cdef %(class_name)s result
result = %(class_name)s.__new__(%(class_name)s) if non_py:
%(assignments)s msg = "%s cannot be converted to a Python object" % ','.join("self.%s" % e.name for e in non_py)
return result pickle_func = TreeFragment(u"""
""" % { def __reduce__(self):
'unpickle_func_name': unpickle_func_name, raise TypeError("%s")
'class_name': node.class_name, """ % msg,
'assignments': '; '.join('result.%s = %s' % (v, v) for v in all_members_names), level='c_class', pipeline=[NormalizeTree(None)]).substitute({})
'args': ','.join(all_members_names), pickle_func.analyse_declarations(node.scope)
}, level='module', pipeline=[NormalizeTree(None)]).substitute({}) self.visit(pickle_func)
unpickle_func.analyse_declarations(node.entry.scope) node.body.stats.append(pickle_func)
self.visit(unpickle_func)
self.extra_module_declarations.append(unpickle_func) else:
all_members_names = [e.name for e in all_members]
pickle_func = TreeFragment(u""" unpickle_func_name = '__pyx_unpickle_%s' % node.class_name
def __reduce__(self):
return %s, (%s) unpickle_func = TreeFragment(u"""
""" % (unpickle_func_name, ', '.join('self.%s' % v for v in all_members_names)), def %(unpickle_func_name)s(%(args)s):
level='c_class', pipeline=[NormalizeTree(None)]).substitute({}) cdef %(class_name)s result
pickle_func.analyse_declarations(node.scope) result = %(class_name)s.__new__(%(class_name)s)
self.visit(pickle_func) %(assignments)s
node.body.stats.append(pickle_func) 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): def _handle_fused_def_decorators(self, old_decorators, env, node):
""" """
......
...@@ -96,3 +96,14 @@ cdef class DefaultReduceSubclass(DefaultReduce): ...@@ -96,3 +96,14 @@ cdef class DefaultReduceSubclass(DefaultReduce):
def __repr__(self): def __repr__(self):
return "DefaultReduceSubclass(i=%s, s=%r, x=%s)" % (self.i, self.s, self.x) 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