##// END OF EJS Templates
docstring for CannedObject.__init__
MinRK -
Show More
@@ -1,338 +1,354 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """Pickle related utilities. Perhaps this should be called 'can'."""
3 """Pickle related utilities. Perhaps this should be called 'can'."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008-2011 The IPython Development Team
8 # Copyright (C) 2008-2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 import copy
18 import copy
19 import logging
19 import logging
20 import sys
20 import sys
21 from types import FunctionType
21 from types import FunctionType
22
22
23 try:
23 try:
24 import cPickle as pickle
24 import cPickle as pickle
25 except ImportError:
25 except ImportError:
26 import pickle
26 import pickle
27
27
28 try:
28 try:
29 import numpy
29 import numpy
30 except:
30 except:
31 numpy = None
31 numpy = None
32
32
33 import codeutil
33 import codeutil
34 import py3compat
34 import py3compat
35 from importstring import import_item
35 from importstring import import_item
36
36
37 from IPython.config import Application
37 from IPython.config import Application
38
38
39 if py3compat.PY3:
39 if py3compat.PY3:
40 buffer = memoryview
40 buffer = memoryview
41 class_type = type
41 class_type = type
42 else:
42 else:
43 from types import ClassType
43 from types import ClassType
44 class_type = (type, ClassType)
44 class_type = (type, ClassType)
45
45
46 #-------------------------------------------------------------------------------
46 #-------------------------------------------------------------------------------
47 # Classes
47 # Classes
48 #-------------------------------------------------------------------------------
48 #-------------------------------------------------------------------------------
49
49
50
50
51 class CannedObject(object):
51 class CannedObject(object):
52 def __init__(self, obj, keys=[], hook=None):
52 def __init__(self, obj, keys=[], hook=None):
53 """can an object for safe pickling
54
55 Parameters
56 ==========
57
58 obj:
59 The object to be canned
60 keys: list (optional)
61 list of attribute names that will be explicitly canned / uncanned
62 hook: callable (optional)
63 An optional extra callable,
64 which can do additional processing of the uncanned object.
65
66 large data may be offloaded into the buffers list,
67 used for zero-copy transfers.
68 """
53 self.keys = keys
69 self.keys = keys
54 self.obj = copy.copy(obj)
70 self.obj = copy.copy(obj)
55 self.hook = can(hook)
71 self.hook = can(hook)
56 for key in keys:
72 for key in keys:
57 setattr(self.obj, key, can(getattr(obj, key)))
73 setattr(self.obj, key, can(getattr(obj, key)))
58
74
59 self.buffers = []
75 self.buffers = []
60
76
61 def get_object(self, g=None):
77 def get_object(self, g=None):
62 if g is None:
78 if g is None:
63 g = {}
79 g = {}
64 obj = self.obj
80 obj = self.obj
65 for key in self.keys:
81 for key in self.keys:
66 setattr(obj, key, uncan(getattr(obj, key), g))
82 setattr(obj, key, uncan(getattr(obj, key), g))
67
83
68 if self.hook:
84 if self.hook:
69 self.hook = uncan(self.hook, g)
85 self.hook = uncan(self.hook, g)
70 self.hook(obj, g)
86 self.hook(obj, g)
71 return self.obj
87 return self.obj
72
88
73
89
74 class Reference(CannedObject):
90 class Reference(CannedObject):
75 """object for wrapping a remote reference by name."""
91 """object for wrapping a remote reference by name."""
76 def __init__(self, name):
92 def __init__(self, name):
77 if not isinstance(name, basestring):
93 if not isinstance(name, basestring):
78 raise TypeError("illegal name: %r"%name)
94 raise TypeError("illegal name: %r"%name)
79 self.name = name
95 self.name = name
80 self.buffers = []
96 self.buffers = []
81
97
82 def __repr__(self):
98 def __repr__(self):
83 return "<Reference: %r>"%self.name
99 return "<Reference: %r>"%self.name
84
100
85 def get_object(self, g=None):
101 def get_object(self, g=None):
86 if g is None:
102 if g is None:
87 g = {}
103 g = {}
88
104
89 return eval(self.name, g)
105 return eval(self.name, g)
90
106
91
107
92 class CannedFunction(CannedObject):
108 class CannedFunction(CannedObject):
93
109
94 def __init__(self, f):
110 def __init__(self, f):
95 self._check_type(f)
111 self._check_type(f)
96 self.code = f.func_code
112 self.code = f.func_code
97 if f.func_defaults:
113 if f.func_defaults:
98 self.defaults = [ can(fd) for fd in f.func_defaults ]
114 self.defaults = [ can(fd) for fd in f.func_defaults ]
99 else:
115 else:
100 self.defaults = None
116 self.defaults = None
101 self.module = f.__module__ or '__main__'
117 self.module = f.__module__ or '__main__'
102 self.__name__ = f.__name__
118 self.__name__ = f.__name__
103 self.buffers = []
119 self.buffers = []
104
120
105 def _check_type(self, obj):
121 def _check_type(self, obj):
106 assert isinstance(obj, FunctionType), "Not a function type"
122 assert isinstance(obj, FunctionType), "Not a function type"
107
123
108 def get_object(self, g=None):
124 def get_object(self, g=None):
109 # try to load function back into its module:
125 # try to load function back into its module:
110 if not self.module.startswith('__'):
126 if not self.module.startswith('__'):
111 __import__(self.module)
127 __import__(self.module)
112 g = sys.modules[self.module].__dict__
128 g = sys.modules[self.module].__dict__
113
129
114 if g is None:
130 if g is None:
115 g = {}
131 g = {}
116 if self.defaults:
132 if self.defaults:
117 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
133 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
118 else:
134 else:
119 defaults = None
135 defaults = None
120 newFunc = FunctionType(self.code, g, self.__name__, defaults)
136 newFunc = FunctionType(self.code, g, self.__name__, defaults)
121 return newFunc
137 return newFunc
122
138
123 class CannedClass(CannedObject):
139 class CannedClass(CannedObject):
124
140
125 def __init__(self, cls):
141 def __init__(self, cls):
126 self._check_type(cls)
142 self._check_type(cls)
127 self.name = cls.__name__
143 self.name = cls.__name__
128 self.old_style = not isinstance(cls, type)
144 self.old_style = not isinstance(cls, type)
129 self._canned_dict = {}
145 self._canned_dict = {}
130 for k,v in cls.__dict__.items():
146 for k,v in cls.__dict__.items():
131 if k not in ('__weakref__', '__dict__'):
147 if k not in ('__weakref__', '__dict__'):
132 self._canned_dict[k] = can(v)
148 self._canned_dict[k] = can(v)
133 if self.old_style:
149 if self.old_style:
134 mro = []
150 mro = []
135 else:
151 else:
136 mro = cls.mro()
152 mro = cls.mro()
137
153
138 self.parents = [ can(c) for c in mro[1:] ]
154 self.parents = [ can(c) for c in mro[1:] ]
139 self.buffers = []
155 self.buffers = []
140
156
141 def _check_type(self, obj):
157 def _check_type(self, obj):
142 assert isinstance(obj, class_type), "Not a class type"
158 assert isinstance(obj, class_type), "Not a class type"
143
159
144 def get_object(self, g=None):
160 def get_object(self, g=None):
145 parents = tuple(uncan(p, g) for p in self.parents)
161 parents = tuple(uncan(p, g) for p in self.parents)
146 return type(self.name, parents, uncan_dict(self._canned_dict, g=g))
162 return type(self.name, parents, uncan_dict(self._canned_dict, g=g))
147
163
148 class CannedArray(CannedObject):
164 class CannedArray(CannedObject):
149 def __init__(self, obj):
165 def __init__(self, obj):
150 self.shape = obj.shape
166 self.shape = obj.shape
151 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
167 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
152 if sum(obj.shape) == 0:
168 if sum(obj.shape) == 0:
153 # just pickle it
169 # just pickle it
154 self.buffers = [pickle.dumps(obj, -1)]
170 self.buffers = [pickle.dumps(obj, -1)]
155 else:
171 else:
156 # ensure contiguous
172 # ensure contiguous
157 obj = numpy.ascontiguousarray(obj, dtype=None)
173 obj = numpy.ascontiguousarray(obj, dtype=None)
158 self.buffers = [buffer(obj)]
174 self.buffers = [buffer(obj)]
159
175
160 def get_object(self, g=None):
176 def get_object(self, g=None):
161 data = self.buffers[0]
177 data = self.buffers[0]
162 if sum(self.shape) == 0:
178 if sum(self.shape) == 0:
163 # no shape, we just pickled it
179 # no shape, we just pickled it
164 return pickle.loads(data)
180 return pickle.loads(data)
165 else:
181 else:
166 return numpy.frombuffer(data, dtype=self.dtype).reshape(self.shape)
182 return numpy.frombuffer(data, dtype=self.dtype).reshape(self.shape)
167
183
168
184
169 class CannedBytes(CannedObject):
185 class CannedBytes(CannedObject):
170 wrap = bytes
186 wrap = bytes
171 def __init__(self, obj):
187 def __init__(self, obj):
172 self.buffers = [obj]
188 self.buffers = [obj]
173
189
174 def get_object(self, g=None):
190 def get_object(self, g=None):
175 data = self.buffers[0]
191 data = self.buffers[0]
176 return self.wrap(data)
192 return self.wrap(data)
177
193
178 def CannedBuffer(CannedBytes):
194 def CannedBuffer(CannedBytes):
179 wrap = buffer
195 wrap = buffer
180
196
181 #-------------------------------------------------------------------------------
197 #-------------------------------------------------------------------------------
182 # Functions
198 # Functions
183 #-------------------------------------------------------------------------------
199 #-------------------------------------------------------------------------------
184
200
185 def _logger():
201 def _logger():
186 """get the logger for the current Application
202 """get the logger for the current Application
187
203
188 the root logger will be used if no Application is running
204 the root logger will be used if no Application is running
189 """
205 """
190 if Application.initialized():
206 if Application.initialized():
191 logger = Application.instance().log
207 logger = Application.instance().log
192 else:
208 else:
193 logger = logging.getLogger()
209 logger = logging.getLogger()
194 if not logger.handlers:
210 if not logger.handlers:
195 logging.basicConfig()
211 logging.basicConfig()
196
212
197 return logger
213 return logger
198
214
199 def _import_mapping(mapping, original=None):
215 def _import_mapping(mapping, original=None):
200 """import any string-keys in a type mapping
216 """import any string-keys in a type mapping
201
217
202 """
218 """
203 log = _logger()
219 log = _logger()
204 log.debug("Importing canning map")
220 log.debug("Importing canning map")
205 for key,value in mapping.items():
221 for key,value in mapping.items():
206 if isinstance(key, basestring):
222 if isinstance(key, basestring):
207 try:
223 try:
208 cls = import_item(key)
224 cls = import_item(key)
209 except Exception:
225 except Exception:
210 if original and key not in original:
226 if original and key not in original:
211 # only message on user-added classes
227 # only message on user-added classes
212 log.error("cannning class not importable: %r", key, exc_info=True)
228 log.error("cannning class not importable: %r", key, exc_info=True)
213 mapping.pop(key)
229 mapping.pop(key)
214 else:
230 else:
215 mapping[cls] = mapping.pop(key)
231 mapping[cls] = mapping.pop(key)
216
232
217 def istype(obj, check):
233 def istype(obj, check):
218 """like isinstance(obj, check), but strict
234 """like isinstance(obj, check), but strict
219
235
220 This won't catch subclasses.
236 This won't catch subclasses.
221 """
237 """
222 if isinstance(check, tuple):
238 if isinstance(check, tuple):
223 for cls in check:
239 for cls in check:
224 if type(obj) is cls:
240 if type(obj) is cls:
225 return True
241 return True
226 return False
242 return False
227 else:
243 else:
228 return type(obj) is check
244 return type(obj) is check
229
245
230 def can(obj):
246 def can(obj):
231 """prepare an object for pickling"""
247 """prepare an object for pickling"""
232
248
233 import_needed = False
249 import_needed = False
234
250
235 for cls,canner in can_map.iteritems():
251 for cls,canner in can_map.iteritems():
236 if isinstance(cls, basestring):
252 if isinstance(cls, basestring):
237 import_needed = True
253 import_needed = True
238 break
254 break
239 elif istype(obj, cls):
255 elif istype(obj, cls):
240 return canner(obj)
256 return canner(obj)
241
257
242 if import_needed:
258 if import_needed:
243 # perform can_map imports, then try again
259 # perform can_map imports, then try again
244 # this will usually only happen once
260 # this will usually only happen once
245 _import_mapping(can_map, _original_can_map)
261 _import_mapping(can_map, _original_can_map)
246 return can(obj)
262 return can(obj)
247
263
248 return obj
264 return obj
249
265
250 def can_class(obj):
266 def can_class(obj):
251 if isinstance(obj, class_type) and obj.__module__ == '__main__':
267 if isinstance(obj, class_type) and obj.__module__ == '__main__':
252 return CannedClass(obj)
268 return CannedClass(obj)
253 else:
269 else:
254 return obj
270 return obj
255
271
256 def can_dict(obj):
272 def can_dict(obj):
257 """can the *values* of a dict"""
273 """can the *values* of a dict"""
258 if istype(obj, dict):
274 if istype(obj, dict):
259 newobj = {}
275 newobj = {}
260 for k, v in obj.iteritems():
276 for k, v in obj.iteritems():
261 newobj[k] = can(v)
277 newobj[k] = can(v)
262 return newobj
278 return newobj
263 else:
279 else:
264 return obj
280 return obj
265
281
266 sequence_types = (list, tuple, set)
282 sequence_types = (list, tuple, set)
267
283
268 def can_sequence(obj):
284 def can_sequence(obj):
269 """can the elements of a sequence"""
285 """can the elements of a sequence"""
270 if istype(obj, sequence_types):
286 if istype(obj, sequence_types):
271 t = type(obj)
287 t = type(obj)
272 return t([can(i) for i in obj])
288 return t([can(i) for i in obj])
273 else:
289 else:
274 return obj
290 return obj
275
291
276 def uncan(obj, g=None):
292 def uncan(obj, g=None):
277 """invert canning"""
293 """invert canning"""
278
294
279 import_needed = False
295 import_needed = False
280 for cls,uncanner in uncan_map.iteritems():
296 for cls,uncanner in uncan_map.iteritems():
281 if isinstance(cls, basestring):
297 if isinstance(cls, basestring):
282 import_needed = True
298 import_needed = True
283 break
299 break
284 elif isinstance(obj, cls):
300 elif isinstance(obj, cls):
285 return uncanner(obj, g)
301 return uncanner(obj, g)
286
302
287 if import_needed:
303 if import_needed:
288 # perform uncan_map imports, then try again
304 # perform uncan_map imports, then try again
289 # this will usually only happen once
305 # this will usually only happen once
290 _import_mapping(uncan_map, _original_uncan_map)
306 _import_mapping(uncan_map, _original_uncan_map)
291 return uncan(obj, g)
307 return uncan(obj, g)
292
308
293 return obj
309 return obj
294
310
295 def uncan_dict(obj, g=None):
311 def uncan_dict(obj, g=None):
296 if istype(obj, dict):
312 if istype(obj, dict):
297 newobj = {}
313 newobj = {}
298 for k, v in obj.iteritems():
314 for k, v in obj.iteritems():
299 newobj[k] = uncan(v,g)
315 newobj[k] = uncan(v,g)
300 return newobj
316 return newobj
301 else:
317 else:
302 return obj
318 return obj
303
319
304 def uncan_sequence(obj, g=None):
320 def uncan_sequence(obj, g=None):
305 if istype(obj, sequence_types):
321 if istype(obj, sequence_types):
306 t = type(obj)
322 t = type(obj)
307 return t([uncan(i,g) for i in obj])
323 return t([uncan(i,g) for i in obj])
308 else:
324 else:
309 return obj
325 return obj
310
326
311 def _uncan_dependent_hook(dep, g=None):
327 def _uncan_dependent_hook(dep, g=None):
312 dep.check_dependency()
328 dep.check_dependency()
313
329
314 def can_dependent(obj):
330 def can_dependent(obj):
315 return CannedObject(obj, keys=('f', 'df'), hook=_uncan_dependent_hook)
331 return CannedObject(obj, keys=('f', 'df'), hook=_uncan_dependent_hook)
316
332
317 #-------------------------------------------------------------------------------
333 #-------------------------------------------------------------------------------
318 # API dictionaries
334 # API dictionaries
319 #-------------------------------------------------------------------------------
335 #-------------------------------------------------------------------------------
320
336
321 # These dicts can be extended for custom serialization of new objects
337 # These dicts can be extended for custom serialization of new objects
322
338
323 can_map = {
339 can_map = {
324 'IPython.parallel.dependent' : can_dependent,
340 'IPython.parallel.dependent' : can_dependent,
325 'numpy.ndarray' : CannedArray,
341 'numpy.ndarray' : CannedArray,
326 FunctionType : CannedFunction,
342 FunctionType : CannedFunction,
327 bytes : CannedBytes,
343 bytes : CannedBytes,
328 buffer : CannedBuffer,
344 buffer : CannedBuffer,
329 class_type : can_class,
345 class_type : can_class,
330 }
346 }
331
347
332 uncan_map = {
348 uncan_map = {
333 CannedObject : lambda obj, g: obj.get_object(g),
349 CannedObject : lambda obj, g: obj.get_object(g),
334 }
350 }
335
351
336 # for use in _import_mapping:
352 # for use in _import_mapping:
337 _original_can_map = can_map.copy()
353 _original_can_map = can_map.copy()
338 _original_uncan_map = uncan_map.copy()
354 _original_uncan_map = uncan_map.copy()
General Comments 0
You need to be logged in to leave comments. Login now