immlib 1.0.0.dev2__py3-none-any.whl

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 (45) hide show
  1. immlib/__init__.py +131 -0
  2. immlib/_init.py +108 -0
  3. immlib/_version.py +235 -0
  4. immlib/doc/__init__.py +38 -0
  5. immlib/doc/_core.py +311 -0
  6. immlib/iolib/__init__.py +29 -0
  7. immlib/iolib/_core.py +720 -0
  8. immlib/pathlib/__init__.py +69 -0
  9. immlib/pathlib/_cache.py +152 -0
  10. immlib/pathlib/_core.py +869 -0
  11. immlib/pathlib/_osf.py +538 -0
  12. immlib/test/__init__.py +16 -0
  13. immlib/test/__main__.py +10 -0
  14. immlib/test/doc/__init__.py +6 -0
  15. immlib/test/doc/test_core.py +91 -0
  16. immlib/test/iolib/__init__.py +7 -0
  17. immlib/test/iolib/test_core.py +81 -0
  18. immlib/test/pathlib/__init__.py +11 -0
  19. immlib/test/pathlib/test_core.py +146 -0
  20. immlib/test/pathlib/test_osf.py +54 -0
  21. immlib/test/types/__init__.py +5 -0
  22. immlib/test/types/test_core.py +110 -0
  23. immlib/test/util/__init__.py +11 -0
  24. immlib/test/util/test_core.py +681 -0
  25. immlib/test/util/test_numeric.py +1374 -0
  26. immlib/test/util/test_quantity.py +218 -0
  27. immlib/test/util/test_url.py +51 -0
  28. immlib/test/workflow/__init__.py +9 -0
  29. immlib/test/workflow/test_core.py +418 -0
  30. immlib/test/workflow/test_plantype.py +248 -0
  31. immlib/types/__init__.py +29 -0
  32. immlib/types/_core.py +333 -0
  33. immlib/util/__init__.py +283 -0
  34. immlib/util/_core.py +2524 -0
  35. immlib/util/_numeric.py +2651 -0
  36. immlib/util/_quantity.py +523 -0
  37. immlib/util/_url.py +114 -0
  38. immlib/workflow/__init__.py +48 -0
  39. immlib/workflow/_core.py +1635 -0
  40. immlib/workflow/_plantype.py +334 -0
  41. immlib-1.0.0.dev2.dist-info/METADATA +76 -0
  42. immlib-1.0.0.dev2.dist-info/RECORD +45 -0
  43. immlib-1.0.0.dev2.dist-info/WHEEL +5 -0
  44. immlib-1.0.0.dev2.dist-info/licenses/LICENSE +21 -0
  45. immlib-1.0.0.dev2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+ ###############################################################################
3
+ # pimms/types/__init__.py
4
+
5
+ """The pimms subpackage containing various utility types.
6
+
7
+ The utility types included in pimms are:
8
+ * `MetaObject` is a `planobject` type that implements metadata via the
9
+ value `metadata` (a lazy dictionary) and the `withmeta` and `dropmeta`
10
+ methods.
11
+ * `ArrayInde` is a `planobject` type that implements
12
+ """
13
+
14
+ from ._core import (
15
+ MetaObject,
16
+ ArrayIndex,
17
+ ImmutableType,
18
+ Immutable)
19
+
20
+ __all__ = (
21
+ "MetaObject",
22
+ "ArrayIndex",
23
+ "ImmutableType",
24
+ "Immutable")
25
+
26
+ # Mark all the imported functions as belonging to this module instead of the
27
+ # hidden submodules:
28
+ from .._init import reclaim
29
+ reclaim(__name__, del_reclaim=True)
immlib/types/_core.py ADDED
@@ -0,0 +1,333 @@
1
+ # -*- Coding: utf-8 -*-
2
+ ###############################################################################
3
+ # pimms/types/_core.py
4
+
5
+ """The pimms subpackage containing various utility types.
6
+
7
+ The utility types included in pimms are:
8
+ * `MetaObject` is a `planobject` type that implements metadata via the
9
+ value `metadata` (a lazy dictionary) and the `withmeta` and `dropmeta`
10
+ methods.
11
+ * `PropTable` is a lazy table similar to a pandas `DataFrame` but built around
12
+ the abilities to lazily load properties (columns) and to annotating multiple
13
+ values for each property along arbitrary dimensions such as time or depth.
14
+ """
15
+
16
+
17
+ # Dependencies ################################################################
18
+
19
+ import math
20
+ import operator as op
21
+ from functools import partial
22
+ from warnings import warn
23
+ from collections import namedtuple
24
+ from threading import Lock
25
+ LockType = type(Lock())
26
+
27
+ import numpy as np
28
+ import scipy as sp
29
+ from pcollections import *
30
+ from numpy.lib.mixins import NDArrayOperatorsMixin
31
+
32
+ from ..doc import docwrap
33
+ from ..util import (
34
+ freezearray,
35
+ frozenarray,
36
+ is_integer,
37
+ is_tuple,
38
+ is_amap,
39
+ is_array,
40
+ is_realdata,
41
+ is_tensor,
42
+ is_array,
43
+ merge,
44
+ to_array)
45
+ from ..workflow import *
46
+
47
+
48
+ # MetaObject ##################################################################
49
+
50
+ class MetaObject(planobject):
51
+ """Base planobject type for objects that keep track of metadata.
52
+
53
+ Parameters
54
+ ----------
55
+ metadata : None or Mapping, optional
56
+ The dictionary of metadata that is to be attached to the object. If the
57
+ argument ``None`` is provided (the default), then the empty dictionary
58
+ is used.
59
+
60
+ Attributes
61
+ ----------
62
+ metadata : ldict
63
+ A lazy dictionary of the metadata tracked by the object.
64
+ """
65
+ def __init__(self, metadata=None):
66
+ if metadata is None:
67
+ metadata = ldict.empty
68
+ self.metadata = metadata
69
+ @calc('metadata', lazy=False)
70
+ def filter_metadata(metadata):
71
+ return ldict.empty if metadata is None else ldict(metadata)
72
+ def withmeta(self, *args, **kwargs):
73
+ """Return a duplicate object with updated metadata.
74
+
75
+ The arguments and keyword arguments to ``withmeta`` are merged,
76
+ left-to-right, into the current metadata; this new dictionary is used
77
+ as the metadata parameter of the new object.
78
+ """
79
+ new_metadata = merge(self.metadata, *args, **kwargs)
80
+ return self.set_metadata(new_metadata)
81
+ def dropmeta(self, *args):
82
+ """Returns a duplicate object with given metadata keys cleared.
83
+
84
+ The arguments must be keys, which are dropped from the metadata of
85
+ the duplicate object.
86
+ """
87
+ md = self.metadata
88
+ for k in args:
89
+ md = md.drop(k)
90
+ return self.set_metadata(md)
91
+ def set_metadata(self, md):
92
+ """Returns a duplicate object with the given metadata dictionary.
93
+
94
+ The argument must be a dict-like object.
95
+ """
96
+ if md is self.metadata:
97
+ return self
98
+ return self.copy(metadata=md)
99
+ def clear_metadata(self):
100
+ """Returns a duplicate object with its metadata dictionary cleared."""
101
+ if len(self.metadata) == 0:
102
+ return self
103
+ return self.copy(metadata=None)
104
+
105
+
106
+ # Immutable ###################################################################
107
+
108
+ class ImmutableType(type):
109
+ """A meta-class for types that are immutable.
110
+
111
+ When this metaclass is used in a class, objects of the type become
112
+ immutable immediately after the ``__init__`` method is run. Such types
113
+ should not overload the ``__new__`` classmethod and instead should see to
114
+ their initialization in ``__init__`` as usual. Once the ``__init__`` method
115
+ has finished, the ``__setattr__``, ``__delattr__``, ``__setitem__``, and
116
+ ``__delitem__`` methods will raise a ``TypeError``.
117
+ """
118
+ class ImmutableBase:
119
+ "The base class of all immlib immutable classes."
120
+ __slots__ = ('__init_status',)
121
+ def __setattr__(self, k, v):
122
+ if self.__init_status:
123
+ raise TypeError(f"{type(self)} is immutable")
124
+ else:
125
+ return object.__setattr__(self, k, v)
126
+ def __delattr__(self, k):
127
+ if self.__init_status:
128
+ raise TypeError(f"{type(self)} is immutable")
129
+ else:
130
+ return object.__delattr__(self, k)
131
+ def __init_wrap(self, *args, **kw):
132
+ # Find the correct __init__ function to run:
133
+ initfn = next(
134
+ filter(
135
+ None,
136
+ (getattr(c, f'_{c.__name__}__init__', None)
137
+ for c in type(self).__mro__)),
138
+ None)
139
+ if initfn:
140
+ initfn(self, *args, **kw)
141
+ # Note that we have now initialized everything.
142
+ self.__init_status = True
143
+ def __new__(cls, *args, **kw):
144
+ self = object.__new__(cls)
145
+ object.__setattr__(self, '_ImmutableBase__init_status', False)
146
+ return self
147
+ def __new__(cls, name, bases, attrs, **kwargs):
148
+ init_orig = attrs.get('__init__')
149
+ if init_orig:
150
+ attrs[f'_{name}__init__'] = init_orig
151
+ base = ImmutableType.ImmutableBase
152
+ attrs['__init__'] = base._ImmutableBase__init_wrap
153
+ if base not in bases:
154
+ bases = bases + (base,)
155
+ return type.__new__(cls, name, bases, attrs, **kwargs)
156
+ class Immutable(ImmutableType.ImmutableBase, metaclass=ImmutableType):
157
+ """A type that becomes immutable immediately after initialization.
158
+
159
+ Any class that inherits from ``Immutable`` should implement an ``__init__``
160
+ method, within which it is allowed to change the attributes of the ``self``
161
+ object normally. After the ``__init__`` method terminates, the object
162
+ becomes read-only and can no longer be updated.
163
+ """
164
+ __slots__ = ()
165
+
166
+
167
+ # ArrayIndex ##################################################################
168
+
169
+ ArrayIndexFlatData = namedtuple(
170
+ 'ArrayIndexFlatData',
171
+ ['ident', 'index'])
172
+ ArrayIndexFlatData.__doc__ = \
173
+ """Flattened identity and index data used by ArrayIndex to search for IDs.
174
+
175
+ Attributes
176
+ ----------
177
+ ident : read-only numpy array
178
+ The sorted and flattened identities represented in the original array.
179
+ index : read-only numpy array
180
+ The argsort of the flattened original array object.
181
+ """
182
+ class ArrayIndex:
183
+ """A type that indexes the elements of an array for easy searching.
184
+
185
+ The ``ArrayIndex`` class is a class that stores a (typically read-only)
186
+ numpy array whose elements must all be unique and that creates an index of
187
+ that array's elements. ``ArrayIndex`` objects primarily support a a
188
+ ``find`` method that can be used to look up object indices.
189
+
190
+ ``ArrayIndex`` objects require that the arrays they are given contain
191
+ unique objects that are sortable and hashable.
192
+
193
+ Examples
194
+ --------
195
+ >>> from immlib import ArrayIndex
196
+ >>> labels = [['r1c1', 'r1c2'], ['r2c1', 'r2c2']]
197
+ >>> index = ArrayIndex(labels)
198
+ >>> index.find('r1c1')
199
+ (0, 0)
200
+ >>> index.find(['r2c1', 'r1c2'])
201
+ (array([1, 0]), array([0, 1]))
202
+ >>> index.find(['r2c2', 'r2c1', 'r1c2'], ravel=True)
203
+ array([3, 1, 2])
204
+ """
205
+ # Class Methods -----------------------------------------------------------
206
+ @classmethod
207
+ def _make_flatdata(cls, array):
208
+ flatins = np.argsort(array.flat)
209
+ flatids = array.flat[flatins]
210
+ freezearray(flatins)
211
+ freezearray(flatids)
212
+ return ArrayIndexFlatData(flatids, flatins)
213
+ # Construction ------------------------------------------------------------
214
+ __slots__ = ('array', '_flatdata')
215
+ def __new__(cls, array, freeze=True):
216
+ if not freeze and is_array(array, frozen=True):
217
+ freeze = True
218
+ else:
219
+ array = to_array(array, frozen=freeze)
220
+ self = object.__new__(cls)
221
+ object.__setattr__(self, '_flatdata', Lock())
222
+ object.__setattr__(self, 'array', array)
223
+ return self
224
+ # Public Methods -----------------------------------------------------------
225
+ @docwrap(indent=8)
226
+ def find(self, ids, *, ravel=False, **kw):
227
+ """Finds and returns the indices of the given identities.
228
+
229
+ ``index.find(id)`` returns the index, in the original array on which
230
+ ``index`` is based, of the identity ``id``. If ``id`` is not in the
231
+ original array, then a ``KeyError`` is raised.
232
+
233
+ Parameters
234
+ ----------
235
+ ids : array-like
236
+ The identity or identities to look up in the index.
237
+ ravel : boolean, optional
238
+ Whether the return value should be an array representing a raveled
239
+ index into the flattened version of the original indexed array
240
+ (``True``) or a tuple representing an unraveled multi-index into
241
+ the original indexed array (``False``). The default is ``False``.
242
+ default : object, optional
243
+ If `default` is not given, then an error is raised when an identity
244
+ is not found. If `default` is given, however, the default value is
245
+ inserted into the place of any missing indices and no error is
246
+ raised.
247
+
248
+ Returns
249
+ -------
250
+ indices
251
+ The indices, into the original indexed array, of the given
252
+ identities, `ids`.
253
+ """
254
+ if len(kw) == 1:
255
+ try:
256
+ default = kw.pop('default')
257
+ except KeyError:
258
+ err = TypeError(
259
+ f'unrecognized option for find: {next(iter(kw.keys()))}')
260
+ raise err from None
261
+ error = False
262
+ else:
263
+ default = None
264
+ error = True
265
+ if len(kw) > 0:
266
+ k = next(iter(kw.keys()))
267
+ raise TypeError(f"'{k}' is an invalid keyword argument for find()")
268
+ ids = to_array(ids)
269
+ (flatids, flatins) = self.flatdata
270
+ # flatids is the ids in sorded order; flatins is the argsort of the
271
+ # original argsort--how to put the sorted ids back in canonical
272
+ # order; flatarg is the argsort itself.
273
+ ii = np.searchsorted(flatids, ids)
274
+ try:
275
+ ins = flatins[ii]
276
+ ok = flatids[ii] == ids
277
+ except IndexError:
278
+ ok = np.asarray((ii < len(flatids)) & (ii >= 0))
279
+ ok[ok] &= flatids[ii[ok]] == ids[ok]
280
+ ins = np.empty_like(ii, dtype=flatins.dtype)
281
+ ins[ok] = flatins[ii[ok]]
282
+ bad = ~ok
283
+ anymissing = np.any(bad)
284
+ # If some were not found, we might need to raise an error.
285
+ if anymissing:
286
+ if error:
287
+ raise KeyError(ids[bad].flat[0])
288
+ else:
289
+ ins[bad] = default
290
+ if not ravel:
291
+ if anymissing:
292
+ unrav = np.unravel_index(ins[ok], self.array.shape)
293
+ if not is_tuple(default):
294
+ default = (default,) * np.size(ins)
295
+ ins = tuple(np.empty_like(ins) for u in unrav)
296
+ for (u,r,d) in zip(ins, unrav, default):
297
+ u[bad] = d
298
+ u[ok] = r
299
+ else:
300
+ ins = np.unravel_index(ins, self.array.shape)
301
+ return ins
302
+ @property
303
+ def flatdata(self):
304
+ """Returns a named tuple containing the flattened data used by the
305
+ ``ArrayIndex`` type to lookup identities.
306
+
307
+ ``index.flatdata`` returns a named 2-tuple with keys ``ident`` and
308
+ ``index``. The ``ident`` element is a read-only numpy array containing
309
+ the sorted and flattened identities represented in the original
310
+ array. The ``index`` element is a read-only numpy array containing the
311
+ argsort of the flattened original array object.
312
+ """
313
+ flatdata = self._flatdata
314
+ if isinstance(flatdata, LockType):
315
+ with flatdata:
316
+ # Make sure that once we've acquired the lock we still need to
317
+ # calculate the flatdata (i.e., we didn't check then acquire
318
+ # after another thread ran).
319
+ lock = flatdata
320
+ flatdata = self._flatdata
321
+ if flatdata is lock:
322
+ flatdata = self._make_flatdata(self.array)
323
+ object.__setattr__(self, '_flatdata', flatdata)
324
+ return flatdata
325
+ # Disabled Methods --------------------------------------------------------
326
+ def __setattr__(self, k, v):
327
+ raise TypeError(f"{type(self)} is immutable")
328
+ def __delattr__(self, k):
329
+ raise TypeError(f"{type(self)} is immutable")
330
+ def __setitem__(self, k, v):
331
+ raise TypeError(f"{type(self)} is immutable")
332
+ def __delitem__(self, k):
333
+ raise TypeError(f"{type(self)} is immutable")
@@ -0,0 +1,283 @@
1
+ # -*- coding: utf-8 -*-
2
+ ################################################################################
3
+ # immlib/util/__init__.py
4
+
5
+ """Utilites managed by immlib.
6
+
7
+ The `immlib.util` module contains numerous utility functions that are intended
8
+ to be useful when writing APIs for scientific libraries. These include
9
+ functions that test for particular types or common object features, functions
10
+ for coercing types into other types, and functions for querying numpy arrays
11
+ and pytorch tensors.
12
+ """
13
+
14
+ # The _init module provides useful utilities for use during import, but these
15
+ # utilities happen to rightfully belong in this namespace.
16
+ from .._init import (
17
+ reclaim)
18
+
19
+ from ._core import (
20
+ is_str,
21
+ strnorm,
22
+ strcmp,
23
+ streq,
24
+ strstarts,
25
+ strends,
26
+ strissym,
27
+ striskey,
28
+ strisvar,
29
+ is_acallable,
30
+ is_lambda,
31
+ is_asized,
32
+ is_acontainer,
33
+ is_aiterable,
34
+ is_aiterator,
35
+ is_areversible,
36
+ is_acoll,
37
+ is_aseq,
38
+ is_amseq,
39
+ is_apseq,
40
+ is_abytes,
41
+ is_bytes,
42
+ is_aset,
43
+ is_amset,
44
+ is_apset,
45
+ is_amap,
46
+ is_ammap,
47
+ is_apmap,
48
+ is_ahashable,
49
+ is_tuple,
50
+ is_list,
51
+ is_plist,
52
+ is_tlist,
53
+ is_llist,
54
+ is_set,
55
+ is_frozenset,
56
+ is_pset,
57
+ is_tset,
58
+ is_dict,
59
+ is_odict,
60
+ is_ddict,
61
+ is_pdict,
62
+ is_tdict,
63
+ is_ldict,
64
+ get,
65
+ nestget,
66
+ hashsafe,
67
+ can_hash,
68
+ itersafe,
69
+ can_iter,
70
+ is_pcoll,
71
+ is_tcoll,
72
+ is_mcoll,
73
+ to_pcoll,
74
+ to_tcoll,
75
+ to_mcoll,
76
+ freezearray,
77
+ frozenarray,
78
+ lazykeymap,
79
+ lazyvalmap,
80
+ lazyitemmap,
81
+ keymap,
82
+ valmap,
83
+ itemmap,
84
+ dictmap,
85
+ pdictmap,
86
+ ldictmap,
87
+ merge,
88
+ rmerge,
89
+ assoc,
90
+ dissoc,
91
+ lambdadict,
92
+ args,
93
+ argfilter,
94
+ unitregistry,
95
+ to_pathcache,
96
+ to_lrucache,
97
+ identfn)
98
+
99
+ from ._numeric import (
100
+ checktorch,
101
+ alttorch,
102
+ is_numberdata,
103
+ is_booldata,
104
+ is_intdata,
105
+ is_realdata,
106
+ is_complexdata,
107
+ is_numpydtype,
108
+ like_numpydtype,
109
+ to_numpydtype,
110
+ sparse_layout,
111
+ sparse_find,
112
+ sparse_tolayout,
113
+ sparse_haslayout,
114
+ sparse_data,
115
+ sparse_indices,
116
+ is_array,
117
+ to_array,
118
+ is_torchdtype,
119
+ like_torchdtype,
120
+ to_torchdtype,
121
+ is_tensor,
122
+ to_tensor,
123
+ is_numeric,
124
+ to_numeric,
125
+ is_sparse,
126
+ to_sparse,
127
+ is_dense,
128
+ to_dense,
129
+ is_bool,
130
+ is_integer,
131
+ is_real,
132
+ is_complex,
133
+ is_number,
134
+ like_number,
135
+ to_number,
136
+ numapi,
137
+ tensor_args,
138
+ array_args,
139
+ numeric_args)
140
+
141
+ from ._quantity import (
142
+ is_ureg,
143
+ is_unit,
144
+ like_unit,
145
+ is_quant,
146
+ default_ureg,
147
+ alike_units,
148
+ unit,
149
+ quant,
150
+ mag,
151
+ promote)
152
+
153
+ from ._url import (
154
+ is_url,
155
+ can_download_url,
156
+ url_download)
157
+
158
+ __all__ = (
159
+ #"checktorch",
160
+ #"alttorch",
161
+ #"is_numpydtype",
162
+ #"like_numpydtype",
163
+ #"to_numpydtype",
164
+ #"is_torchdtype",
165
+ #"like_torchdtype",
166
+ #"to_torchdtype",
167
+ "sparse_find",
168
+ "is_array",
169
+ "to_array",
170
+ "is_tensor",
171
+ "to_tensor",
172
+ "is_numeric",
173
+ "to_numeric",
174
+ "is_sparse",
175
+ "to_sparse",
176
+ "is_dense",
177
+ "to_dense",
178
+ "like_number",
179
+ "to_number",
180
+ "is_str",
181
+ "strnorm",
182
+ "strcmp",
183
+ "streq",
184
+ "strstarts",
185
+ "strends",
186
+ "strissym",
187
+ "striskey",
188
+ "strisvar",
189
+ "is_ureg",
190
+ "is_unit",
191
+ "is_quant",
192
+ "is_lambda",
193
+ "is_asized",
194
+ "is_acontainer",
195
+ "is_aiterable",
196
+ "is_aiterator",
197
+ "is_areversible",
198
+ "is_acoll",
199
+ "is_aseq",
200
+ "is_amseq",
201
+ "is_apseq",
202
+ "is_abytes",
203
+ "is_bytes",
204
+ "is_aset",
205
+ "is_amset",
206
+ "is_apset",
207
+ "is_pset",
208
+ "is_amap",
209
+ "is_ammap",
210
+ "is_apmap",
211
+ "is_ahashable",
212
+ "is_tuple",
213
+ "is_list",
214
+ "is_plist",
215
+ "is_llist",
216
+ "is_set",
217
+ "is_frozenset",
218
+ "is_dict",
219
+ "is_odict",
220
+ "is_ddict",
221
+ "is_pdict",
222
+ "is_ldict",
223
+ "get",
224
+ "nestget",
225
+ "hashsafe",
226
+ "is_ahashable",
227
+ "can_hash",
228
+ "itersafe",
229
+ "can_iter",
230
+ "is_pcoll",
231
+ "is_tcoll",
232
+ "is_mcoll",
233
+ "to_pcoll",
234
+ "to_tcoll",
235
+ "to_mcoll",
236
+ "freezearray",
237
+ "frozenarray",
238
+ "is_number",
239
+ "is_bool",
240
+ "is_integer",
241
+ "is_real",
242
+ "is_complex",
243
+ "is_numberdata",
244
+ "is_booldata",
245
+ "is_intdata",
246
+ "is_realdata",
247
+ "is_complexdata",
248
+ "numapi",
249
+ "tensor_args",
250
+ "array_args",
251
+ "numeric_args",
252
+ "default_ureg",
253
+ "like_unit",
254
+ "alike_units",
255
+ "unit",
256
+ "quant",
257
+ "mag",
258
+ "promote",
259
+ "lazykeymap",
260
+ "lazyvalmap",
261
+ "lazyitemmap",
262
+ "keymap",
263
+ "valmap",
264
+ "itemmap",
265
+ "dictmap",
266
+ "pdictmap",
267
+ "ldictmap",
268
+ "merge",
269
+ "rmerge",
270
+ "assoc",
271
+ "dissoc",
272
+ "lambdadict",
273
+ "args",
274
+ "argfilter",
275
+ "is_url",
276
+ "url_download")
277
+
278
+ # Mark all the imported functions as belonging to this module instead of the
279
+ # hidden submodules. Most of these will later get claimed by the immlib primary
280
+ # module, but any that aren't belong here in immlib.util.
281
+ reclaim(__name__, del_reclaim=False)
282
+ # We also want to claim the `reclaim` function for util.
283
+ reclaim.__module__ = __name__