better-dsh 0.3.0 → 0.3.1-a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +49 -177
  2. package/cordis.patch.yml +5 -36
  3. package/docs/20_specs/kernel-provisioning/spec.md +2 -0
  4. package/docs/adr/0015-independent-bundles-agent-remote.md +40 -0
  5. package/docs/remote-tools.md +56 -0
  6. package/docs/specs/kernel-provisioning/spec.md +12 -40
  7. package/docs/specs/omp-python-repl-port/runtime-guide.md +35 -0
  8. package/docs/specs/omp-python-repl-port/spec.md +68 -0
  9. package/docs/specs/remote-tool-enhancement/spec.md +203 -0
  10. package/dsh-docs/baseline.md +13 -0
  11. package/dsh-docs/subsystems/invariants.md +3 -0
  12. package/dsh-docs/subsystems/invariants.zh.md +3 -0
  13. package/eval-description.md +7 -3
  14. package/lib/LaunchOptions-0Mr6meJc.js +4 -0
  15. package/lib/{LaunchOptions-kGXcZOe0.js → LaunchOptions-iE9M-STS.js} +1 -1
  16. package/lib/{bidi-BalpKaJc.js → bidi-CaQeBZHY.js} +1 -1
  17. package/lib/client/index.js +30 -6
  18. package/lib/failover/index.js +1 -1
  19. package/lib/fs-aware/sandbox-plugin.js +3 -3
  20. package/lib/{index-browser-CysrjMyM.js → index-browser-CJzxT9Vi.js} +4 -4
  21. package/lib/index.d.ts +16 -50
  22. package/lib/index.js +482 -1720
  23. package/lib/kernel-env-x_WkG6Fr.js +76 -0
  24. package/lib/kernel-env.d.ts +5 -69
  25. package/lib/kernel-env.js +2 -2
  26. package/lib/{lsp-server-registry-BXvUTBY7.js → lsp-server-registry-BcdkHROD.js} +1 -1
  27. package/lib/{lsp-server-registry-CH9WNFFt.js → lsp-server-registry-D4VLs1Sm.js} +1 -1
  28. package/lib/{main-BQeFrBM1.js → main-taSMz1KX.js} +1 -1
  29. package/lib/{main-CupOhMp5.js → main-uqGqYYnA.js} +7 -5
  30. package/lib/mobile/plugin.d.ts +1 -1
  31. package/lib/omp-python/DILL-LICENSE +35 -0
  32. package/lib/omp-python/OMP-LICENSE +23 -0
  33. package/lib/omp-python/better_snapshot.py +391 -0
  34. package/lib/omp-python/better_support.py +425 -0
  35. package/lib/omp-python/dill-provenance.json +55 -0
  36. package/lib/omp-python/provenance.json +13 -0
  37. package/lib/omp-python/runner.py +2499 -0
  38. package/lib/omp-python/vendor/dill/__diff.py +234 -0
  39. package/lib/omp-python/vendor/dill/__info__.py +291 -0
  40. package/lib/omp-python/vendor/dill/__init__.py +119 -0
  41. package/lib/omp-python/vendor/dill/_dill.py +2265 -0
  42. package/lib/omp-python/vendor/dill/_objects.py +545 -0
  43. package/lib/omp-python/vendor/dill/_shims.py +193 -0
  44. package/lib/omp-python/vendor/dill/detect.py +287 -0
  45. package/lib/omp-python/vendor/dill/logger.py +285 -0
  46. package/lib/omp-python/vendor/dill/objtypes.py +24 -0
  47. package/lib/omp-python/vendor/dill/pointers.py +122 -0
  48. package/lib/omp-python/vendor/dill/session.py +612 -0
  49. package/lib/omp-python/vendor/dill/settings.py +25 -0
  50. package/lib/omp-python/vendor/dill/source.py +1025 -0
  51. package/lib/omp-python/vendor/dill/temp.py +252 -0
  52. package/lib/{puppeteer-core-CSk2PtSm.js → puppeteer-core-C-4NyOpY.js} +8 -8
  53. package/lib/remote/plugin.d.ts +459 -5
  54. package/lib/remote/plugin.js +2389 -210
  55. package/lib/url-schemes/index.d.ts +11 -11
  56. package/lib/url-schemes/index.js +11 -11
  57. package/lib/web-password.d.ts +8 -8
  58. package/lib/web-password.js +5 -1
  59. package/lib/{web-trust-Dej_RI2B.d.ts → web-trust-C_Y6o8Z2.d.ts} +11 -11
  60. package/lib/web-trust.d.ts +1 -2
  61. package/lib/welcome-notice.d.ts +5 -7
  62. package/lib/welcome-notice.js +9 -48
  63. package/lib/{wrap-T4I0nfPM.js → wrap-0VA0uFx8.js} +1 -1
  64. package/package.json +7 -24
  65. package/scripts/kernel-check.mjs +5 -0
  66. package/lib/LaunchOptions-B874eBu2.js +0 -4
  67. package/lib/bundle-delivery.d.ts +0 -20
  68. package/lib/bundle-delivery.js +0 -209
  69. package/lib/hmr.d.ts +0 -32
  70. package/lib/hmr.js +0 -49
  71. package/lib/kernel-bridge.py +0 -172
  72. package/lib/kernel-env-D8UOWeji.js +0 -195
  73. package/lib/webserver.d.ts +0 -10
  74. package/lib/webserver.js +0 -19
  75. package/scripts/kernel-provision.mjs +0 -35
  76. package/scripts/register-bundle.mjs +0 -80
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env python
2
+ #
3
+ # Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
4
+ # Author: Anirudh Vegesana (avegesan@cs.stanford.edu)
5
+ # Copyright (c) 2021-2026 The Uncertainty Quantification Foundation.
6
+ # License: 3-clause BSD. The full license text is available at:
7
+ # - https://github.com/uqfoundation/dill/blob/master/LICENSE
8
+ """
9
+ Provides shims for compatibility between versions of dill and Python.
10
+
11
+ Compatibility shims should be provided in this file. Here are two simple example
12
+ use cases.
13
+
14
+ Deprecation of constructor function:
15
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16
+ Assume that we were transitioning _import_module in _dill.py to
17
+ the builtin function importlib.import_module when present.
18
+
19
+ @move_to(_dill)
20
+ def _import_module(import_name):
21
+ ... # code already in _dill.py
22
+
23
+ _import_module = Getattr(importlib, 'import_module', Getattr(_dill, '_import_module', None))
24
+
25
+ The code will attempt to find import_module in the importlib module. If not
26
+ present, it will use the _import_module function in _dill.
27
+
28
+ Emulate new Python behavior in older Python versions:
29
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30
+ CellType.cell_contents behaves differently in Python 3.6 and 3.7. It is
31
+ read-only in Python 3.6 and writable and deletable in 3.7.
32
+
33
+ if _dill.OLD37 and _dill.HAS_CTYPES and ...:
34
+ @move_to(_dill)
35
+ def _setattr(object, name, value):
36
+ if type(object) is _dill.CellType and name == 'cell_contents':
37
+ _PyCell_Set.argtypes = (ctypes.py_object, ctypes.py_object)
38
+ _PyCell_Set(object, value)
39
+ else:
40
+ setattr(object, name, value)
41
+ ... # more cases below
42
+
43
+ _setattr = Getattr(_dill, '_setattr', setattr)
44
+
45
+ _dill._setattr will be used when present to emulate Python 3.7 functionality in
46
+ older versions of Python while defaulting to the standard setattr in 3.7+.
47
+
48
+ See this PR for the discussion that lead to this system:
49
+ https://github.com/uqfoundation/dill/pull/443
50
+ """
51
+
52
+ import inspect
53
+ import sys
54
+
55
+ _dill = sys.modules['dill._dill']
56
+
57
+
58
+ class Reduce(object):
59
+ """
60
+ Reduce objects are wrappers used for compatibility enforcement during
61
+ unpickle-time. They should only be used in calls to pickler.save and
62
+ other Reduce objects. They are only evaluated within unpickler.load.
63
+
64
+ Pickling a Reduce object makes the two implementations equivalent:
65
+
66
+ pickler.save(Reduce(*reduction))
67
+
68
+ pickler.save_reduce(*reduction, obj=reduction)
69
+ """
70
+ __slots__ = ['reduction']
71
+ def __new__(cls, *reduction, **kwargs):
72
+ """
73
+ Args:
74
+ *reduction: a tuple that matches the format given here:
75
+ https://docs.python.org/3/library/pickle.html#object.__reduce__
76
+ is_callable: a bool to indicate that the object created by
77
+ unpickling `reduction` is callable. If true, the current Reduce
78
+ is allowed to be used as the function in further save_reduce calls
79
+ or Reduce objects.
80
+ """
81
+ is_callable = kwargs.get('is_callable', False) # Pleases Py2. Can be removed later
82
+ if is_callable:
83
+ self = object.__new__(_CallableReduce)
84
+ else:
85
+ self = object.__new__(Reduce)
86
+ self.reduction = reduction
87
+ return self
88
+ def __repr__(self):
89
+ return 'Reduce%s' % (self.reduction,)
90
+ def __copy__(self):
91
+ return self # pragma: no cover
92
+ def __deepcopy__(self, memo):
93
+ return self # pragma: no cover
94
+ def __reduce__(self):
95
+ return self.reduction
96
+ def __reduce_ex__(self, protocol):
97
+ return self.__reduce__()
98
+
99
+ class _CallableReduce(Reduce):
100
+ # A version of Reduce for functions. Used to trick pickler.save_reduce into
101
+ # thinking that Reduce objects of functions are themselves meaningful functions.
102
+ def __call__(self, *args, **kwargs):
103
+ reduction = self.__reduce__()
104
+ func = reduction[0]
105
+ f_args = reduction[1]
106
+ obj = func(*f_args)
107
+ return obj(*args, **kwargs)
108
+
109
+ __NO_DEFAULT = _dill.Sentinel('Getattr.NO_DEFAULT')
110
+
111
+ def Getattr(object, name, default=__NO_DEFAULT):
112
+ """
113
+ A Reduce object that represents the getattr operation. When unpickled, the
114
+ Getattr will access an attribute 'name' of 'object' and return the value
115
+ stored there. If the attribute doesn't exist, the default value will be
116
+ returned if present.
117
+
118
+ The following statements are equivalent:
119
+
120
+ Getattr(collections, 'OrderedDict')
121
+ Getattr(collections, 'spam', None)
122
+ Getattr(*args)
123
+
124
+ Reduce(getattr, (collections, 'OrderedDict'))
125
+ Reduce(getattr, (collections, 'spam', None))
126
+ Reduce(getattr, args)
127
+
128
+ During unpickling, the first two will result in collections.OrderedDict and
129
+ None respectively because the first attribute exists and the second one does
130
+ not, forcing it to use the default value given in the third argument.
131
+ """
132
+
133
+ if default is Getattr.NO_DEFAULT:
134
+ reduction = (getattr, (object, name))
135
+ else:
136
+ reduction = (getattr, (object, name, default))
137
+
138
+ return Reduce(*reduction, is_callable=callable(default))
139
+
140
+ Getattr.NO_DEFAULT = __NO_DEFAULT
141
+ del __NO_DEFAULT
142
+
143
+ def move_to(module, name=None):
144
+ def decorator(func):
145
+ if name is None:
146
+ fname = func.__name__
147
+ else:
148
+ fname = name
149
+ module.__dict__[fname] = func
150
+ func.__module__ = module.__name__
151
+ return func
152
+ return decorator
153
+
154
+ def register_shim(name, default):
155
+ """
156
+ A easier to understand and more compact way of "softly" defining a function.
157
+ These two pieces of code are equivalent:
158
+
159
+ if _dill.OLD3X:
160
+ def _create_class():
161
+ ...
162
+ _create_class = register_shim('_create_class', types.new_class)
163
+
164
+ if _dill.OLD3X:
165
+ @move_to(_dill)
166
+ def _create_class():
167
+ ...
168
+ _create_class = Getattr(_dill, '_create_class', types.new_class)
169
+
170
+ Intuitively, it creates a function or object in the versions of dill/python
171
+ that require special reimplementations, and use a core library or default
172
+ implementation if that function or object does not exist.
173
+ """
174
+ func = globals().get(name)
175
+ if func is not None:
176
+ _dill.__dict__[name] = func
177
+ func.__module__ = _dill.__name__
178
+
179
+ if default is Getattr.NO_DEFAULT:
180
+ reduction = (getattr, (_dill, name))
181
+ else:
182
+ reduction = (getattr, (_dill, name, default))
183
+
184
+ return Reduce(*reduction, is_callable=callable(default))
185
+
186
+ ######################
187
+ ## Compatibility Shims are defined below
188
+ ######################
189
+
190
+ _CELL_EMPTY = register_shim('_CELL_EMPTY', None)
191
+
192
+ _setattr = register_shim('_setattr', setattr)
193
+ _delattr = register_shim('_delattr', delattr)
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env python
2
+ #
3
+ # Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
4
+ # Copyright (c) 2008-2016 California Institute of Technology.
5
+ # Copyright (c) 2016-2026 The Uncertainty Quantification Foundation.
6
+ # License: 3-clause BSD. The full license text is available at:
7
+ # - https://github.com/uqfoundation/dill/blob/master/LICENSE
8
+ """
9
+ Methods for detecting objects leading to pickling failures.
10
+ """
11
+
12
+ import dis
13
+ from inspect import ismethod, isfunction, istraceback, isframe, iscode
14
+
15
+ from .pointers import parent, reference, at, parents, children
16
+ from .logger import trace
17
+
18
+ __all__ = ['baditems','badobjects','badtypes','code','errors','freevars',
19
+ 'getmodule','globalvars','nestedcode','nestedglobals','outermost',
20
+ 'referredglobals','referrednested','trace','varnames']
21
+
22
+ def getmodule(object, _filename=None, force=False):
23
+ """get the module of the object"""
24
+ from inspect import getmodule as getmod
25
+ module = getmod(object, _filename)
26
+ if module or not force: return module
27
+ import builtins
28
+ from .source import getname
29
+ name = getname(object, force=True)
30
+ return builtins if name in vars(builtins).keys() else None
31
+
32
+ def outermost(func): # is analogous to getsource(func,enclosing=True)
33
+ """get outermost enclosing object (i.e. the outer function in a closure)
34
+
35
+ NOTE: this is the object-equivalent of getsource(func, enclosing=True)
36
+ """
37
+ if ismethod(func):
38
+ _globals = func.__func__.__globals__ or {}
39
+ elif isfunction(func):
40
+ _globals = func.__globals__ or {}
41
+ else:
42
+ return #XXX: or raise? no matches
43
+ _globals = _globals.items()
44
+ # get the enclosing source
45
+ from .source import getsourcelines
46
+ try: lines,lnum = getsourcelines(func, enclosing=True)
47
+ except Exception: #TypeError, IOError
48
+ lines,lnum = [],None
49
+ code = ''.join(lines)
50
+ # get all possible names,objects that are named in the enclosing source
51
+ _locals = ((name,obj) for (name,obj) in _globals if name in code)
52
+ # now only save the objects that generate the enclosing block
53
+ for name,obj in _locals: #XXX: don't really need 'name'
54
+ try:
55
+ if getsourcelines(obj) == (lines,lnum): return obj
56
+ except Exception: #TypeError, IOError
57
+ pass
58
+ return #XXX: or raise? no matches
59
+
60
+ def nestedcode(func, recurse=True): #XXX: or return dict of {co_name: co} ?
61
+ """get the code objects for any nested functions (e.g. in a closure)"""
62
+ func = code(func)
63
+ if not iscode(func): return [] #XXX: or raise? no matches
64
+ nested = set()
65
+ for co in func.co_consts:
66
+ if co is None: continue
67
+ co = code(co)
68
+ if co:
69
+ nested.add(co)
70
+ if recurse: nested |= set(nestedcode(co, recurse=True))
71
+ return list(nested)
72
+
73
+ def code(func):
74
+ """get the code object for the given function or method
75
+
76
+ NOTE: use dill.source.getsource(CODEOBJ) to get the source code
77
+ """
78
+ if ismethod(func): func = func.__func__
79
+ if isfunction(func): func = func.__code__
80
+ if istraceback(func): func = func.tb_frame
81
+ if isframe(func): func = func.f_code
82
+ if iscode(func): return func
83
+ return
84
+
85
+ #XXX: ugly: parse dis.dis for name after "<code object" in line and in globals?
86
+ def referrednested(func, recurse=True): #XXX: return dict of {__name__: obj} ?
87
+ """get functions defined inside of func (e.g. inner functions in a closure)
88
+
89
+ NOTE: results may differ if the function has been executed or not.
90
+ If len(nestedcode(func)) > len(referrednested(func)), try calling func().
91
+ If possible, python builds code objects, but delays building functions
92
+ until func() is called.
93
+ """
94
+ import gc
95
+ funcs = set()
96
+ # get the code objects, and try to track down by referrence
97
+ for co in nestedcode(func, recurse):
98
+ # look for function objects that refer to the code object
99
+ for obj in gc.get_referrers(co):
100
+ # get methods
101
+ _ = getattr(obj, '__func__', None) # ismethod
102
+ if getattr(_, '__code__', None) is co: funcs.add(obj)
103
+ # get functions
104
+ elif getattr(obj, '__code__', None) is co: funcs.add(obj)
105
+ # get frame objects
106
+ elif getattr(obj, 'f_code', None) is co: funcs.add(obj)
107
+ # get code objects
108
+ elif hasattr(obj, 'co_code') and obj is co: funcs.add(obj)
109
+ # frameobjs => func.__code__.co_varnames not in func.__code__.co_cellvars
110
+ # funcobjs => func.__code__.co_cellvars not in func.__code__.co_varnames
111
+ # frameobjs are not found, however funcobjs are...
112
+ # (see: test_mixins.quad ... and test_mixins.wtf)
113
+ # after execution, code objects get compiled, and then may be found by gc
114
+ return list(funcs)
115
+
116
+
117
+ def freevars(func):
118
+ """get objects defined in enclosing code that are referred to by func
119
+
120
+ returns a dict of {name:object}"""
121
+ if ismethod(func): func = func.__func__
122
+ if isfunction(func):
123
+ closures = func.__closure__ or ()
124
+ func = func.__code__.co_freevars # get freevars
125
+ else:
126
+ return {}
127
+
128
+ def get_cell_contents():
129
+ for name, c in zip(func, closures):
130
+ try:
131
+ cell_contents = c.cell_contents
132
+ except ValueError: # cell is empty
133
+ continue
134
+ yield name, c.cell_contents
135
+
136
+ return dict(get_cell_contents())
137
+
138
+ # thanks to Davies Liu for recursion of globals
139
+ def nestedglobals(func, recurse=True):
140
+ """get the names of any globals found within func"""
141
+ func = code(func)
142
+ if func is None: return list()
143
+ import sys
144
+ from .temp import capture
145
+ CAN_NULL = sys.hexversion >= 0x30b00a7 # NULL may be prepended >= 3.11a7
146
+ names = set()
147
+ with capture('stdout') as out:
148
+ try:
149
+ dis.dis(func) #XXX: dis.dis(None) disassembles last traceback
150
+ except IndexError:
151
+ pass #FIXME: HACK for IS_PYPY (3.11)
152
+ for line in out.getvalue().splitlines():
153
+ if '_GLOBAL' in line:
154
+ name = line.split('(')[-1].split(')')[0]
155
+ if CAN_NULL:
156
+ names.add(name.replace('NULL + ', '').replace(' + NULL', ''))
157
+ else:
158
+ names.add(name)
159
+ for co in getattr(func, 'co_consts', tuple()):
160
+ if co and recurse and iscode(co):
161
+ names.update(nestedglobals(co, recurse=True))
162
+ return list(names)
163
+
164
+ def referredglobals(func, recurse=True, builtin=False):
165
+ """get the names of objects in the global scope referred to by func"""
166
+ return globalvars(func, recurse, builtin).keys()
167
+
168
+ def globalvars(func, recurse=True, builtin=False):
169
+ """get objects defined in global scope that are referred to by func
170
+
171
+ return a dict of {name:object}"""
172
+ if ismethod(func): func = func.__func__
173
+ if isfunction(func):
174
+ globs = vars(getmodule(sum)).copy() if builtin else {}
175
+ # get references from within closure
176
+ orig_func, func = func, set()
177
+ for obj in orig_func.__closure__ or {}:
178
+ try:
179
+ cell_contents = obj.cell_contents
180
+ except ValueError: # cell is empty
181
+ pass
182
+ else:
183
+ _vars = globalvars(cell_contents, recurse, builtin) or {}
184
+ func.update(_vars) #XXX: (above) be wary of infinte recursion?
185
+ globs.update(_vars)
186
+ # get globals
187
+ globs.update(orig_func.__globals__ or {})
188
+ # get names of references
189
+ if not recurse:
190
+ func.update(orig_func.__code__.co_names)
191
+ else:
192
+ func.update(nestedglobals(orig_func.__code__))
193
+ # find globals for all entries of func
194
+ for key in func.copy(): #XXX: unnecessary...?
195
+ nested_func = globs.get(key)
196
+ if nested_func is orig_func:
197
+ #func.remove(key) if key in func else None
198
+ continue #XXX: globalvars(func, False)?
199
+ func.update(globalvars(nested_func, True, builtin))
200
+ elif iscode(func):
201
+ globs = vars(getmodule(sum)).copy() if builtin else {}
202
+ #globs.update(globals())
203
+ if not recurse:
204
+ func = func.co_names # get names
205
+ else:
206
+ orig_func = func.co_name # to stop infinite recursion
207
+ func = set(nestedglobals(func))
208
+ # find globals for all entries of func
209
+ for key in func.copy(): #XXX: unnecessary...?
210
+ if key is orig_func:
211
+ #func.remove(key) if key in func else None
212
+ continue #XXX: globalvars(func, False)?
213
+ nested_func = globs.get(key)
214
+ func.update(globalvars(nested_func, True, builtin))
215
+ else:
216
+ return {}
217
+ #NOTE: if name not in __globals__, then we skip it...
218
+ return dict((name,globs[name]) for name in func if name in globs)
219
+
220
+
221
+ def varnames(func):
222
+ """get names of variables defined by func
223
+
224
+ returns a tuple (local vars, local vars referrenced by nested functions)"""
225
+ func = code(func)
226
+ if not iscode(func):
227
+ return () #XXX: better ((),())? or None?
228
+ return func.co_varnames, func.co_cellvars
229
+
230
+
231
+ def baditems(obj, exact=False, safe=False): #XXX: obj=globals() ?
232
+ """get items in object that fail to pickle"""
233
+ if not hasattr(obj,'__iter__'): # is not iterable
234
+ return [j for j in (badobjects(obj,0,exact,safe),) if j is not None]
235
+ obj = obj.values() if getattr(obj,'values',None) else obj
236
+ _obj = [] # can't use a set, as items may be unhashable
237
+ [_obj.append(badobjects(i,0,exact,safe)) for i in obj if i not in _obj]
238
+ return [j for j in _obj if j is not None]
239
+
240
+
241
+ def badobjects(obj, depth=0, exact=False, safe=False):
242
+ """get objects that fail to pickle"""
243
+ from dill import pickles
244
+ if not depth:
245
+ if pickles(obj,exact,safe): return None
246
+ return obj
247
+ return dict(((attr, badobjects(getattr(obj,attr),depth-1,exact,safe)) \
248
+ for attr in dir(obj) if not pickles(getattr(obj,attr),exact,safe)))
249
+
250
+ def badtypes(obj, depth=0, exact=False, safe=False):
251
+ """get types for objects that fail to pickle"""
252
+ from dill import pickles
253
+ if not depth:
254
+ if pickles(obj,exact,safe): return None
255
+ return type(obj)
256
+ return dict(((attr, badtypes(getattr(obj,attr),depth-1,exact,safe)) \
257
+ for attr in dir(obj) if not pickles(getattr(obj,attr),exact,safe)))
258
+
259
+ def errors(obj, depth=0, exact=False, safe=False):
260
+ """get errors for objects that fail to pickle"""
261
+ from dill import pickles, copy
262
+ if not depth:
263
+ try:
264
+ pik = copy(obj)
265
+ if exact:
266
+ assert pik == obj, \
267
+ "Unpickling produces %s instead of %s" % (pik,obj)
268
+ assert type(pik) == type(obj), \
269
+ "Unpickling produces %s instead of %s" % (type(pik),type(obj))
270
+ return None
271
+ except Exception:
272
+ import sys
273
+ return sys.exc_info()[1]
274
+ _dict = {}
275
+ for attr in dir(obj):
276
+ try:
277
+ _attr = getattr(obj,attr)
278
+ except Exception:
279
+ import sys
280
+ _dict[attr] = sys.exc_info()[1]
281
+ continue
282
+ if not pickles(_attr,exact,safe):
283
+ _dict[attr] = errors(_attr,depth-1,exact,safe)
284
+ return _dict
285
+
286
+
287
+ # EOF