##// END OF EJS Templates
Merge pull request #5761 from minrk/pickle_simple_closures...
Min RK -
r16517:9f8c9b46 merge
parent child Browse files
Show More
@@ -0,0 +1,62
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
@@ -1,47 +1,35
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """Utilities to enable code objects to be pickled.
3 """Utilities to enable code objects to be pickled.
4
4
5 Any process that import this module will be able to pickle code objects. This
5 Any process that import this module will be able to pickle code objects. This
6 includes the func_code attribute of any function. Once unpickled, new
6 includes the func_code attribute of any function. Once unpickled, new
7 functions can be built using new.function(code, globals()). Eventually
7 functions can be built using new.function(code, globals()). Eventually
8 we need to automate all of this so that functions themselves can be pickled.
8 we need to automate all of this so that functions themselves can be pickled.
9
9
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
28 try:
18 try:
29 import copyreg # Py 3
19 import copyreg # Py 3
30 except ImportError:
20 except ImportError:
31 import copy_reg as copyreg # Py 2
21 import copy_reg as copyreg # Py 2
32
22
33 def code_ctor(*args):
23 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)
46
34
47 copyreg.pickle(types.CodeType, reduce_code) No newline at end of file
35 copyreg.pickle(types.CodeType, reduce_code)
@@ -1,390 +1,413
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
20 import sys
9 import sys
21 from types import FunctionType
10 from types import FunctionType
22
11
23 try:
12 try:
24 import cPickle as pickle
13 import cPickle as pickle
25 except ImportError:
14 except ImportError:
26 import pickle
15 import pickle
27
16
28 from . import codeutil # This registers a hook when it's imported
17 from . import codeutil # This registers a hook when it's imported
29 from . import py3compat
18 from . import py3compat
30 from .importstring import import_item
19 from .importstring import import_item
31 from .py3compat import string_types, iteritems
20 from .py3compat import string_types, iteritems
32
21
33 from IPython.config import Application
22 from IPython.config import Application
34
23
35 if py3compat.PY3:
24 if py3compat.PY3:
36 buffer = memoryview
25 buffer = memoryview
37 class_type = type
26 class_type = type
38 else:
27 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 #-------------------------------------------------------------------------------
45
44
46
45
47 def use_dill():
46 def use_dill():
48 """use dill to expand serialization support
47 """use dill to expand serialization support
49
48
50 adds support for object methods and closures to serialization.
49 adds support for object methods and closures to serialization.
51 """
50 """
52 # import dill causes most of the magic
51 # import dill causes most of the magic
53 import dill
52 import dill
54
53
55 # dill doesn't work with cPickle,
54 # dill doesn't work with cPickle,
56 # tell the two relevant modules to use plain pickle
55 # tell the two relevant modules to use plain pickle
57
56
58 global pickle
57 global pickle
59 pickle = dill
58 pickle = dill
60
59
61 try:
60 try:
62 from IPython.kernel.zmq import serialize
61 from IPython.kernel.zmq import serialize
63 except ImportError:
62 except ImportError:
64 pass
63 pass
65 else:
64 else:
66 serialize.pickle = dill
65 serialize.pickle = dill
67
66
68 # disable special function handling, let dill take care of it
67 # disable special function handling, let dill take care of it
69 can_map.pop(FunctionType, None)
68 can_map.pop(FunctionType, None)
70
69
71
70
72 #-------------------------------------------------------------------------------
71 #-------------------------------------------------------------------------------
73 # Classes
72 # Classes
74 #-------------------------------------------------------------------------------
73 #-------------------------------------------------------------------------------
75
74
76
75
77 class CannedObject(object):
76 class CannedObject(object):
78 def __init__(self, obj, keys=[], hook=None):
77 def __init__(self, obj, keys=[], hook=None):
79 """can an object for safe pickling
78 """can an object for safe pickling
80
79
81 Parameters
80 Parameters
82 ==========
81 ==========
83
82
84 obj:
83 obj:
85 The object to be canned
84 The object to be canned
86 keys: list (optional)
85 keys: list (optional)
87 list of attribute names that will be explicitly canned / uncanned
86 list of attribute names that will be explicitly canned / uncanned
88 hook: callable (optional)
87 hook: callable (optional)
89 An optional extra callable,
88 An optional extra callable,
90 which can do additional processing of the uncanned object.
89 which can do additional processing of the uncanned object.
91
90
92 large data may be offloaded into the buffers list,
91 large data may be offloaded into the buffers list,
93 used for zero-copy transfers.
92 used for zero-copy transfers.
94 """
93 """
95 self.keys = keys
94 self.keys = keys
96 self.obj = copy.copy(obj)
95 self.obj = copy.copy(obj)
97 self.hook = can(hook)
96 self.hook = can(hook)
98 for key in keys:
97 for key in keys:
99 setattr(self.obj, key, can(getattr(obj, key)))
98 setattr(self.obj, key, can(getattr(obj, key)))
100
99
101 self.buffers = []
100 self.buffers = []
102
101
103 def get_object(self, g=None):
102 def get_object(self, g=None):
104 if g is None:
103 if g is None:
105 g = {}
104 g = {}
106 obj = self.obj
105 obj = self.obj
107 for key in self.keys:
106 for key in self.keys:
108 setattr(obj, key, uncan(getattr(obj, key), g))
107 setattr(obj, key, uncan(getattr(obj, key), g))
109
108
110 if self.hook:
109 if self.hook:
111 self.hook = uncan(self.hook, g)
110 self.hook = uncan(self.hook, g)
112 self.hook(obj, g)
111 self.hook(obj, g)
113 return self.obj
112 return self.obj
114
113
115
114
116 class Reference(CannedObject):
115 class Reference(CannedObject):
117 """object for wrapping a remote reference by name."""
116 """object for wrapping a remote reference by name."""
118 def __init__(self, name):
117 def __init__(self, name):
119 if not isinstance(name, string_types):
118 if not isinstance(name, string_types):
120 raise TypeError("illegal name: %r"%name)
119 raise TypeError("illegal name: %r"%name)
121 self.name = name
120 self.name = name
122 self.buffers = []
121 self.buffers = []
123
122
124 def __repr__(self):
123 def __repr__(self):
125 return "<Reference: %r>"%self.name
124 return "<Reference: %r>"%self.name
126
125
127 def get_object(self, g=None):
126 def get_object(self, g=None):
128 if g is None:
127 if g is None:
129 g = {}
128 g = {}
130
129
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):
137 self._check_type(f)
148 self._check_type(f)
138 self.code = f.__code__
149 self.code = f.__code__
139 if f.__defaults__:
150 if f.__defaults__:
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 = []
146
164
147 def _check_type(self, obj):
165 def _check_type(self, obj):
148 assert isinstance(obj, FunctionType), "Not a function type"
166 assert isinstance(obj, FunctionType), "Not a function type"
149
167
150 def get_object(self, g=None):
168 def get_object(self, g=None):
151 # try to load function back into its module:
169 # try to load function back into its module:
152 if not self.module.startswith('__'):
170 if not self.module.startswith('__'):
153 __import__(self.module)
171 __import__(self.module)
154 g = sys.modules[self.module].__dict__
172 g = sys.modules[self.module].__dict__
155
173
156 if g is None:
174 if g is None:
157 g = {}
175 g = {}
158 if self.defaults:
176 if self.defaults:
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):
166
188
167 def __init__(self, cls):
189 def __init__(self, cls):
168 self._check_type(cls)
190 self._check_type(cls)
169 self.name = cls.__name__
191 self.name = cls.__name__
170 self.old_style = not isinstance(cls, type)
192 self.old_style = not isinstance(cls, type)
171 self._canned_dict = {}
193 self._canned_dict = {}
172 for k,v in cls.__dict__.items():
194 for k,v in cls.__dict__.items():
173 if k not in ('__weakref__', '__dict__'):
195 if k not in ('__weakref__', '__dict__'):
174 self._canned_dict[k] = can(v)
196 self._canned_dict[k] = can(v)
175 if self.old_style:
197 if self.old_style:
176 mro = []
198 mro = []
177 else:
199 else:
178 mro = cls.mro()
200 mro = cls.mro()
179
201
180 self.parents = [ can(c) for c in mro[1:] ]
202 self.parents = [ can(c) for c in mro[1:] ]
181 self.buffers = []
203 self.buffers = []
182
204
183 def _check_type(self, obj):
205 def _check_type(self, obj):
184 assert isinstance(obj, class_type), "Not a class type"
206 assert isinstance(obj, class_type), "Not a class type"
185
207
186 def get_object(self, g=None):
208 def get_object(self, g=None):
187 parents = tuple(uncan(p, g) for p in self.parents)
209 parents = tuple(uncan(p, g) for p in self.parents)
188 return type(self.name, parents, uncan_dict(self._canned_dict, g=g))
210 return type(self.name, parents, uncan_dict(self._canned_dict, g=g))
189
211
190 class CannedArray(CannedObject):
212 class CannedArray(CannedObject):
191 def __init__(self, obj):
213 def __init__(self, obj):
192 from numpy import ascontiguousarray
214 from numpy import ascontiguousarray
193 self.shape = obj.shape
215 self.shape = obj.shape
194 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
216 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
195 self.pickled = False
217 self.pickled = False
196 if sum(obj.shape) == 0:
218 if sum(obj.shape) == 0:
197 self.pickled = True
219 self.pickled = True
198 elif obj.dtype == 'O':
220 elif obj.dtype == 'O':
199 # can't handle object dtype with buffer approach
221 # can't handle object dtype with buffer approach
200 self.pickled = True
222 self.pickled = True
201 elif obj.dtype.fields and any(dt == 'O' for dt,sz in obj.dtype.fields.values()):
223 elif obj.dtype.fields and any(dt == 'O' for dt,sz in obj.dtype.fields.values()):
202 self.pickled = True
224 self.pickled = True
203 if self.pickled:
225 if self.pickled:
204 # just pickle it
226 # just pickle it
205 self.buffers = [pickle.dumps(obj, -1)]
227 self.buffers = [pickle.dumps(obj, -1)]
206 else:
228 else:
207 # ensure contiguous
229 # ensure contiguous
208 obj = ascontiguousarray(obj, dtype=None)
230 obj = ascontiguousarray(obj, dtype=None)
209 self.buffers = [buffer(obj)]
231 self.buffers = [buffer(obj)]
210
232
211 def get_object(self, g=None):
233 def get_object(self, g=None):
212 from numpy import frombuffer
234 from numpy import frombuffer
213 data = self.buffers[0]
235 data = self.buffers[0]
214 if self.pickled:
236 if self.pickled:
215 # no shape, we just pickled it
237 # no shape, we just pickled it
216 return pickle.loads(data)
238 return pickle.loads(data)
217 else:
239 else:
218 return frombuffer(data, dtype=self.dtype).reshape(self.shape)
240 return frombuffer(data, dtype=self.dtype).reshape(self.shape)
219
241
220
242
221 class CannedBytes(CannedObject):
243 class CannedBytes(CannedObject):
222 wrap = bytes
244 wrap = bytes
223 def __init__(self, obj):
245 def __init__(self, obj):
224 self.buffers = [obj]
246 self.buffers = [obj]
225
247
226 def get_object(self, g=None):
248 def get_object(self, g=None):
227 data = self.buffers[0]
249 data = self.buffers[0]
228 return self.wrap(data)
250 return self.wrap(data)
229
251
230 def CannedBuffer(CannedBytes):
252 def CannedBuffer(CannedBytes):
231 wrap = buffer
253 wrap = buffer
232
254
233 #-------------------------------------------------------------------------------
255 #-------------------------------------------------------------------------------
234 # Functions
256 # Functions
235 #-------------------------------------------------------------------------------
257 #-------------------------------------------------------------------------------
236
258
237 def _logger():
259 def _logger():
238 """get the logger for the current Application
260 """get the logger for the current Application
239
261
240 the root logger will be used if no Application is running
262 the root logger will be used if no Application is running
241 """
263 """
242 if Application.initialized():
264 if Application.initialized():
243 logger = Application.instance().log
265 logger = Application.instance().log
244 else:
266 else:
245 logger = logging.getLogger()
267 logger = logging.getLogger()
246 if not logger.handlers:
268 if not logger.handlers:
247 logging.basicConfig()
269 logging.basicConfig()
248
270
249 return logger
271 return logger
250
272
251 def _import_mapping(mapping, original=None):
273 def _import_mapping(mapping, original=None):
252 """import any string-keys in a type mapping
274 """import any string-keys in a type mapping
253
275
254 """
276 """
255 log = _logger()
277 log = _logger()
256 log.debug("Importing canning map")
278 log.debug("Importing canning map")
257 for key,value in list(mapping.items()):
279 for key,value in list(mapping.items()):
258 if isinstance(key, string_types):
280 if isinstance(key, string_types):
259 try:
281 try:
260 cls = import_item(key)
282 cls = import_item(key)
261 except Exception:
283 except Exception:
262 if original and key not in original:
284 if original and key not in original:
263 # only message on user-added classes
285 # only message on user-added classes
264 log.error("canning class not importable: %r", key, exc_info=True)
286 log.error("canning class not importable: %r", key, exc_info=True)
265 mapping.pop(key)
287 mapping.pop(key)
266 else:
288 else:
267 mapping[cls] = mapping.pop(key)
289 mapping[cls] = mapping.pop(key)
268
290
269 def istype(obj, check):
291 def istype(obj, check):
270 """like isinstance(obj, check), but strict
292 """like isinstance(obj, check), but strict
271
293
272 This won't catch subclasses.
294 This won't catch subclasses.
273 """
295 """
274 if isinstance(check, tuple):
296 if isinstance(check, tuple):
275 for cls in check:
297 for cls in check:
276 if type(obj) is cls:
298 if type(obj) is cls:
277 return True
299 return True
278 return False
300 return False
279 else:
301 else:
280 return type(obj) is check
302 return type(obj) is check
281
303
282 def can(obj):
304 def can(obj):
283 """prepare an object for pickling"""
305 """prepare an object for pickling"""
284
306
285 import_needed = False
307 import_needed = False
286
308
287 for cls,canner in iteritems(can_map):
309 for cls,canner in iteritems(can_map):
288 if isinstance(cls, string_types):
310 if isinstance(cls, string_types):
289 import_needed = True
311 import_needed = True
290 break
312 break
291 elif istype(obj, cls):
313 elif istype(obj, cls):
292 return canner(obj)
314 return canner(obj)
293
315
294 if import_needed:
316 if import_needed:
295 # perform can_map imports, then try again
317 # perform can_map imports, then try again
296 # this will usually only happen once
318 # this will usually only happen once
297 _import_mapping(can_map, _original_can_map)
319 _import_mapping(can_map, _original_can_map)
298 return can(obj)
320 return can(obj)
299
321
300 return obj
322 return obj
301
323
302 def can_class(obj):
324 def can_class(obj):
303 if isinstance(obj, class_type) and obj.__module__ == '__main__':
325 if isinstance(obj, class_type) and obj.__module__ == '__main__':
304 return CannedClass(obj)
326 return CannedClass(obj)
305 else:
327 else:
306 return obj
328 return obj
307
329
308 def can_dict(obj):
330 def can_dict(obj):
309 """can the *values* of a dict"""
331 """can the *values* of a dict"""
310 if istype(obj, dict):
332 if istype(obj, dict):
311 newobj = {}
333 newobj = {}
312 for k, v in iteritems(obj):
334 for k, v in iteritems(obj):
313 newobj[k] = can(v)
335 newobj[k] = can(v)
314 return newobj
336 return newobj
315 else:
337 else:
316 return obj
338 return obj
317
339
318 sequence_types = (list, tuple, set)
340 sequence_types = (list, tuple, set)
319
341
320 def can_sequence(obj):
342 def can_sequence(obj):
321 """can the elements of a sequence"""
343 """can the elements of a sequence"""
322 if istype(obj, sequence_types):
344 if istype(obj, sequence_types):
323 t = type(obj)
345 t = type(obj)
324 return t([can(i) for i in obj])
346 return t([can(i) for i in obj])
325 else:
347 else:
326 return obj
348 return obj
327
349
328 def uncan(obj, g=None):
350 def uncan(obj, g=None):
329 """invert canning"""
351 """invert canning"""
330
352
331 import_needed = False
353 import_needed = False
332 for cls,uncanner in iteritems(uncan_map):
354 for cls,uncanner in iteritems(uncan_map):
333 if isinstance(cls, string_types):
355 if isinstance(cls, string_types):
334 import_needed = True
356 import_needed = True
335 break
357 break
336 elif isinstance(obj, cls):
358 elif isinstance(obj, cls):
337 return uncanner(obj, g)
359 return uncanner(obj, g)
338
360
339 if import_needed:
361 if import_needed:
340 # perform uncan_map imports, then try again
362 # perform uncan_map imports, then try again
341 # this will usually only happen once
363 # this will usually only happen once
342 _import_mapping(uncan_map, _original_uncan_map)
364 _import_mapping(uncan_map, _original_uncan_map)
343 return uncan(obj, g)
365 return uncan(obj, g)
344
366
345 return obj
367 return obj
346
368
347 def uncan_dict(obj, g=None):
369 def uncan_dict(obj, g=None):
348 if istype(obj, dict):
370 if istype(obj, dict):
349 newobj = {}
371 newobj = {}
350 for k, v in iteritems(obj):
372 for k, v in iteritems(obj):
351 newobj[k] = uncan(v,g)
373 newobj[k] = uncan(v,g)
352 return newobj
374 return newobj
353 else:
375 else:
354 return obj
376 return obj
355
377
356 def uncan_sequence(obj, g=None):
378 def uncan_sequence(obj, g=None):
357 if istype(obj, sequence_types):
379 if istype(obj, sequence_types):
358 t = type(obj)
380 t = type(obj)
359 return t([uncan(i,g) for i in obj])
381 return t([uncan(i,g) for i in obj])
360 else:
382 else:
361 return obj
383 return obj
362
384
363 def _uncan_dependent_hook(dep, g=None):
385 def _uncan_dependent_hook(dep, g=None):
364 dep.check_dependency()
386 dep.check_dependency()
365
387
366 def can_dependent(obj):
388 def can_dependent(obj):
367 return CannedObject(obj, keys=('f', 'df'), hook=_uncan_dependent_hook)
389 return CannedObject(obj, keys=('f', 'df'), hook=_uncan_dependent_hook)
368
390
369 #-------------------------------------------------------------------------------
391 #-------------------------------------------------------------------------------
370 # API dictionaries
392 # API dictionaries
371 #-------------------------------------------------------------------------------
393 #-------------------------------------------------------------------------------
372
394
373 # These dicts can be extended for custom serialization of new objects
395 # These dicts can be extended for custom serialization of new objects
374
396
375 can_map = {
397 can_map = {
376 'IPython.parallel.dependent' : can_dependent,
398 'IPython.parallel.dependent' : can_dependent,
377 'numpy.ndarray' : CannedArray,
399 'numpy.ndarray' : CannedArray,
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
384 uncan_map = {
407 uncan_map = {
385 CannedObject : lambda obj, g: obj.get_object(g),
408 CannedObject : lambda obj, g: obj.get_object(g),
386 }
409 }
387
410
388 # for use in _import_mapping:
411 # for use in _import_mapping:
389 _original_can_map = can_map.copy()
412 _original_can_map = can_map.copy()
390 _original_uncan_map = uncan_map.copy()
413 _original_uncan_map = uncan_map.copy()
@@ -1,264 +1,271
1 # coding: utf-8
1 # coding: utf-8
2 """Compatibility tricks for Python 3. Mainly to do with unicode."""
2 """Compatibility tricks for Python 3. Mainly to do with unicode."""
3 import functools
3 import functools
4 import os
4 import os
5 import sys
5 import sys
6 import re
6 import re
7 import types
7 import types
8
8
9 from .encoding import DEFAULT_ENCODING
9 from .encoding import DEFAULT_ENCODING
10
10
11 orig_open = open
11 orig_open = open
12
12
13 def no_code(x, encoding=None):
13 def no_code(x, encoding=None):
14 return x
14 return x
15
15
16 def decode(s, encoding=None):
16 def decode(s, encoding=None):
17 encoding = encoding or DEFAULT_ENCODING
17 encoding = encoding or DEFAULT_ENCODING
18 return s.decode(encoding, "replace")
18 return s.decode(encoding, "replace")
19
19
20 def encode(u, encoding=None):
20 def encode(u, encoding=None):
21 encoding = encoding or DEFAULT_ENCODING
21 encoding = encoding or DEFAULT_ENCODING
22 return u.encode(encoding, "replace")
22 return u.encode(encoding, "replace")
23
23
24
24
25 def cast_unicode(s, encoding=None):
25 def cast_unicode(s, encoding=None):
26 if isinstance(s, bytes):
26 if isinstance(s, bytes):
27 return decode(s, encoding)
27 return decode(s, encoding)
28 return s
28 return s
29
29
30 def cast_bytes(s, encoding=None):
30 def cast_bytes(s, encoding=None):
31 if not isinstance(s, bytes):
31 if not isinstance(s, bytes):
32 return encode(s, encoding)
32 return encode(s, encoding)
33 return s
33 return s
34
34
35 def _modify_str_or_docstring(str_change_func):
35 def _modify_str_or_docstring(str_change_func):
36 @functools.wraps(str_change_func)
36 @functools.wraps(str_change_func)
37 def wrapper(func_or_str):
37 def wrapper(func_or_str):
38 if isinstance(func_or_str, string_types):
38 if isinstance(func_or_str, string_types):
39 func = None
39 func = None
40 doc = func_or_str
40 doc = func_or_str
41 else:
41 else:
42 func = func_or_str
42 func = func_or_str
43 doc = func.__doc__
43 doc = func.__doc__
44
44
45 doc = str_change_func(doc)
45 doc = str_change_func(doc)
46
46
47 if func:
47 if func:
48 func.__doc__ = doc
48 func.__doc__ = doc
49 return func
49 return func
50 return doc
50 return doc
51 return wrapper
51 return wrapper
52
52
53 def safe_unicode(e):
53 def safe_unicode(e):
54 """unicode(e) with various fallbacks. Used for exceptions, which may not be
54 """unicode(e) with various fallbacks. Used for exceptions, which may not be
55 safe to call unicode() on.
55 safe to call unicode() on.
56 """
56 """
57 try:
57 try:
58 return unicode_type(e)
58 return unicode_type(e)
59 except UnicodeError:
59 except UnicodeError:
60 pass
60 pass
61
61
62 try:
62 try:
63 return str_to_unicode(str(e))
63 return str_to_unicode(str(e))
64 except UnicodeError:
64 except UnicodeError:
65 pass
65 pass
66
66
67 try:
67 try:
68 return str_to_unicode(repr(e))
68 return str_to_unicode(repr(e))
69 except UnicodeError:
69 except UnicodeError:
70 pass
70 pass
71
71
72 return u'Unrecoverably corrupt evalue'
72 return u'Unrecoverably corrupt evalue'
73
73
74 if sys.version_info[0] >= 3:
74 if sys.version_info[0] >= 3:
75 PY3 = True
75 PY3 = True
76
76
77 # keep reference to builtin_mod because the kernel overrides that value
77 # keep reference to builtin_mod because the kernel overrides that value
78 # to forward requests to a frontend.
78 # to forward requests to a frontend.
79 def input(prompt=''):
79 def input(prompt=''):
80 return builtin_mod.input(prompt)
80 return builtin_mod.input(prompt)
81
81
82 builtin_mod_name = "builtins"
82 builtin_mod_name = "builtins"
83 import builtins as builtin_mod
83 import builtins as builtin_mod
84
84
85 str_to_unicode = no_code
85 str_to_unicode = no_code
86 unicode_to_str = no_code
86 unicode_to_str = no_code
87 str_to_bytes = encode
87 str_to_bytes = encode
88 bytes_to_str = decode
88 bytes_to_str = decode
89 cast_bytes_py2 = no_code
89 cast_bytes_py2 = no_code
90 cast_unicode_py2 = no_code
90 cast_unicode_py2 = no_code
91
91
92 string_types = (str,)
92 string_types = (str,)
93 unicode_type = str
93 unicode_type = str
94
94
95 def isidentifier(s, dotted=False):
95 def isidentifier(s, dotted=False):
96 if dotted:
96 if dotted:
97 return all(isidentifier(a) for a in s.split("."))
97 return all(isidentifier(a) for a in s.split("."))
98 return s.isidentifier()
98 return s.isidentifier()
99
99
100 open = orig_open
100 open = orig_open
101 xrange = range
101 xrange = range
102 def iteritems(d): return iter(d.items())
102 def iteritems(d): return iter(d.items())
103 def itervalues(d): return iter(d.values())
103 def itervalues(d): return iter(d.values())
104 getcwd = os.getcwd
104 getcwd = os.getcwd
105
105
106 MethodType = types.MethodType
106 MethodType = types.MethodType
107
107
108 def execfile(fname, glob, loc=None):
108 def execfile(fname, glob, loc=None):
109 loc = loc if (loc is not None) else glob
109 loc = loc if (loc is not None) else glob
110 with open(fname, 'rb') as f:
110 with open(fname, 'rb') as f:
111 exec(compile(f.read(), fname, 'exec'), glob, loc)
111 exec(compile(f.read(), fname, 'exec'), glob, loc)
112
112
113 # Refactor print statements in doctests.
113 # Refactor print statements in doctests.
114 _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE)
114 _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE)
115 def _print_statement_sub(match):
115 def _print_statement_sub(match):
116 expr = match.groups('expr')
116 expr = match.groups('expr')
117 return "print(%s)" % expr
117 return "print(%s)" % expr
118
118
119 @_modify_str_or_docstring
119 @_modify_str_or_docstring
120 def doctest_refactor_print(doc):
120 def doctest_refactor_print(doc):
121 """Refactor 'print x' statements in a doctest to print(x) style. 2to3
121 """Refactor 'print x' statements in a doctest to print(x) style. 2to3
122 unfortunately doesn't pick up on our doctests.
122 unfortunately doesn't pick up on our doctests.
123
123
124 Can accept a string or a function, so it can be used as a decorator."""
124 Can accept a string or a function, so it can be used as a decorator."""
125 return _print_statement_re.sub(_print_statement_sub, doc)
125 return _print_statement_re.sub(_print_statement_sub, doc)
126
126
127 # Abstract u'abc' syntax:
127 # Abstract u'abc' syntax:
128 @_modify_str_or_docstring
128 @_modify_str_or_docstring
129 def u_format(s):
129 def u_format(s):
130 """"{u}'abc'" --> "'abc'" (Python 3)
130 """"{u}'abc'" --> "'abc'" (Python 3)
131
131
132 Accepts a string or a function, so it can be used as a decorator."""
132 Accepts a string or a function, so it can be used as a decorator."""
133 return s.format(u='')
133 return s.format(u='')
134
135 def get_closure(f):
136 """Get a function's closure attribute"""
137 return f.__closure__
134
138
135 else:
139 else:
136 PY3 = False
140 PY3 = False
137
141
138 # keep reference to builtin_mod because the kernel overrides that value
142 # keep reference to builtin_mod because the kernel overrides that value
139 # to forward requests to a frontend.
143 # to forward requests to a frontend.
140 def input(prompt=''):
144 def input(prompt=''):
141 return builtin_mod.raw_input(prompt)
145 return builtin_mod.raw_input(prompt)
142
146
143 builtin_mod_name = "__builtin__"
147 builtin_mod_name = "__builtin__"
144 import __builtin__ as builtin_mod
148 import __builtin__ as builtin_mod
145
149
146 str_to_unicode = decode
150 str_to_unicode = decode
147 unicode_to_str = encode
151 unicode_to_str = encode
148 str_to_bytes = no_code
152 str_to_bytes = no_code
149 bytes_to_str = no_code
153 bytes_to_str = no_code
150 cast_bytes_py2 = cast_bytes
154 cast_bytes_py2 = cast_bytes
151 cast_unicode_py2 = cast_unicode
155 cast_unicode_py2 = cast_unicode
152
156
153 string_types = (str, unicode)
157 string_types = (str, unicode)
154 unicode_type = unicode
158 unicode_type = unicode
155
159
156 import re
160 import re
157 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
161 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
158 def isidentifier(s, dotted=False):
162 def isidentifier(s, dotted=False):
159 if dotted:
163 if dotted:
160 return all(isidentifier(a) for a in s.split("."))
164 return all(isidentifier(a) for a in s.split("."))
161 return bool(_name_re.match(s))
165 return bool(_name_re.match(s))
162
166
163 class open(object):
167 class open(object):
164 """Wrapper providing key part of Python 3 open() interface."""
168 """Wrapper providing key part of Python 3 open() interface."""
165 def __init__(self, fname, mode="r", encoding="utf-8"):
169 def __init__(self, fname, mode="r", encoding="utf-8"):
166 self.f = orig_open(fname, mode)
170 self.f = orig_open(fname, mode)
167 self.enc = encoding
171 self.enc = encoding
168
172
169 def write(self, s):
173 def write(self, s):
170 return self.f.write(s.encode(self.enc))
174 return self.f.write(s.encode(self.enc))
171
175
172 def read(self, size=-1):
176 def read(self, size=-1):
173 return self.f.read(size).decode(self.enc)
177 return self.f.read(size).decode(self.enc)
174
178
175 def close(self):
179 def close(self):
176 return self.f.close()
180 return self.f.close()
177
181
178 def __enter__(self):
182 def __enter__(self):
179 return self
183 return self
180
184
181 def __exit__(self, etype, value, traceback):
185 def __exit__(self, etype, value, traceback):
182 self.f.close()
186 self.f.close()
183
187
184 xrange = xrange
188 xrange = xrange
185 def iteritems(d): return d.iteritems()
189 def iteritems(d): return d.iteritems()
186 def itervalues(d): return d.itervalues()
190 def itervalues(d): return d.itervalues()
187 getcwd = os.getcwdu
191 getcwd = os.getcwdu
188
192
189 def MethodType(func, instance):
193 def MethodType(func, instance):
190 return types.MethodType(func, instance, type(instance))
194 return types.MethodType(func, instance, type(instance))
191
195
192 def doctest_refactor_print(func_or_str):
196 def doctest_refactor_print(func_or_str):
193 return func_or_str
197 return func_or_str
194
198
199 def get_closure(f):
200 """Get a function's closure attribute"""
201 return f.func_closure
195
202
196 # Abstract u'abc' syntax:
203 # Abstract u'abc' syntax:
197 @_modify_str_or_docstring
204 @_modify_str_or_docstring
198 def u_format(s):
205 def u_format(s):
199 """"{u}'abc'" --> "u'abc'" (Python 2)
206 """"{u}'abc'" --> "u'abc'" (Python 2)
200
207
201 Accepts a string or a function, so it can be used as a decorator."""
208 Accepts a string or a function, so it can be used as a decorator."""
202 return s.format(u='u')
209 return s.format(u='u')
203
210
204 if sys.platform == 'win32':
211 if sys.platform == 'win32':
205 def execfile(fname, glob=None, loc=None):
212 def execfile(fname, glob=None, loc=None):
206 loc = loc if (loc is not None) else glob
213 loc = loc if (loc is not None) else glob
207 # The rstrip() is necessary b/c trailing whitespace in files will
214 # The rstrip() is necessary b/c trailing whitespace in files will
208 # cause an IndentationError in Python 2.6 (this was fixed in 2.7,
215 # cause an IndentationError in Python 2.6 (this was fixed in 2.7,
209 # but we still support 2.6). See issue 1027.
216 # but we still support 2.6). See issue 1027.
210 scripttext = builtin_mod.open(fname).read().rstrip() + '\n'
217 scripttext = builtin_mod.open(fname).read().rstrip() + '\n'
211 # compile converts unicode filename to str assuming
218 # compile converts unicode filename to str assuming
212 # ascii. Let's do the conversion before calling compile
219 # ascii. Let's do the conversion before calling compile
213 if isinstance(fname, unicode):
220 if isinstance(fname, unicode):
214 filename = unicode_to_str(fname)
221 filename = unicode_to_str(fname)
215 else:
222 else:
216 filename = fname
223 filename = fname
217 exec(compile(scripttext, filename, 'exec'), glob, loc)
224 exec(compile(scripttext, filename, 'exec'), glob, loc)
218 else:
225 else:
219 def execfile(fname, *where):
226 def execfile(fname, *where):
220 if isinstance(fname, unicode):
227 if isinstance(fname, unicode):
221 filename = fname.encode(sys.getfilesystemencoding())
228 filename = fname.encode(sys.getfilesystemencoding())
222 else:
229 else:
223 filename = fname
230 filename = fname
224 builtin_mod.execfile(filename, *where)
231 builtin_mod.execfile(filename, *where)
225
232
226
233
227 def annotate(**kwargs):
234 def annotate(**kwargs):
228 """Python 3 compatible function annotation for Python 2."""
235 """Python 3 compatible function annotation for Python 2."""
229 if not kwargs:
236 if not kwargs:
230 raise ValueError('annotations must be provided as keyword arguments')
237 raise ValueError('annotations must be provided as keyword arguments')
231 def dec(f):
238 def dec(f):
232 if hasattr(f, '__annotations__'):
239 if hasattr(f, '__annotations__'):
233 for k, v in kwargs.items():
240 for k, v in kwargs.items():
234 f.__annotations__[k] = v
241 f.__annotations__[k] = v
235 else:
242 else:
236 f.__annotations__ = kwargs
243 f.__annotations__ = kwargs
237 return f
244 return f
238 return dec
245 return dec
239
246
240
247
241 # Parts below taken from six:
248 # Parts below taken from six:
242 # Copyright (c) 2010-2013 Benjamin Peterson
249 # Copyright (c) 2010-2013 Benjamin Peterson
243 #
250 #
244 # Permission is hereby granted, free of charge, to any person obtaining a copy
251 # Permission is hereby granted, free of charge, to any person obtaining a copy
245 # of this software and associated documentation files (the "Software"), to deal
252 # of this software and associated documentation files (the "Software"), to deal
246 # in the Software without restriction, including without limitation the rights
253 # in the Software without restriction, including without limitation the rights
247 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
254 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
248 # copies of the Software, and to permit persons to whom the Software is
255 # copies of the Software, and to permit persons to whom the Software is
249 # furnished to do so, subject to the following conditions:
256 # furnished to do so, subject to the following conditions:
250 #
257 #
251 # The above copyright notice and this permission notice shall be included in all
258 # The above copyright notice and this permission notice shall be included in all
252 # copies or substantial portions of the Software.
259 # copies or substantial portions of the Software.
253 #
260 #
254 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
261 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
255 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
262 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
256 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
263 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
257 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
264 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
258 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
265 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
259 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
266 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
260 # SOFTWARE.
267 # SOFTWARE.
261
268
262 def with_metaclass(meta, *bases):
269 def with_metaclass(meta, *bases):
263 """Create a base class with a metaclass."""
270 """Create a base class with a metaclass."""
264 return meta("_NewBase", bases, {})
271 return meta("_NewBase", bases, {})
General Comments 0
You need to be logged in to leave comments. Login now