##// END OF EJS Templates
can classes
MinRK -
Show More
@@ -1,277 +1,309 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, ClassType
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
41
42 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
43 # Classes
43 # Classes
44 #-------------------------------------------------------------------------------
44 #-------------------------------------------------------------------------------
45
45
46
46
47 class CannedObject(object):
47 class CannedObject(object):
48 def __init__(self, obj, keys=[]):
48 def __init__(self, obj, keys=[]):
49 self.keys = keys
49 self.keys = keys
50 self.obj = copy.copy(obj)
50 self.obj = copy.copy(obj)
51 for key in keys:
51 for key in keys:
52 setattr(self.obj, key, can(getattr(obj, key)))
52 setattr(self.obj, key, can(getattr(obj, key)))
53
53
54 self.buffers = []
54 self.buffers = []
55
55
56 def get_object(self, g=None):
56 def get_object(self, g=None):
57 if g is None:
57 if g is None:
58 g = {}
58 g = {}
59 for key in self.keys:
59 for key in self.keys:
60 setattr(self.obj, key, uncan(getattr(self.obj, key), g))
60 setattr(self.obj, key, uncan(getattr(self.obj, key), g))
61 return self.obj
61 return self.obj
62
62
63
63
64 class Reference(CannedObject):
64 class Reference(CannedObject):
65 """object for wrapping a remote reference by name."""
65 """object for wrapping a remote reference by name."""
66 def __init__(self, name):
66 def __init__(self, name):
67 if not isinstance(name, basestring):
67 if not isinstance(name, basestring):
68 raise TypeError("illegal name: %r"%name)
68 raise TypeError("illegal name: %r"%name)
69 self.name = name
69 self.name = name
70 self.buffers = []
70 self.buffers = []
71
71
72 def __repr__(self):
72 def __repr__(self):
73 return "<Reference: %r>"%self.name
73 return "<Reference: %r>"%self.name
74
74
75 def get_object(self, g=None):
75 def get_object(self, g=None):
76 if g is None:
76 if g is None:
77 g = {}
77 g = {}
78
78
79 return eval(self.name, g)
79 return eval(self.name, g)
80
80
81
81
82 class CannedFunction(CannedObject):
82 class CannedFunction(CannedObject):
83
83
84 def __init__(self, f):
84 def __init__(self, f):
85 self._check_type(f)
85 self._check_type(f)
86 self.code = f.func_code
86 self.code = f.func_code
87 if f.func_defaults:
87 if f.func_defaults:
88 self.defaults = [ can(fd) for fd in f.func_defaults ]
88 self.defaults = [ can(fd) for fd in f.func_defaults ]
89 else:
89 else:
90 self.defaults = None
90 self.defaults = None
91 self.module = f.__module__ or '__main__'
91 self.module = f.__module__ or '__main__'
92 self.__name__ = f.__name__
92 self.__name__ = f.__name__
93 self.buffers = []
93 self.buffers = []
94
94
95 def _check_type(self, obj):
95 def _check_type(self, obj):
96 assert isinstance(obj, FunctionType), "Not a function type"
96 assert isinstance(obj, FunctionType), "Not a function type"
97
97
98 def get_object(self, g=None):
98 def get_object(self, g=None):
99 # try to load function back into its module:
99 # try to load function back into its module:
100 if not self.module.startswith('__'):
100 if not self.module.startswith('__'):
101 __import__(self.module)
101 __import__(self.module)
102 g = sys.modules[self.module].__dict__
102 g = sys.modules[self.module].__dict__
103
103
104 if g is None:
104 if g is None:
105 g = {}
105 g = {}
106 if self.defaults:
106 if self.defaults:
107 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
107 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
108 else:
108 else:
109 defaults = None
109 defaults = None
110 newFunc = FunctionType(self.code, g, self.__name__, defaults)
110 newFunc = FunctionType(self.code, g, self.__name__, defaults)
111 return newFunc
111 return newFunc
112
112
113 class CannedClass(CannedObject):
114
115 def __init__(self, cls):
116 self._check_type(cls)
117 self.name = cls.__name__
118 self.old_style = not isinstance(cls, type)
119 self._canned_dict = {}
120 for k,v in cls.__dict__.items():
121 if k not in ('__weakref__', '__dict__'):
122 self._canned_dict[k] = can(v)
123 if self.old_style:
124 mro = []
125 else:
126 mro = cls.mro()
127
128 self.parents = [ can(c) for c in mro[1:] ]
129 self.buffers = []
130
131 def _check_type(self, obj):
132 assert isinstance(obj, (type, ClassType)), "Not a class type"
133
134 def get_object(self, g=None):
135 parents = tuple(uncan(p, g) for p in self.parents)
136 return type(self.name, parents, uncan_dict(self._canned_dict, g=g))
113
137
114 class CannedArray(CannedObject):
138 class CannedArray(CannedObject):
115 def __init__(self, obj):
139 def __init__(self, obj):
116 self.shape = obj.shape
140 self.shape = obj.shape
117 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
141 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
118 if sum(obj.shape) == 0:
142 if sum(obj.shape) == 0:
119 # just pickle it
143 # just pickle it
120 self.buffers = [pickle.dumps(obj, -1)]
144 self.buffers = [pickle.dumps(obj, -1)]
121 else:
145 else:
122 # ensure contiguous
146 # ensure contiguous
123 obj = numpy.ascontiguousarray(obj, dtype=None)
147 obj = numpy.ascontiguousarray(obj, dtype=None)
124 self.buffers = [buffer(obj)]
148 self.buffers = [buffer(obj)]
125
149
126 def get_object(self, g=None):
150 def get_object(self, g=None):
127 data = self.buffers[0]
151 data = self.buffers[0]
128 if sum(self.shape) == 0:
152 if sum(self.shape) == 0:
129 # no shape, we just pickled it
153 # no shape, we just pickled it
130 return pickle.loads(data)
154 return pickle.loads(data)
131 else:
155 else:
132 return numpy.frombuffer(data, dtype=self.dtype).reshape(self.shape)
156 return numpy.frombuffer(data, dtype=self.dtype).reshape(self.shape)
133
157
134
158
135 class CannedBytes(CannedObject):
159 class CannedBytes(CannedObject):
136 wrap = bytes
160 wrap = bytes
137 def __init__(self, obj):
161 def __init__(self, obj):
138 self.buffers = [obj]
162 self.buffers = [obj]
139
163
140 def get_object(self, g=None):
164 def get_object(self, g=None):
141 data = self.buffers[0]
165 data = self.buffers[0]
142 return self.wrap(data)
166 return self.wrap(data)
143
167
144 def CannedBuffer(CannedBytes):
168 def CannedBuffer(CannedBytes):
145 wrap = buffer
169 wrap = buffer
146
170
147 #-------------------------------------------------------------------------------
171 #-------------------------------------------------------------------------------
148 # Functions
172 # Functions
149 #-------------------------------------------------------------------------------
173 #-------------------------------------------------------------------------------
150
174
151 def _logger():
175 def _logger():
152 """get the logger for the current Application
176 """get the logger for the current Application
153
177
154 the root logger will be used if no Application is running
178 the root logger will be used if no Application is running
155 """
179 """
156 if Application.initialized():
180 if Application.initialized():
157 logger = Application.instance().log
181 logger = Application.instance().log
158 else:
182 else:
159 logger = logging.getLogger()
183 logger = logging.getLogger()
160 if not logger.handlers:
184 if not logger.handlers:
161 logging.basicConfig()
185 logging.basicConfig()
162
186
163 return logger
187 return logger
164
188
165 def _import_mapping(mapping, original=None):
189 def _import_mapping(mapping, original=None):
166 """import any string-keys in a type mapping
190 """import any string-keys in a type mapping
167
191
168 """
192 """
169 log = _logger()
193 log = _logger()
170 log.debug("Importing canning map")
194 log.debug("Importing canning map")
171 for key,value in mapping.items():
195 for key,value in mapping.items():
172 if isinstance(key, basestring):
196 if isinstance(key, basestring):
173 try:
197 try:
174 cls = import_item(key)
198 cls = import_item(key)
175 except Exception:
199 except Exception:
176 if original and key not in original:
200 if original and key not in original:
177 # only message on user-added classes
201 # only message on user-added classes
178 log.error("cannning class not importable: %r", key, exc_info=True)
202 log.error("cannning class not importable: %r", key, exc_info=True)
179 mapping.pop(key)
203 mapping.pop(key)
180 else:
204 else:
181 mapping[cls] = mapping.pop(key)
205 mapping[cls] = mapping.pop(key)
182
206
183 def can(obj):
207 def can(obj):
184 """prepare an object for pickling"""
208 """prepare an object for pickling"""
185
209
186 import_needed = False
210 import_needed = False
187
211
188 for cls,canner in can_map.iteritems():
212 for cls,canner in can_map.iteritems():
189 if isinstance(cls, basestring):
213 if isinstance(cls, basestring):
190 import_needed = True
214 import_needed = True
191 break
215 break
192 elif isinstance(obj, cls):
216 elif isinstance(obj, cls):
193 return canner(obj)
217 return canner(obj)
194
218
195 if import_needed:
219 if import_needed:
196 # perform can_map imports, then try again
220 # perform can_map imports, then try again
197 # this will usually only happen once
221 # this will usually only happen once
198 _import_mapping(can_map, _original_can_map)
222 _import_mapping(can_map, _original_can_map)
199 return can(obj)
223 return can(obj)
200
224
201 return obj
225 return obj
202
226
227 def can_class(obj):
228 if isinstance(obj, (type, ClassType)) and obj.__module__ == '__main__':
229 return CannedClass(obj)
230 else:
231 return obj
232
203 def can_dict(obj):
233 def can_dict(obj):
204 """can the *values* of a dict"""
234 """can the *values* of a dict"""
205 if isinstance(obj, dict):
235 if isinstance(obj, dict):
206 newobj = {}
236 newobj = {}
207 for k, v in obj.iteritems():
237 for k, v in obj.iteritems():
208 newobj[k] = can(v)
238 newobj[k] = can(v)
209 return newobj
239 return newobj
210 else:
240 else:
211 return obj
241 return obj
212
242
213 def can_sequence(obj):
243 def can_sequence(obj):
214 """can the elements of a sequence"""
244 """can the elements of a sequence"""
215 if isinstance(obj, (list, tuple)):
245 if isinstance(obj, (list, tuple)):
216 t = type(obj)
246 t = type(obj)
217 return t([can(i) for i in obj])
247 return t([can(i) for i in obj])
218 else:
248 else:
219 return obj
249 return obj
220
250
221 def uncan(obj, g=None):
251 def uncan(obj, g=None):
222 """invert canning"""
252 """invert canning"""
223
253
224 import_needed = False
254 import_needed = False
225 for cls,uncanner in uncan_map.iteritems():
255 for cls,uncanner in uncan_map.iteritems():
226 if isinstance(cls, basestring):
256 if isinstance(cls, basestring):
227 import_needed = True
257 import_needed = True
228 break
258 break
229 elif isinstance(obj, cls):
259 elif isinstance(obj, cls):
230 return uncanner(obj, g)
260 return uncanner(obj, g)
231
261
232 if import_needed:
262 if import_needed:
233 # perform uncan_map imports, then try again
263 # perform uncan_map imports, then try again
234 # this will usually only happen once
264 # this will usually only happen once
235 _import_mapping(uncan_map, _original_uncan_map)
265 _import_mapping(uncan_map, _original_uncan_map)
236 return uncan(obj, g)
266 return uncan(obj, g)
237
267
238 return obj
268 return obj
239
269
240 def uncan_dict(obj, g=None):
270 def uncan_dict(obj, g=None):
241 if isinstance(obj, dict):
271 if isinstance(obj, dict):
242 newobj = {}
272 newobj = {}
243 for k, v in obj.iteritems():
273 for k, v in obj.iteritems():
244 newobj[k] = uncan(v,g)
274 newobj[k] = uncan(v,g)
245 return newobj
275 return newobj
246 else:
276 else:
247 return obj
277 return obj
248
278
249 def uncan_sequence(obj, g=None):
279 def uncan_sequence(obj, g=None):
250 if isinstance(obj, (list, tuple)):
280 if isinstance(obj, (list, tuple)):
251 t = type(obj)
281 t = type(obj)
252 return t([uncan(i,g) for i in obj])
282 return t([uncan(i,g) for i in obj])
253 else:
283 else:
254 return obj
284 return obj
255
285
256
286
257 #-------------------------------------------------------------------------------
287 #-------------------------------------------------------------------------------
258 # API dictionaries
288 # API dictionaries
259 #-------------------------------------------------------------------------------
289 #-------------------------------------------------------------------------------
260
290
261 # These dicts can be extended for custom serialization of new objects
291 # These dicts can be extended for custom serialization of new objects
262
292
263 can_map = {
293 can_map = {
264 'IPython.parallel.dependent' : lambda obj: CannedObject(obj, keys=('f','df')),
294 'IPython.parallel.dependent' : lambda obj: CannedObject(obj, keys=('f','df')),
265 'numpy.ndarray' : CannedArray,
295 'numpy.ndarray' : CannedArray,
266 FunctionType : CannedFunction,
296 FunctionType : CannedFunction,
267 bytes : CannedBytes,
297 bytes : CannedBytes,
268 buffer : CannedBuffer,
298 buffer : CannedBuffer,
299 type : can_class,
300 ClassType : can_class,
269 }
301 }
270
302
271 uncan_map = {
303 uncan_map = {
272 CannedObject : lambda obj, g: obj.get_object(g),
304 CannedObject : lambda obj, g: obj.get_object(g),
273 }
305 }
274
306
275 # for use in _import_mapping:
307 # for use in _import_mapping:
276 _original_can_map = can_map.copy()
308 _original_can_map = can_map.copy()
277 _original_uncan_map = uncan_map.copy()
309 _original_uncan_map = uncan_map.copy()
General Comments 0
You need to be logged in to leave comments. Login now