better-dsh 0.3.0 → 0.3.1
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.
- package/README.md +49 -178
- package/cordis.patch.yml +4 -35
- package/docs/adr/0015-independent-bundles-agent-remote.md +40 -0
- package/docs/remote-tools.md +56 -0
- package/docs/specs/omp-python-repl-port/runtime-guide.md +33 -0
- package/docs/specs/omp-python-repl-port/spec.md +61 -0
- package/docs/specs/remote-tool-enhancement/spec.md +203 -0
- package/dsh-docs/baseline.md +13 -0
- package/dsh-docs/subsystems/invariants.md +3 -0
- package/dsh-docs/subsystems/invariants.zh.md +3 -0
- package/eval-description.md +7 -3
- package/lib/{LaunchOptions-kGXcZOe0.js → LaunchOptions-5eg7E0ye.js} +1 -1
- package/lib/LaunchOptions-DxQQ0s-A.js +4 -0
- package/lib/{bidi-BalpKaJc.js → bidi-CwabFH07.js} +1 -1
- package/lib/client/index.js +30 -6
- package/lib/failover/index.js +1 -1
- package/lib/fs-aware/sandbox-plugin.js +3 -3
- package/lib/{index-browser-CysrjMyM.js → index-browser-B-9jm12n.js} +4 -4
- package/lib/index.d.ts +17 -41
- package/lib/index.js +473 -1678
- package/lib/kernel-env-BOF0fNHn.js +91 -0
- package/lib/kernel-env.d.ts +4 -58
- package/lib/kernel-env.js +2 -2
- package/lib/{lsp-server-registry-BXvUTBY7.js → lsp-server-registry-BcdkHROD.js} +1 -1
- package/lib/{lsp-server-registry-CH9WNFFt.js → lsp-server-registry-D4VLs1Sm.js} +1 -1
- package/lib/{main-CupOhMp5.js → main-BeCgUd20.js} +8 -6
- package/lib/{main-BQeFrBM1.js → main-Dv_UQqG2.js} +1 -1
- package/lib/mobile/plugin.d.ts +1 -1
- package/lib/omp-python/DILL-LICENSE +35 -0
- package/lib/omp-python/OMP-LICENSE +23 -0
- package/lib/omp-python/better_snapshot.py +391 -0
- package/lib/omp-python/better_support.py +425 -0
- package/lib/omp-python/dill-provenance.json +55 -0
- package/lib/omp-python/provenance.json +13 -0
- package/lib/omp-python/runner.py +2499 -0
- package/lib/omp-python/vendor/dill/__diff.py +234 -0
- package/lib/omp-python/vendor/dill/__info__.py +291 -0
- package/lib/omp-python/vendor/dill/__init__.py +119 -0
- package/lib/omp-python/vendor/dill/_dill.py +2265 -0
- package/lib/omp-python/vendor/dill/_objects.py +545 -0
- package/lib/omp-python/vendor/dill/_shims.py +193 -0
- package/lib/omp-python/vendor/dill/detect.py +287 -0
- package/lib/omp-python/vendor/dill/logger.py +285 -0
- package/lib/omp-python/vendor/dill/objtypes.py +24 -0
- package/lib/omp-python/vendor/dill/pointers.py +122 -0
- package/lib/omp-python/vendor/dill/session.py +612 -0
- package/lib/omp-python/vendor/dill/settings.py +25 -0
- package/lib/omp-python/vendor/dill/source.py +1025 -0
- package/lib/omp-python/vendor/dill/temp.py +252 -0
- package/lib/{puppeteer-core-CSk2PtSm.js → puppeteer-core-Dnd7eqel.js} +9 -9
- package/lib/remote/plugin.d.ts +459 -5
- package/lib/remote/plugin.js +2389 -210
- package/lib/url-schemes/index.d.ts +11 -11
- package/lib/url-schemes/index.js +12 -12
- package/lib/web-password.d.ts +8 -8
- package/lib/web-password.js +5 -1
- package/lib/{web-trust-Dej_RI2B.d.ts → web-trust-CwZrTBcl.d.ts} +11 -11
- package/lib/web-trust.d.ts +1 -2
- package/lib/welcome-notice.d.ts +5 -7
- package/lib/welcome-notice.js +9 -48
- package/lib/{wrap-T4I0nfPM.js → wrap-0VA0uFx8.js} +1 -1
- package/package.json +6 -23
- package/scripts/kernel-provision.mjs +4 -34
- package/lib/LaunchOptions-B874eBu2.js +0 -4
- package/lib/bundle-delivery.d.ts +0 -20
- package/lib/bundle-delivery.js +0 -209
- package/lib/hmr.d.ts +0 -32
- package/lib/hmr.js +0 -49
- package/lib/kernel-bridge.py +0 -172
- package/lib/kernel-env-D8UOWeji.js +0 -195
- package/lib/webserver.d.ts +0 -10
- package/lib/webserver.js +0 -19
- package/scripts/register-bundle.mjs +0 -80
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Author: Leonardo Gama (@leogama)
|
|
5
|
+
# Copyright (c) 2022-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
|
+
Logging utilities for dill.
|
|
10
|
+
|
|
11
|
+
The 'logger' object is dill's top-level logger.
|
|
12
|
+
|
|
13
|
+
The 'adapter' object wraps the logger and implements a 'trace()' method that
|
|
14
|
+
generates a detailed tree-style trace for the pickling call at log level INFO.
|
|
15
|
+
|
|
16
|
+
The 'trace()' function sets and resets dill's logger log level, enabling and
|
|
17
|
+
disabling the pickling trace.
|
|
18
|
+
|
|
19
|
+
The trace shows a tree structure depicting the depth of each object serialized
|
|
20
|
+
*with dill save functions*, but not the ones that use save functions from
|
|
21
|
+
'pickle._Pickler.dispatch'. If the information is available, it also displays
|
|
22
|
+
the size in bytes that the object contributed to the pickle stream (including
|
|
23
|
+
its child objects). Sample trace output:
|
|
24
|
+
|
|
25
|
+
>>> import dill, dill.tests
|
|
26
|
+
>>> dill.detect.trace(True)
|
|
27
|
+
>>> dill.dump_session(main=dill.tests)
|
|
28
|
+
┬ M1: <module 'dill.tests' from '.../dill/tests/__init__.py'>
|
|
29
|
+
├┬ F2: <function _import_module at 0x7f0d2dce1b80>
|
|
30
|
+
│└ # F2 [32 B]
|
|
31
|
+
├┬ D2: <dict object at 0x7f0d2e98a540>
|
|
32
|
+
│├┬ T4: <class '_frozen_importlib.ModuleSpec'>
|
|
33
|
+
││└ # T4 [35 B]
|
|
34
|
+
│├┬ D2: <dict object at 0x7f0d2ef0e8c0>
|
|
35
|
+
││├┬ T4: <class '_frozen_importlib_external.SourceFileLoader'>
|
|
36
|
+
│││└ # T4 [50 B]
|
|
37
|
+
││├┬ D2: <dict object at 0x7f0d2e988a40>
|
|
38
|
+
│││└ # D2 [84 B]
|
|
39
|
+
││└ # D2 [413 B]
|
|
40
|
+
│└ # D2 [763 B]
|
|
41
|
+
└ # M1 [813 B]
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
__all__ = ['adapter', 'logger', 'trace']
|
|
45
|
+
|
|
46
|
+
import codecs
|
|
47
|
+
import contextlib
|
|
48
|
+
import locale
|
|
49
|
+
import logging
|
|
50
|
+
import math
|
|
51
|
+
import os
|
|
52
|
+
from functools import partial
|
|
53
|
+
from typing import TextIO, Union
|
|
54
|
+
|
|
55
|
+
import dill
|
|
56
|
+
|
|
57
|
+
# Tree drawing characters: Unicode to ASCII map.
|
|
58
|
+
ASCII_MAP = str.maketrans({"│": "|", "├": "|", "┬": "+", "└": "`"})
|
|
59
|
+
|
|
60
|
+
## Notes about the design choices ##
|
|
61
|
+
|
|
62
|
+
# Here is some domumentation of the Standard Library's logging internals that
|
|
63
|
+
# can't be found completely in the official documentation. dill's logger is
|
|
64
|
+
# obtained by calling logging.getLogger('dill') and therefore is an instance of
|
|
65
|
+
# logging.getLoggerClass() at the call time. As this is controlled by the user,
|
|
66
|
+
# in order to add some functionality to it it's necessary to use a LoggerAdapter
|
|
67
|
+
# to wrap it, overriding some of the adapter's methods and creating new ones.
|
|
68
|
+
#
|
|
69
|
+
# Basic calling sequence
|
|
70
|
+
# ======================
|
|
71
|
+
#
|
|
72
|
+
# Python's logging functionality can be conceptually divided into five steps:
|
|
73
|
+
# 0. Check logging level -> abort if call level is greater than logger level
|
|
74
|
+
# 1. Gather information -> construct a LogRecord from passed arguments and context
|
|
75
|
+
# 2. Filter (optional) -> discard message if the record matches a filter
|
|
76
|
+
# 3. Format -> format message with args, then format output string with message plus record
|
|
77
|
+
# 4. Handle -> write the formatted string to output as defined in the handler
|
|
78
|
+
#
|
|
79
|
+
# dill.logging.logger.log -> # or logger.info, etc.
|
|
80
|
+
# Logger.log -> \
|
|
81
|
+
# Logger._log -> }- accept 'extra' parameter for custom record entries
|
|
82
|
+
# Logger.makeRecord -> /
|
|
83
|
+
# LogRecord.__init__
|
|
84
|
+
# Logger.handle ->
|
|
85
|
+
# Logger.callHandlers ->
|
|
86
|
+
# Handler.handle ->
|
|
87
|
+
# Filterer.filter ->
|
|
88
|
+
# Filter.filter
|
|
89
|
+
# StreamHandler.emit ->
|
|
90
|
+
# Handler.format ->
|
|
91
|
+
# Formatter.format ->
|
|
92
|
+
# LogRecord.getMessage # does: record.message = msg % args
|
|
93
|
+
# Formatter.formatMessage ->
|
|
94
|
+
# PercentStyle.format # does: self._fmt % vars(record)
|
|
95
|
+
#
|
|
96
|
+
# NOTE: All methods from the second line on are from logging.__init__.py
|
|
97
|
+
|
|
98
|
+
class TraceAdapter(logging.LoggerAdapter):
|
|
99
|
+
"""
|
|
100
|
+
Tracks object tree depth and calculates pickled object size.
|
|
101
|
+
|
|
102
|
+
A single instance of this wraps the module's logger, as the logging API
|
|
103
|
+
doesn't allow setting it directly with a custom Logger subclass. The added
|
|
104
|
+
'trace()' method receives a pickle instance as the first argument and
|
|
105
|
+
creates extra values to be added in the LogRecord from it, then calls
|
|
106
|
+
'info()'.
|
|
107
|
+
|
|
108
|
+
Usage of logger with 'trace()' method:
|
|
109
|
+
|
|
110
|
+
>>> from dill.logger import adapter as logger #NOTE: not dill.logger.logger
|
|
111
|
+
>>> ...
|
|
112
|
+
>>> def save_atype(pickler, obj):
|
|
113
|
+
>>> logger.trace(pickler, "Message with %s and %r etc. placeholders", 'text', obj)
|
|
114
|
+
>>> ...
|
|
115
|
+
"""
|
|
116
|
+
def __init__(self, logger):
|
|
117
|
+
self.logger = logger
|
|
118
|
+
def addHandler(self, handler):
|
|
119
|
+
formatter = TraceFormatter("%(prefix)s%(message)s%(suffix)s", handler=handler)
|
|
120
|
+
handler.setFormatter(formatter)
|
|
121
|
+
self.logger.addHandler(handler)
|
|
122
|
+
def removeHandler(self, handler):
|
|
123
|
+
self.logger.removeHandler(handler)
|
|
124
|
+
def process(self, msg, kwargs):
|
|
125
|
+
# A no-op override, as we don't have self.extra.
|
|
126
|
+
return msg, kwargs
|
|
127
|
+
def trace_setup(self, pickler):
|
|
128
|
+
# Called by Pickler.dump().
|
|
129
|
+
if not dill._dill.is_dill(pickler, child=False):
|
|
130
|
+
return
|
|
131
|
+
if self.isEnabledFor(logging.INFO):
|
|
132
|
+
pickler._trace_depth = 1
|
|
133
|
+
pickler._size_stack = []
|
|
134
|
+
else:
|
|
135
|
+
pickler._trace_depth = None
|
|
136
|
+
def trace(self, pickler, msg, *args, **kwargs):
|
|
137
|
+
if not hasattr(pickler, '_trace_depth'):
|
|
138
|
+
logger.info(msg, *args, **kwargs)
|
|
139
|
+
return
|
|
140
|
+
if pickler._trace_depth is None:
|
|
141
|
+
return
|
|
142
|
+
extra = kwargs.get('extra', {})
|
|
143
|
+
pushed_obj = msg.startswith('#')
|
|
144
|
+
size = None
|
|
145
|
+
try:
|
|
146
|
+
# Streams are not required to be tellable.
|
|
147
|
+
size = pickler._file.tell()
|
|
148
|
+
frame = pickler.framer.current_frame
|
|
149
|
+
try:
|
|
150
|
+
size += frame.tell()
|
|
151
|
+
except AttributeError:
|
|
152
|
+
# PyPy may use a BytesBuilder as frame
|
|
153
|
+
size += len(frame)
|
|
154
|
+
except (AttributeError, TypeError):
|
|
155
|
+
pass
|
|
156
|
+
if size is not None:
|
|
157
|
+
if not pushed_obj:
|
|
158
|
+
pickler._size_stack.append(size)
|
|
159
|
+
else:
|
|
160
|
+
size -= pickler._size_stack.pop()
|
|
161
|
+
extra['size'] = size
|
|
162
|
+
if pushed_obj:
|
|
163
|
+
pickler._trace_depth -= 1
|
|
164
|
+
extra['depth'] = pickler._trace_depth
|
|
165
|
+
kwargs['extra'] = extra
|
|
166
|
+
self.info(msg, *args, **kwargs)
|
|
167
|
+
if not pushed_obj:
|
|
168
|
+
pickler._trace_depth += 1
|
|
169
|
+
|
|
170
|
+
class TraceFormatter(logging.Formatter):
|
|
171
|
+
"""
|
|
172
|
+
Generates message prefix and suffix from record.
|
|
173
|
+
|
|
174
|
+
This Formatter adds prefix and suffix strings to the log message in trace
|
|
175
|
+
mode (an also provides empty string defaults for normal logs).
|
|
176
|
+
"""
|
|
177
|
+
def __init__(self, *args, handler=None, **kwargs):
|
|
178
|
+
super().__init__(*args, **kwargs)
|
|
179
|
+
try:
|
|
180
|
+
encoding = handler.stream.encoding
|
|
181
|
+
if encoding is None:
|
|
182
|
+
raise AttributeError
|
|
183
|
+
except AttributeError:
|
|
184
|
+
encoding = locale.getpreferredencoding()
|
|
185
|
+
try:
|
|
186
|
+
encoding = codecs.lookup(encoding).name
|
|
187
|
+
except LookupError:
|
|
188
|
+
self.is_utf8 = False
|
|
189
|
+
else:
|
|
190
|
+
self.is_utf8 = (encoding == codecs.lookup('utf-8').name)
|
|
191
|
+
def format(self, record):
|
|
192
|
+
fields = {'prefix': "", 'suffix': ""}
|
|
193
|
+
if getattr(record, 'depth', 0) > 0:
|
|
194
|
+
if record.msg.startswith("#"):
|
|
195
|
+
prefix = (record.depth - 1)*"│" + "└"
|
|
196
|
+
elif record.depth == 1:
|
|
197
|
+
prefix = "┬"
|
|
198
|
+
else:
|
|
199
|
+
prefix = (record.depth - 2)*"│" + "├┬"
|
|
200
|
+
if not self.is_utf8:
|
|
201
|
+
prefix = prefix.translate(ASCII_MAP) + "-"
|
|
202
|
+
fields['prefix'] = prefix + " "
|
|
203
|
+
if hasattr(record, 'size') and record.size is not None and record.size >= 1:
|
|
204
|
+
# Show object size in human-readable form.
|
|
205
|
+
power = int(math.log(record.size, 2)) // 10
|
|
206
|
+
size = record.size >> power*10
|
|
207
|
+
fields['suffix'] = " [%d %sB]" % (size, "KMGTP"[power] + "i" if power else "")
|
|
208
|
+
vars(record).update(fields)
|
|
209
|
+
return super().format(record)
|
|
210
|
+
|
|
211
|
+
logger = logging.getLogger('dill')
|
|
212
|
+
logger.propagate = False
|
|
213
|
+
adapter = TraceAdapter(logger)
|
|
214
|
+
stderr_handler = logging._StderrHandler()
|
|
215
|
+
adapter.addHandler(stderr_handler)
|
|
216
|
+
|
|
217
|
+
def trace(arg: Union[bool, TextIO, str, os.PathLike] = None, *, mode: str = 'a') -> None:
|
|
218
|
+
"""print a trace through the stack when pickling; useful for debugging
|
|
219
|
+
|
|
220
|
+
With a single boolean argument, enable or disable the tracing.
|
|
221
|
+
|
|
222
|
+
Example usage:
|
|
223
|
+
|
|
224
|
+
>>> import dill
|
|
225
|
+
>>> dill.detect.trace(True)
|
|
226
|
+
>>> dill.dump_session()
|
|
227
|
+
|
|
228
|
+
Alternatively, ``trace()`` can be used as a context manager. With no
|
|
229
|
+
arguments, it just takes care of restoring the tracing state on exit.
|
|
230
|
+
Either a file handle, or a file name and (optionally) a file mode may be
|
|
231
|
+
specitfied to redirect the tracing output in the ``with`` block context. A
|
|
232
|
+
log function is yielded by the manager so the user can write extra
|
|
233
|
+
information to the file.
|
|
234
|
+
|
|
235
|
+
Example usage:
|
|
236
|
+
|
|
237
|
+
>>> from dill import detect
|
|
238
|
+
>>> D = {'a': 42, 'b': {'x': None}}
|
|
239
|
+
>>> with detect.trace():
|
|
240
|
+
>>> dumps(D)
|
|
241
|
+
┬ D2: <dict object at 0x7f2721804800>
|
|
242
|
+
├┬ D2: <dict object at 0x7f27217f5c40>
|
|
243
|
+
│└ # D2 [8 B]
|
|
244
|
+
└ # D2 [22 B]
|
|
245
|
+
>>> squared = lambda x: x**2
|
|
246
|
+
>>> with detect.trace('output.txt', mode='w') as log:
|
|
247
|
+
>>> log("> D = %r", D)
|
|
248
|
+
>>> dumps(D)
|
|
249
|
+
>>> log("> squared = %r", squared)
|
|
250
|
+
>>> dumps(squared)
|
|
251
|
+
|
|
252
|
+
Arguments:
|
|
253
|
+
arg: a boolean value, or an optional file-like or path-like object for the context manager
|
|
254
|
+
mode: mode string for ``open()`` if a file name is passed as the first argument
|
|
255
|
+
"""
|
|
256
|
+
if repr(arg) not in ('False', 'True'):
|
|
257
|
+
return TraceManager(file=arg, mode=mode)
|
|
258
|
+
logger.setLevel(logging.INFO if arg else logging.WARNING)
|
|
259
|
+
|
|
260
|
+
class TraceManager(contextlib.AbstractContextManager):
|
|
261
|
+
"""context manager version of trace(); can redirect the trace to a file"""
|
|
262
|
+
def __init__(self, file, mode):
|
|
263
|
+
self.file = file
|
|
264
|
+
self.mode = mode
|
|
265
|
+
self.redirect = file is not None
|
|
266
|
+
self.file_is_stream = hasattr(file, 'write')
|
|
267
|
+
def __enter__(self):
|
|
268
|
+
if self.redirect:
|
|
269
|
+
stderr_handler.flush()
|
|
270
|
+
if self.file_is_stream:
|
|
271
|
+
self.handler = logging.StreamHandler(self.file)
|
|
272
|
+
else:
|
|
273
|
+
self.handler = logging.FileHandler(self.file, self.mode)
|
|
274
|
+
adapter.removeHandler(stderr_handler)
|
|
275
|
+
adapter.addHandler(self.handler)
|
|
276
|
+
self.old_level = adapter.getEffectiveLevel()
|
|
277
|
+
adapter.setLevel(logging.INFO)
|
|
278
|
+
return adapter.info
|
|
279
|
+
def __exit__(self, *exc_info):
|
|
280
|
+
adapter.setLevel(self.old_level)
|
|
281
|
+
if self.redirect:
|
|
282
|
+
adapter.removeHandler(self.handler)
|
|
283
|
+
adapter.addHandler(stderr_handler)
|
|
284
|
+
if not self.file_is_stream:
|
|
285
|
+
self.handler.close()
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
all Python Standard Library object types (currently: CH 1-15 @ 2.7)
|
|
10
|
+
and some other common object types (i.e. numpy.ndarray)
|
|
11
|
+
|
|
12
|
+
to load more objects and types, use dill.load_types()
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# non-local import of dill.objects
|
|
16
|
+
from dill import objects
|
|
17
|
+
for _type in objects.keys():
|
|
18
|
+
exec("%s = type(objects['%s'])" % (_type,_type))
|
|
19
|
+
|
|
20
|
+
del objects
|
|
21
|
+
try:
|
|
22
|
+
del _type
|
|
23
|
+
except NameError:
|
|
24
|
+
pass
|
|
@@ -0,0 +1,122 @@
|
|
|
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
|
+
__all__ = ['parent', 'reference', 'at', 'parents', 'children']
|
|
10
|
+
|
|
11
|
+
import gc
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from ._dill import _proxy_helper as reference
|
|
15
|
+
from ._dill import _locate_object as at
|
|
16
|
+
|
|
17
|
+
def parent(obj, objtype, ignore=()):
|
|
18
|
+
"""
|
|
19
|
+
>>> listiter = iter([4,5,6,7])
|
|
20
|
+
>>> obj = parent(listiter, list)
|
|
21
|
+
>>> obj == [4,5,6,7] # actually 'is', but don't have handle any longer
|
|
22
|
+
True
|
|
23
|
+
|
|
24
|
+
NOTE: objtype can be a single type (e.g. int or list) or a tuple of types.
|
|
25
|
+
|
|
26
|
+
WARNING: if obj is a sequence (e.g. list), may produce unexpected results.
|
|
27
|
+
Parent finds *one* parent (e.g. the last member of the sequence).
|
|
28
|
+
"""
|
|
29
|
+
depth = 1 #XXX: always looking for the parent (only, right?)
|
|
30
|
+
chain = parents(obj, objtype, depth, ignore)
|
|
31
|
+
parent = chain.pop()
|
|
32
|
+
if parent is obj:
|
|
33
|
+
return None
|
|
34
|
+
return parent
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parents(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ?
|
|
38
|
+
"""Find the chain of referents for obj. Chain will end with obj.
|
|
39
|
+
|
|
40
|
+
objtype: an object type or tuple of types to search for
|
|
41
|
+
depth: search depth (e.g. depth=2 is 'grandparents')
|
|
42
|
+
ignore: an object or tuple of objects to ignore in the search
|
|
43
|
+
"""
|
|
44
|
+
edge_func = gc.get_referents # looking for refs, not back_refs
|
|
45
|
+
predicate = lambda x: isinstance(x, objtype) # looking for parent type
|
|
46
|
+
#if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ?
|
|
47
|
+
ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore
|
|
48
|
+
ignore = (id(obj) for obj in ignore)
|
|
49
|
+
chain = find_chain(obj, predicate, edge_func, depth)[::-1]
|
|
50
|
+
#XXX: should pop off obj... ?
|
|
51
|
+
return chain
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def children(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ?
|
|
55
|
+
"""Find the chain of referrers for obj. Chain will start with obj.
|
|
56
|
+
|
|
57
|
+
objtype: an object type or tuple of types to search for
|
|
58
|
+
depth: search depth (e.g. depth=2 is 'grandchildren')
|
|
59
|
+
ignore: an object or tuple of objects to ignore in the search
|
|
60
|
+
|
|
61
|
+
NOTE: a common thing to ignore is all globals, 'ignore=(globals(),)'
|
|
62
|
+
|
|
63
|
+
NOTE: repeated calls may yield different results, as python stores
|
|
64
|
+
the last value in the special variable '_'; thus, it is often good
|
|
65
|
+
to execute something to replace '_' (e.g. >>> 1+1).
|
|
66
|
+
"""
|
|
67
|
+
edge_func = gc.get_referrers # looking for back_refs, not refs
|
|
68
|
+
predicate = lambda x: isinstance(x, objtype) # looking for child type
|
|
69
|
+
#if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ?
|
|
70
|
+
ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore
|
|
71
|
+
ignore = (id(obj) for obj in ignore)
|
|
72
|
+
chain = find_chain(obj, predicate, edge_func, depth, ignore)
|
|
73
|
+
#XXX: should pop off obj... ?
|
|
74
|
+
return chain
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# more generic helper function (cut-n-paste from objgraph)
|
|
78
|
+
# Source at http://mg.pov.lt/objgraph/
|
|
79
|
+
# Copyright (c) 2008-2010 Marius Gedminas <marius@pov.lt>
|
|
80
|
+
# Copyright (c) 2010 Stefano Rivera <stefano@rivera.za.net>
|
|
81
|
+
# Released under the MIT licence (see objgraph/objgrah.py)
|
|
82
|
+
|
|
83
|
+
def find_chain(obj, predicate, edge_func, max_depth=20, extra_ignore=()):
|
|
84
|
+
queue = [obj]
|
|
85
|
+
depth = {id(obj): 0}
|
|
86
|
+
parent = {id(obj): None}
|
|
87
|
+
ignore = set(extra_ignore)
|
|
88
|
+
ignore.add(id(extra_ignore))
|
|
89
|
+
ignore.add(id(queue))
|
|
90
|
+
ignore.add(id(depth))
|
|
91
|
+
ignore.add(id(parent))
|
|
92
|
+
ignore.add(id(ignore))
|
|
93
|
+
ignore.add(id(sys._getframe())) # this function
|
|
94
|
+
ignore.add(id(sys._getframe(1))) # find_chain/find_backref_chain, likely
|
|
95
|
+
gc.collect()
|
|
96
|
+
while queue:
|
|
97
|
+
target = queue.pop(0)
|
|
98
|
+
if predicate(target):
|
|
99
|
+
chain = [target]
|
|
100
|
+
while parent[id(target)] is not None:
|
|
101
|
+
target = parent[id(target)]
|
|
102
|
+
chain.append(target)
|
|
103
|
+
return chain
|
|
104
|
+
tdepth = depth[id(target)]
|
|
105
|
+
if tdepth < max_depth:
|
|
106
|
+
referrers = edge_func(target)
|
|
107
|
+
ignore.add(id(referrers))
|
|
108
|
+
for source in referrers:
|
|
109
|
+
if id(source) in ignore:
|
|
110
|
+
continue
|
|
111
|
+
if id(source) not in depth:
|
|
112
|
+
depth[id(source)] = tdepth + 1
|
|
113
|
+
parent[id(source)] = target
|
|
114
|
+
queue.append(source)
|
|
115
|
+
return [obj] # not found
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# backward compatibility
|
|
119
|
+
refobject = at
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# EOF
|