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