##// END OF EJS Templates
adjust how canning deals with import strings...
MinRK -
Show More
@@ -1,244 +1,281 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
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 try:
101 try:
102 __import__(self.module)
102 __import__(self.module)
103 except ImportError:
103 except ImportError:
104 pass
104 pass
105 else:
105 else:
106 g = sys.modules[self.module].__dict__
106 g = sys.modules[self.module].__dict__
107
107
108 if g is None:
108 if g is None:
109 g = {}
109 g = {}
110 if self.defaults:
110 if self.defaults:
111 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
111 defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
112 else:
112 else:
113 defaults = None
113 defaults = None
114 newFunc = FunctionType(self.code, g, self.__name__, defaults)
114 newFunc = FunctionType(self.code, g, self.__name__, defaults)
115 return newFunc
115 return newFunc
116
116
117
117
118 class CannedArray(CannedObject):
118 class CannedArray(CannedObject):
119 def __init__(self, obj):
119 def __init__(self, obj):
120 self.shape = obj.shape
120 self.shape = obj.shape
121 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
121 self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str
122 if sum(obj.shape) == 0:
122 if sum(obj.shape) == 0:
123 # just pickle it
123 # just pickle it
124 self.buffers = [pickle.dumps(obj, -1)]
124 self.buffers = [pickle.dumps(obj, -1)]
125 else:
125 else:
126 # ensure contiguous
126 # ensure contiguous
127 obj = numpy.ascontiguousarray(obj, dtype=None)
127 obj = numpy.ascontiguousarray(obj, dtype=None)
128 self.buffers = [buffer(obj)]
128 self.buffers = [buffer(obj)]
129
129
130 def get_object(self, g=None):
130 def get_object(self, g=None):
131 data = self.buffers[0]
131 data = self.buffers[0]
132 if sum(self.shape) == 0:
132 if sum(self.shape) == 0:
133 # no shape, we just pickled it
133 # no shape, we just pickled it
134 return pickle.loads(data)
134 return pickle.loads(data)
135 else:
135 else:
136 return numpy.frombuffer(data, dtype=self.dtype).reshape(self.shape)
136 return numpy.frombuffer(data, dtype=self.dtype).reshape(self.shape)
137
137
138
138
139 class CannedBytes(CannedObject):
139 class CannedBytes(CannedObject):
140 wrap = bytes
140 wrap = bytes
141 def __init__(self, obj):
141 def __init__(self, obj):
142 self.buffers = [obj]
142 self.buffers = [obj]
143
143
144 def get_object(self, g=None):
144 def get_object(self, g=None):
145 data = self.buffers[0]
145 data = self.buffers[0]
146 return self.wrap(data)
146 return self.wrap(data)
147
147
148 def CannedBuffer(CannedBytes):
148 def CannedBuffer(CannedBytes):
149 wrap = buffer
149 wrap = buffer
150
150
151 #-------------------------------------------------------------------------------
151 #-------------------------------------------------------------------------------
152 # Functions
152 # Functions
153 #-------------------------------------------------------------------------------
153 #-------------------------------------------------------------------------------
154
154
155 def _error(*args, **kwargs):
155 def _logger():
156 """get the logger for the current Application
157
158 the root logger will be used if no Application is running
159 """
156 if Application.initialized():
160 if Application.initialized():
157 logger = Application.instance().log
161 logger = Application.instance().log
158 else:
162 else:
159 logger = logging.getLogger()
163 logger = logging.getLogger()
160 if not logger.handlers:
164 if not logger.handlers:
161 logging.basicConfig()
165 logging.basicConfig()
162 logger.error(*args, **kwargs)
166
167 return logger
168
169 def _import_mapping(mapping, original=None):
170 """import any string-keys in a type mapping
171
172 """
173 log = _logger()
174 log.debug("Importing canning map")
175 for key,value in mapping.items():
176 if isinstance(key, basestring):
177 try:
178 cls = import_item(key)
179 except Exception:
180 if original and key not in original:
181 # only message on user-added classes
182 log.error("cannning class not importable: %r", key, exc_info=True)
183 mapping.pop(key)
184 else:
185 mapping[cls] = mapping.pop(key)
163
186
164 def can(obj):
187 def can(obj):
165 """prepare an object for pickling"""
188 """prepare an object for pickling"""
189
190 import_needed = False
191
166 for cls,canner in can_map.iteritems():
192 for cls,canner in can_map.iteritems():
167 if isinstance(cls, basestring):
193 if isinstance(cls, basestring):
168 try:
194 import_needed = True
169 cls = import_item(cls)
195 break
170 except Exception:
196 elif isinstance(obj, cls):
171 _error("cannning class not importable: %r", cls, exc_info=True)
172 cls = None
173 continue
174 if isinstance(obj, cls):
175 return canner(obj)
197 return canner(obj)
198
199 if import_needed:
200 # perform can_map imports, then try again
201 # this will usually only happen once
202 _import_mapping(can_map, _original_can_map)
203 return can(obj)
204
176 return obj
205 return obj
177
206
178 def can_dict(obj):
207 def can_dict(obj):
179 """can the *values* of a dict"""
208 """can the *values* of a dict"""
180 if isinstance(obj, dict):
209 if isinstance(obj, dict):
181 newobj = {}
210 newobj = {}
182 for k, v in obj.iteritems():
211 for k, v in obj.iteritems():
183 newobj[k] = can(v)
212 newobj[k] = can(v)
184 return newobj
213 return newobj
185 else:
214 else:
186 return obj
215 return obj
187
216
188 def can_sequence(obj):
217 def can_sequence(obj):
189 """can the elements of a sequence"""
218 """can the elements of a sequence"""
190 if isinstance(obj, (list, tuple)):
219 if isinstance(obj, (list, tuple)):
191 t = type(obj)
220 t = type(obj)
192 return t([can(i) for i in obj])
221 return t([can(i) for i in obj])
193 else:
222 else:
194 return obj
223 return obj
195
224
196 def uncan(obj, g=None):
225 def uncan(obj, g=None):
197 """invert canning"""
226 """invert canning"""
227
228 import_needed = False
198 for cls,uncanner in uncan_map.iteritems():
229 for cls,uncanner in uncan_map.iteritems():
199 if isinstance(cls, basestring):
230 if isinstance(cls, basestring):
200 try:
231 import_needed = True
201 cls = import_item(cls)
232 break
202 except Exception:
233 elif isinstance(obj, cls):
203 _error("uncanning class not importable: %r", cls, exc_info=True)
204 cls = None
205 continue
206 if isinstance(obj, cls):
207 return uncanner(obj, g)
234 return uncanner(obj, g)
235
236 if import_needed:
237 # perform uncan_map imports, then try again
238 # this will usually only happen once
239 _import_mapping(uncan_map, _original_uncan_map)
240 return uncan(obj, g)
241
208 return obj
242 return obj
209
243
210 def uncan_dict(obj, g=None):
244 def uncan_dict(obj, g=None):
211 if isinstance(obj, dict):
245 if isinstance(obj, dict):
212 newobj = {}
246 newobj = {}
213 for k, v in obj.iteritems():
247 for k, v in obj.iteritems():
214 newobj[k] = uncan(v,g)
248 newobj[k] = uncan(v,g)
215 return newobj
249 return newobj
216 else:
250 else:
217 return obj
251 return obj
218
252
219 def uncan_sequence(obj, g=None):
253 def uncan_sequence(obj, g=None):
220 if isinstance(obj, (list, tuple)):
254 if isinstance(obj, (list, tuple)):
221 t = type(obj)
255 t = type(obj)
222 return t([uncan(i,g) for i in obj])
256 return t([uncan(i,g) for i in obj])
223 else:
257 else:
224 return obj
258 return obj
225
259
226
260
227 #-------------------------------------------------------------------------------
261 #-------------------------------------------------------------------------------
228 # API dictionary
262 # API dictionaries
229 #-------------------------------------------------------------------------------
263 #-------------------------------------------------------------------------------
230
264
231 # These dicts can be extended for custom serialization of new objects
265 # These dicts can be extended for custom serialization of new objects
232
266
233 can_map = {
267 can_map = {
234 'IPython.parallel.dependent' : lambda obj: CannedObject(obj, keys=('f','df')),
268 'IPython.parallel.dependent' : lambda obj: CannedObject(obj, keys=('f','df')),
235 'numpy.ndarray' : CannedArray,
269 'numpy.ndarray' : CannedArray,
236 FunctionType : CannedFunction,
270 FunctionType : CannedFunction,
237 bytes : CannedBytes,
271 bytes : CannedBytes,
238 buffer : CannedBuffer,
272 buffer : CannedBuffer,
239 }
273 }
240
274
241 uncan_map = {
275 uncan_map = {
242 CannedObject : lambda obj, g: obj.get_object(g),
276 CannedObject : lambda obj, g: obj.get_object(g),
243 }
277 }
244
278
279 # for use in _import_mapping:
280 _original_can_map = can_map.copy()
281 _original_uncan_map = uncan_map.copy()
General Comments 0
You need to be logged in to leave comments. Login now