##// END OF EJS Templates
handle simple closures in pickleutil...
MinRK -
Show More
@@ -0,0 +1,62 b''
1
2 import pickle
3
4 import nose.tools as nt
5 from IPython.utils import codeutil
6 from IPython.utils.pickleutil import can, uncan
7
8 def interactive(f):
9 f.__module__ = '__main__'
10 return f
11
12 def dumps(obj):
13 return pickle.dumps(can(obj))
14
15 def loads(obj):
16 return uncan(pickle.loads(obj))
17
18 def test_no_closure():
19 @interactive
20 def foo():
21 a = 5
22 return a
23
24 pfoo = dumps(foo)
25 bar = loads(pfoo)
26 nt.assert_equal(foo(), bar())
27
28 def test_generator_closure():
29 # this only creates a closure on Python 3
30 @interactive
31 def foo():
32 i = 'i'
33 r = [ i for j in (1,2) ]
34 return r
35
36 pfoo = dumps(foo)
37 bar = loads(pfoo)
38 nt.assert_equal(foo(), bar())
39
40 def test_nested_closure():
41 @interactive
42 def foo():
43 i = 'i'
44 def g():
45 return i
46 return g()
47
48 pfoo = dumps(foo)
49 bar = loads(pfoo)
50 nt.assert_equal(foo(), bar())
51
52 def test_closure():
53 i = 'i'
54 @interactive
55 def foo():
56 return i
57
58 pfoo = dumps(foo)
59 bar = loads(pfoo)
60 nt.assert_equal(foo(), bar())
61
62 No newline at end of file
@@ -10,18 +10,8 b' we need to automate all of this so that functions themselves can be pickled.'
10 10 Reference: A. Tremols, P Cogolo, "Python Cookbook," p 302-305
11 11 """
12 12
13 __docformat__ = "restructuredtext en"
14
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008-2011 The IPython Development Team
17 #
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
21
22 #-------------------------------------------------------------------------------
23 # Imports
24 #-------------------------------------------------------------------------------
13 # Copyright (c) IPython Development Team.
14 # Distributed under the terms of the Modified BSD License.
25 15
26 16 import sys
27 17 import types
@@ -34,12 +24,10 b' def code_ctor(*args):'
34 24 return types.CodeType(*args)
35 25
36 26 def reduce_code(co):
37 if co.co_freevars or co.co_cellvars:
38 raise ValueError("Sorry, cannot pickle code objects with closures")
39 27 args = [co.co_argcount, co.co_nlocals, co.co_stacksize,
40 28 co.co_flags, co.co_code, co.co_consts, co.co_names,
41 29 co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno,
42 co.co_lnotab]
30 co.co_lnotab, co.co_freevars, co.co_cellvars]
43 31 if sys.version_info[0] >= 3:
44 32 args.insert(1, co.co_kwonlyargcount)
45 33 return code_ctor, tuple(args)
@@ -1,19 +1,8 b''
1 1 # encoding: utf-8
2
3 2 """Pickle related utilities. Perhaps this should be called 'can'."""
4 3
5 __docformat__ = "restructuredtext en"
6
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008-2011 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
13
14 #-------------------------------------------------------------------------------
15 # Imports
16 #-------------------------------------------------------------------------------
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
17 6
18 7 import copy
19 8 import logging
@@ -39,6 +28,16 b' else:'
39 28 from types import ClassType
40 29 class_type = (type, ClassType)
41 30
31 def _get_cell_type(a=None):
32 """the type of a closure cell doesn't seem to be importable,
33 so just create one
34 """
35 def inner():
36 return a
37 return type(py3compat.get_closure(inner)[0])
38
39 cell_type = _get_cell_type()
40
42 41 #-------------------------------------------------------------------------------
43 42 # Functions
44 43 #-------------------------------------------------------------------------------
@@ -131,6 +130,18 b' class Reference(CannedObject):'
131 130 return eval(self.name, g)
132 131
133 132
133 class CannedCell(CannedObject):
134 """Can a closure cell"""
135 def __init__(self, cell):
136 self.cell_contents = can(cell.cell_contents)
137
138 def get_object(self, g=None):
139 cell_contents = uncan(self.cell_contents, g)
140 def inner():
141 return cell_contents
142 return py3compat.get_closure(inner)[0]
143
144
134 145 class CannedFunction(CannedObject):
135 146
136 147 def __init__(self, f):
@@ -140,6 +151,13 b' class CannedFunction(CannedObject):'
140 151 self.defaults = [ can(fd) for fd in f.__defaults__ ]
141 152 else:
142 153 self.defaults = None
154
155 closure = py3compat.get_closure(f)
156 if closure:
157 self.closure = tuple( can(cell) for cell in closure )
158 else:
159 self.closure = None
160
143 161 self.module = f.__module__ or '__main__'
144 162 self.__name__ = f.__name__
145 163 self.buffers = []
@@ -159,7 +177,11 b' class CannedFunction(CannedObject):'
159 177 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
160 178 else:
161 179 defaults = None
162 newFunc = FunctionType(self.code, g, self.__name__, defaults)
180 if self.closure:
181 closure = tuple(uncan(cell, g) for cell in self.closure)
182 else:
183 closure = None
184 newFunc = FunctionType(self.code, g, self.__name__, defaults, closure)
163 185 return newFunc
164 186
165 187 class CannedClass(CannedObject):
@@ -378,6 +400,7 b' can_map = {'
378 400 FunctionType : CannedFunction,
379 401 bytes : CannedBytes,
380 402 buffer : CannedBuffer,
403 cell_type : CannedCell,
381 404 class_type : can_class,
382 405 }
383 406
General Comments 0
You need to be logged in to leave comments. Login now