##// 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 Reference: A. Tremols, P Cogolo, "Python Cookbook," p 302-305
10 Reference: A. Tremols, P Cogolo, "Python Cookbook," p 302-305
11 """
11 """
12
12
13 __docformat__ = "restructuredtext en"
13 # Copyright (c) IPython Development Team.
14
14 # Distributed under the terms of the Modified BSD License.
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 #-------------------------------------------------------------------------------
25
15
26 import sys
16 import sys
27 import types
17 import types
@@ -34,12 +24,10 b' def code_ctor(*args):'
34 return types.CodeType(*args)
24 return types.CodeType(*args)
35
25
36 def reduce_code(co):
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 args = [co.co_argcount, co.co_nlocals, co.co_stacksize,
27 args = [co.co_argcount, co.co_nlocals, co.co_stacksize,
40 co.co_flags, co.co_code, co.co_consts, co.co_names,
28 co.co_flags, co.co_code, co.co_consts, co.co_names,
41 co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno,
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 if sys.version_info[0] >= 3:
31 if sys.version_info[0] >= 3:
44 args.insert(1, co.co_kwonlyargcount)
32 args.insert(1, co.co_kwonlyargcount)
45 return code_ctor, tuple(args)
33 return code_ctor, tuple(args)
@@ -1,19 +1,8 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
3 """Pickle related utilities. Perhaps this should be called 'can'."""
2 """Pickle related utilities. Perhaps this should be called 'can'."""
4
3
5 __docformat__ = "restructuredtext en"
4 # Copyright (c) IPython Development Team.
6
5 # Distributed under the terms of the Modified BSD License.
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 #-------------------------------------------------------------------------------
17
6
18 import copy
7 import copy
19 import logging
8 import logging
@@ -39,6 +28,16 b' else:'
39 from types import ClassType
28 from types import ClassType
40 class_type = (type, ClassType)
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 # Functions
42 # Functions
44 #-------------------------------------------------------------------------------
43 #-------------------------------------------------------------------------------
@@ -131,6 +130,18 b' class Reference(CannedObject):'
131 return eval(self.name, g)
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 class CannedFunction(CannedObject):
145 class CannedFunction(CannedObject):
135
146
136 def __init__(self, f):
147 def __init__(self, f):
@@ -140,6 +151,13 b' class CannedFunction(CannedObject):'
140 self.defaults = [ can(fd) for fd in f.__defaults__ ]
151 self.defaults = [ can(fd) for fd in f.__defaults__ ]
141 else:
152 else:
142 self.defaults = None
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 self.module = f.__module__ or '__main__'
161 self.module = f.__module__ or '__main__'
144 self.__name__ = f.__name__
162 self.__name__ = f.__name__
145 self.buffers = []
163 self.buffers = []
@@ -159,7 +177,11 b' class CannedFunction(CannedObject):'
159 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
177 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
160 else:
178 else:
161 defaults = None
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 return newFunc
185 return newFunc
164
186
165 class CannedClass(CannedObject):
187 class CannedClass(CannedObject):
@@ -378,6 +400,7 b' can_map = {'
378 FunctionType : CannedFunction,
400 FunctionType : CannedFunction,
379 bytes : CannedBytes,
401 bytes : CannedBytes,
380 buffer : CannedBuffer,
402 buffer : CannedBuffer,
403 cell_type : CannedCell,
381 class_type : can_class,
404 class_type : can_class,
382 }
405 }
383
406
General Comments 0
You need to be logged in to leave comments. Login now