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.
- immlib/__init__.py +131 -0
- immlib/_init.py +108 -0
- immlib/_version.py +235 -0
- immlib/doc/__init__.py +38 -0
- immlib/doc/_core.py +311 -0
- immlib/iolib/__init__.py +29 -0
- immlib/iolib/_core.py +720 -0
- immlib/pathlib/__init__.py +69 -0
- immlib/pathlib/_cache.py +152 -0
- immlib/pathlib/_core.py +869 -0
- immlib/pathlib/_osf.py +538 -0
- immlib/test/__init__.py +16 -0
- immlib/test/__main__.py +10 -0
- immlib/test/doc/__init__.py +6 -0
- immlib/test/doc/test_core.py +91 -0
- immlib/test/iolib/__init__.py +7 -0
- immlib/test/iolib/test_core.py +81 -0
- immlib/test/pathlib/__init__.py +11 -0
- immlib/test/pathlib/test_core.py +146 -0
- immlib/test/pathlib/test_osf.py +54 -0
- immlib/test/types/__init__.py +5 -0
- immlib/test/types/test_core.py +110 -0
- immlib/test/util/__init__.py +11 -0
- immlib/test/util/test_core.py +681 -0
- immlib/test/util/test_numeric.py +1374 -0
- immlib/test/util/test_quantity.py +218 -0
- immlib/test/util/test_url.py +51 -0
- immlib/test/workflow/__init__.py +9 -0
- immlib/test/workflow/test_core.py +418 -0
- immlib/test/workflow/test_plantype.py +248 -0
- immlib/types/__init__.py +29 -0
- immlib/types/_core.py +333 -0
- immlib/util/__init__.py +283 -0
- immlib/util/_core.py +2524 -0
- immlib/util/_numeric.py +2651 -0
- immlib/util/_quantity.py +523 -0
- immlib/util/_url.py +114 -0
- immlib/workflow/__init__.py +48 -0
- immlib/workflow/_core.py +1635 -0
- immlib/workflow/_plantype.py +334 -0
- immlib-1.0.0.dev2.dist-info/METADATA +76 -0
- immlib-1.0.0.dev2.dist-info/RECORD +45 -0
- immlib-1.0.0.dev2.dist-info/WHEEL +5 -0
- immlib-1.0.0.dev2.dist-info/licenses/LICENSE +21 -0
- immlib-1.0.0.dev2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
###############################################################################
|
|
3
|
+
# immlib/workflow/_plantype.py
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Dependencies ################################################################
|
|
7
|
+
|
|
8
|
+
import inspect
|
|
9
|
+
from functools import wraps
|
|
10
|
+
from copy import copy
|
|
11
|
+
|
|
12
|
+
from pcollections import (pdict, ldict, lazy)
|
|
13
|
+
|
|
14
|
+
from ..doc import docwrap
|
|
15
|
+
from ..util import (is_str, is_pdict, is_tdict, is_ldict, assoc, merge)
|
|
16
|
+
from ._core import (calc, plan, plandict, tplandict, is_calcfn)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# #plantype ###################################################################
|
|
20
|
+
|
|
21
|
+
class plantype(type):
|
|
22
|
+
"""A metaclass that allows one to create lazy types from calculation plans.
|
|
23
|
+
|
|
24
|
+
The ``plantype`` metaclass handles classes with the base-class
|
|
25
|
+
``planobject``. In general, one should create a plan-object by inheriting
|
|
26
|
+
from ``planobject``, not by providing the ``plantype`` metaclass, but
|
|
27
|
+
passing ``plantype`` has the same effect (all classes created with
|
|
28
|
+
metaclass ``plantype`` will inherit from ``planobject``).
|
|
29
|
+
|
|
30
|
+
See ``planobject`` for more information.
|
|
31
|
+
"""
|
|
32
|
+
__slots__ = ()
|
|
33
|
+
class planobject_base:
|
|
34
|
+
"""The base-class for the ``immlib.planobject`` class.
|
|
35
|
+
|
|
36
|
+
``plantype.planobject_base`` is a simple class that implements the
|
|
37
|
+
basic features of the ``planobject`` class. The separation for certain
|
|
38
|
+
methods from the ``planobject`` type itself is required due to details
|
|
39
|
+
of how the ``planobject`` class, which has the ``plantype`` meta-class,
|
|
40
|
+
gets initialized while the ``plantype.__new__`` method depends on
|
|
41
|
+
methods in the ``planobject`` class (which hasn't been
|
|
42
|
+
initialized/defined at the time that the ``planobject.__new__`` method
|
|
43
|
+
is called. This class shouldn't be used directly and shouldn't be
|
|
44
|
+
inherited. Use the ``planobject`` class instead.
|
|
45
|
+
"""
|
|
46
|
+
__slots__ = ('_plandict_',)
|
|
47
|
+
def __getattr__(self, k):
|
|
48
|
+
pd = self._plandict_
|
|
49
|
+
r = pd.get(k, pd)
|
|
50
|
+
if r is pd:
|
|
51
|
+
raise AttributeError
|
|
52
|
+
else:
|
|
53
|
+
return r
|
|
54
|
+
def __setattr__(self, k, v):
|
|
55
|
+
pd = self._plandict_
|
|
56
|
+
pdtype = type(pd)
|
|
57
|
+
plan = type(self).plan
|
|
58
|
+
if pdtype is dict or pdtype is tplandict:
|
|
59
|
+
if k not in plan.inputs:
|
|
60
|
+
msg = "only planobject inputs may be mutated in "
|
|
61
|
+
if pdtype is dict:
|
|
62
|
+
msg += "the __init__ method"
|
|
63
|
+
else:
|
|
64
|
+
msg += "transient planobjects"
|
|
65
|
+
raise ValueError(msg)
|
|
66
|
+
else:
|
|
67
|
+
pd[k] = v
|
|
68
|
+
else:
|
|
69
|
+
raise TypeError(f"type {type(self)} is immutable")
|
|
70
|
+
def __delattr__(self, k):
|
|
71
|
+
pd = self._plandict_
|
|
72
|
+
if type(pd) is dict:
|
|
73
|
+
raise TypeError(
|
|
74
|
+
f"cannot delete attributes from plantype: {type(self)}")
|
|
75
|
+
else:
|
|
76
|
+
raise TypeError(f"type {type(self)} is immutable")
|
|
77
|
+
def __new__(cls, *args, **kwargs):
|
|
78
|
+
# Start by creating the object itself and setting up its slots.
|
|
79
|
+
obj = object.__new__(cls)
|
|
80
|
+
object.__setattr__(obj, '_plandict_', None)
|
|
81
|
+
# Once the __init__ function is done running, the plandict will be
|
|
82
|
+
# cleaned up (this is guaranteed by the plantype meta-class).
|
|
83
|
+
return obj
|
|
84
|
+
def __dir__(self):
|
|
85
|
+
pd = self._plandict_
|
|
86
|
+
l = object.__dir__(self)
|
|
87
|
+
l.extend(pd.keys())
|
|
88
|
+
l.sort()
|
|
89
|
+
return l
|
|
90
|
+
def __init__(self, *args, **kwargs):
|
|
91
|
+
for (k,v) in merge(*args, **kwargs).items():
|
|
92
|
+
setattr(self, k, v)
|
|
93
|
+
@staticmethod
|
|
94
|
+
def _init_wrapper(cls, self, *args, **kwargs):
|
|
95
|
+
"""Manages the initialization (``__init__``) for ``planobject``
|
|
96
|
+
types.
|
|
97
|
+
|
|
98
|
+
The ``planobject`` type, and any types that inherit from it, is
|
|
99
|
+
generally immutable; however, When a ``planobject`` is first
|
|
100
|
+
created, it is allowed to set its inputs, as if they were mutable
|
|
101
|
+
attributes, during the ``__init__()`` method. In order to
|
|
102
|
+
facilitate this, a reorganization of the initialization code for
|
|
103
|
+
each ``planobject`` subclass is performed when that class is
|
|
104
|
+
defined. The class's true ``__init__`` method is stored in the
|
|
105
|
+
method ``__planobject_init__``, while the
|
|
106
|
+
``plantype.planobject_base._init_wrapper`` method is stored in the
|
|
107
|
+
type's ``__init__`` method. This method calls the type's
|
|
108
|
+
``__planobject_init__`` method then makes the initialized object
|
|
109
|
+
immutable.
|
|
110
|
+
|
|
111
|
+
This method should not be called directly by the user.
|
|
112
|
+
"""
|
|
113
|
+
# If the plandict is not a mutable dictionary, we have already been
|
|
114
|
+
# initialized.
|
|
115
|
+
pd = self._plandict_
|
|
116
|
+
if is_pdict(pd) or is_tdict(pd):
|
|
117
|
+
raise RuntimeError(
|
|
118
|
+
"_init_wrapper method called on an already-initialized"
|
|
119
|
+
" planobject")
|
|
120
|
+
elif pd is not None:
|
|
121
|
+
# We're already in the middle of initializing; one of the
|
|
122
|
+
# __init__ methods probably just called a parent class's
|
|
123
|
+
# __init__ method. We can just run it and return.
|
|
124
|
+
return cls.__planobject_init__(self, *args, **kwargs)
|
|
125
|
+
# Otherwise, pd is None, meaning that we're the first initializer.
|
|
126
|
+
# Note that we are now in the process of initializing...
|
|
127
|
+
pd = {}
|
|
128
|
+
object.__setattr__(self, '_plandict_', pd)
|
|
129
|
+
# This method is the real initializer for the class (what the
|
|
130
|
+
# class's actual code wrote as the __init__ method).
|
|
131
|
+
cls.__planobject_init__(self, *args, **kwargs)
|
|
132
|
+
# Postprocess the argument.
|
|
133
|
+
theplan = type(self).plan
|
|
134
|
+
params = dict(theplan.defaults, **pd)
|
|
135
|
+
if params.keys() != theplan.inputs:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f"bad parameters for plantype {type(self)};"
|
|
138
|
+
f" expected {tuple(theplan.inputs)} but found"
|
|
139
|
+
f" {tuple(params.keys())}")
|
|
140
|
+
pd = theplan(params)
|
|
141
|
+
object.__setattr__(self, '_plandict_', pd)
|
|
142
|
+
# For the pickle module:
|
|
143
|
+
def __getstate__(self):
|
|
144
|
+
return dict(self._plandict_.inputs)
|
|
145
|
+
def __setstate__(self, inputs):
|
|
146
|
+
plan = type(self).plan
|
|
147
|
+
object.__setattr__(self, '_plandict_', plan(inputs))
|
|
148
|
+
def __new__(cls, name, bases, attrs, **kwargs):
|
|
149
|
+
sup = super(plantype, cls)
|
|
150
|
+
# Before we go too far, let's extract the valid args from kwargs.
|
|
151
|
+
initplan = kwargs.pop('plan', None)
|
|
152
|
+
inittrans = kwargs.pop('transient', False)
|
|
153
|
+
if len(kwargs) > 0:
|
|
154
|
+
ks = tuple(kwargs.keys())
|
|
155
|
+
raise ValueError(f"unsupported type options: {ks}")
|
|
156
|
+
# We want to go over the attributes and make a few changes:
|
|
157
|
+
# (1) We want to make the basic updates.
|
|
158
|
+
kvs = [
|
|
159
|
+
('__getattr__', plantype.planobject_base.__getattr__),
|
|
160
|
+
('__setattr__', plantype.planobject_base.__setattr__),
|
|
161
|
+
('__new__', plantype.planobject_base.__new__),
|
|
162
|
+
('__dir__', plantype.planobject_base.__dir__)]
|
|
163
|
+
for (k,v) in kvs:
|
|
164
|
+
if k in attrs:
|
|
165
|
+
raise ValueError(f"plantype classes may not define {k}")
|
|
166
|
+
else:
|
|
167
|
+
attrs[k] = v
|
|
168
|
+
# (2) We want to save the init function and update it to our version.
|
|
169
|
+
init = attrs.get('__init__', plantype.planobject_base.__init__)
|
|
170
|
+
attrs['__planobject_init__'] = init
|
|
171
|
+
def _initfn(self, *args, **kwargs):
|
|
172
|
+
return plantype.planobject_base._init_wrapper(
|
|
173
|
+
_initfn.cls, self, *args,
|
|
174
|
+
**kwargs)
|
|
175
|
+
attrs['__init__'] = wraps(init)(_initfn)
|
|
176
|
+
# (3) Go through the bases: see if there are planobject bases already,
|
|
177
|
+
# and if not, add planobject in. As we go, collect calculations.
|
|
178
|
+
calcs = {k:v for (k,v) in attrs.items() if is_calcfn(v)}
|
|
179
|
+
found_planobj = False
|
|
180
|
+
for b in bases:
|
|
181
|
+
if not issubclass(b, plantype.planobject_base): continue
|
|
182
|
+
found_planobj = True
|
|
183
|
+
for (k,v) in inspect.getmembers(b):
|
|
184
|
+
if k not in calcs and is_calcfn(v):
|
|
185
|
+
calcs[k] = v
|
|
186
|
+
if not found_planobj:
|
|
187
|
+
bases.append(plantype.planobject_base)
|
|
188
|
+
# (4) We now want to create the plan from these calculations and make
|
|
189
|
+
# sure it's part of the class.
|
|
190
|
+
if initplan is None:
|
|
191
|
+
attrs['plan'] = plan(calcs)
|
|
192
|
+
else:
|
|
193
|
+
attrs['plan'] = plan(initplan, **calcs)
|
|
194
|
+
# (5) Return the type with all the updated attributes.
|
|
195
|
+
cls = sup.__new__(cls, name, bases, attrs)
|
|
196
|
+
_initfn.cls = cls
|
|
197
|
+
return cls
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# #planobject #################################################################
|
|
201
|
+
|
|
202
|
+
class planobject(plantype.planobject_base, metaclass=plantype):
|
|
203
|
+
"""Base class for objects that are based on lazy calculation plans.
|
|
204
|
+
|
|
205
|
+
``planobject`` is the base-class for all objects that use ``immlib.plan``
|
|
206
|
+
objects as their base type. Objects that inherit from ``planobject`` (which
|
|
207
|
+
uses metaclass ``plantype``) are defined in the same way that calculation
|
|
208
|
+
plans are defined. Any attributes of the class (including those inherited
|
|
209
|
+
from base classes) that are calculations (see ``immlib.calc`` and
|
|
210
|
+
``immlib.plan``) are turned into a plan. The inputs of the plan are the
|
|
211
|
+
required parameters for the class, and the outputs of the plan become the
|
|
212
|
+
attributes of the class, which are resolved lazily like in ``plandict`` s.
|
|
213
|
+
|
|
214
|
+
The ``__init__`` function of a ``planobject`` is special. During the
|
|
215
|
+
``__init__`` function only, the parameters of a ``planobject`` function can
|
|
216
|
+
be set using the usual ``setattr`` interface. All ``planobject``s are
|
|
217
|
+
immutable once they have been initialized, however. At the end of the
|
|
218
|
+
``__init__`` function, the object must have all of its parameters set,
|
|
219
|
+
otherwise an error is raised. If no ``__init__`` function is defined, then
|
|
220
|
+
the ``planobject`` default init function calls ``merge`` on its arguments
|
|
221
|
+
and keywords; the resulting dict must be a dictionary of the class's
|
|
222
|
+
parameters.
|
|
223
|
+
|
|
224
|
+
``planobject`` types must not overload the following methods, as they are
|
|
225
|
+
used by the ``planobject`` / ``plantype`` system. These are:
|
|
226
|
+
* ``__new__``
|
|
227
|
+
* ``__setattr__``
|
|
228
|
+
* ``__getattr__``
|
|
229
|
+
* ``__dir__``
|
|
230
|
+
"""
|
|
231
|
+
__slots__ = ()
|
|
232
|
+
def __str__(self):
|
|
233
|
+
pd = object.__getattribute__(self, '_plandict_')
|
|
234
|
+
p = pd.plan
|
|
235
|
+
param_str = ", ".join(
|
|
236
|
+
f"{k}={pd[k] if pd.is_ready(k) else '<lazy>'}"
|
|
237
|
+
for k in p.inputs)
|
|
238
|
+
rest_str = ", ".join(
|
|
239
|
+
f"{k}={pd[k] if pd.is_ready(k) else '<lazy>'}"
|
|
240
|
+
for k in p.outputs if not k.startswith('_'))
|
|
241
|
+
cls = type(self)
|
|
242
|
+
if rest_str:
|
|
243
|
+
return f"{cls.__name__}({param_str}; {rest_str})"
|
|
244
|
+
else:
|
|
245
|
+
return f"{cls.__name__}({param_str})"
|
|
246
|
+
def __repr__(self):
|
|
247
|
+
pd = object.__getattribute__(self, '_plandict_')
|
|
248
|
+
p = pd.plan
|
|
249
|
+
param_str = ", ".join(
|
|
250
|
+
f"{k}={pd[k]}" for k in p.inputs)
|
|
251
|
+
rest_str = ", ".join(
|
|
252
|
+
f"{k}={repr(pd.getlazy(k))}" for k in p.outputs)
|
|
253
|
+
cls = type(self)
|
|
254
|
+
if rest_str:
|
|
255
|
+
return f"{cls.__module__}.{cls.__name__}({param_str}; {rest_str})"
|
|
256
|
+
else:
|
|
257
|
+
return f"{cls.__module__}.{cls.__name__}({param_str})"
|
|
258
|
+
def __eq__(self, other):
|
|
259
|
+
if type(self) is not type(other):
|
|
260
|
+
return False
|
|
261
|
+
return self._plandict_.inputs == other._plandict_.inputs
|
|
262
|
+
def __ne__(self, other):
|
|
263
|
+
if type(self) is not type(other):
|
|
264
|
+
return True
|
|
265
|
+
return self._plandict_.inputs != other._plandict_.inputs
|
|
266
|
+
def __hash__(self):
|
|
267
|
+
return hash((type(self), self._plandict_.inputs))
|
|
268
|
+
def copy(self, **kwargs):
|
|
269
|
+
"""Creates a copy of the planobject with optional parameter updates.
|
|
270
|
+
"""
|
|
271
|
+
pd = plandict(self._plandict_, **kwargs)
|
|
272
|
+
obj = object.__new__(self.__class__)
|
|
273
|
+
object.__setattr__(obj, '_plandict_', pd)
|
|
274
|
+
return obj
|
|
275
|
+
def transient(self):
|
|
276
|
+
"""Returns a transient copy of the planobject."""
|
|
277
|
+
pd = self._plandict_
|
|
278
|
+
if pd is None or isinstance(pd, dict):
|
|
279
|
+
raise RuntimeError(
|
|
280
|
+
f"cannot create transient copy during initialization")
|
|
281
|
+
c = copy(self)
|
|
282
|
+
object.__setattr__(c, '_plandict_', tplandict(pd))
|
|
283
|
+
return c
|
|
284
|
+
def persistent(self):
|
|
285
|
+
"""Returns a persistent copy of the planobject. If the planobject is
|
|
286
|
+
already persistent, it is returned unchanged.
|
|
287
|
+
"""
|
|
288
|
+
pd = self._plandict_
|
|
289
|
+
if pd is None or isinstance(pd, dict):
|
|
290
|
+
raise RuntimeError(
|
|
291
|
+
f"cannot create persistent copy during initialization")
|
|
292
|
+
elif isinstance(pd, plandict):
|
|
293
|
+
# Already persistent.
|
|
294
|
+
return self
|
|
295
|
+
elif not isinstance(pd, tplandict):
|
|
296
|
+
raise ValueError(f"unknown plandict type: {type(pd)}")
|
|
297
|
+
c = copy(self)
|
|
298
|
+
object.__setattr__(c, '_plandict_', pd.persistent())
|
|
299
|
+
return c
|
|
300
|
+
def is_persistent(self):
|
|
301
|
+
"""Returns ``True`` if the planobject is persistent and ``False`` if it
|
|
302
|
+
is transient.
|
|
303
|
+
"""
|
|
304
|
+
pd = self._plandict_
|
|
305
|
+
if pd is None or isinstance(pd, dict):
|
|
306
|
+
return type(self).init_transient
|
|
307
|
+
else:
|
|
308
|
+
return isinstance(pd, plandict)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# Utilities ###################################################################
|
|
312
|
+
|
|
313
|
+
@docwrap
|
|
314
|
+
def is_planobject(obj):
|
|
315
|
+
'''Determines if an object is an instance of a ``immlib.plantype`` object.
|
|
316
|
+
|
|
317
|
+
``is_planobject(obj)`` returns ``True`` if ``obj`` is an instance of a
|
|
318
|
+
``immlib.plantype`` class and ``False`` otherwise.
|
|
319
|
+
|
|
320
|
+
See also: ``plantype``, ``is_plantype``
|
|
321
|
+
'''
|
|
322
|
+
return isinstance(obj, planobject)
|
|
323
|
+
@docwrap
|
|
324
|
+
def is_plantype(obj):
|
|
325
|
+
'''Determines if an object is a ``immlib.plantype``.
|
|
326
|
+
|
|
327
|
+
``is_plantype(obj)`` returns ``True`` if ``obj`` is a ``immlib``
|
|
328
|
+
``plantype`` class and ``False`` otherwise. Note that this works for the
|
|
329
|
+
type but not instances of the type, for which you should use
|
|
330
|
+
``is_planobject``.
|
|
331
|
+
|
|
332
|
+
See also: ``is_planobject``, ``plantype``
|
|
333
|
+
'''
|
|
334
|
+
return isinstance(obj, plantype)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: immlib
|
|
3
|
+
Version: 1.0.0.dev2
|
|
4
|
+
Summary: A library of utilites for immutable scientific data.
|
|
5
|
+
Author-email: "Noah C. Benson" <nben@uw.edu>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: homepage, https://github.com/noahbenson/immlib
|
|
8
|
+
Project-URL: documentation, https://github.com/noahbenson/immlib
|
|
9
|
+
Project-URL: repository, https://github.com/noahbenson/immlib
|
|
10
|
+
Keywords: persistent,immutable,functional,scientific,workflow
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
26
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
27
|
+
Classifier: Operating System :: POSIX
|
|
28
|
+
Classifier: Operating System :: Unix
|
|
29
|
+
Classifier: Operating System :: MacOS
|
|
30
|
+
Requires-Python: >=3.8
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Dist: pcollections>=0.3.2
|
|
34
|
+
Requires-Dist: numpy>=1.24.0
|
|
35
|
+
Requires-Dist: scipy>=1.7.0
|
|
36
|
+
Requires-Dist: pint>=0.20.0
|
|
37
|
+
Requires-Dist: docrep>=0.3.2
|
|
38
|
+
Requires-Dist: joblib>=1.3.0
|
|
39
|
+
Requires-Dist: cloudpathlib[azure,gs,s3]>=0.18.0
|
|
40
|
+
Requires-Dist: pyyaml>=6.0
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: torch>=2.2.0; extra == "dev"
|
|
43
|
+
Provides-Extra: test
|
|
44
|
+
Requires-Dist: torch>=2.2.0; extra == "test"
|
|
45
|
+
Provides-Extra: docs
|
|
46
|
+
Requires-Dist: torch>=2.2.0; extra == "docs"
|
|
47
|
+
Requires-Dist: jupyter-book>=1.0.0; extra == "docs"
|
|
48
|
+
Requires-Dist: matplotlib>=3.4.0; extra == "docs"
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
[](https://codecov.io/gh/noahbenson/immlib)
|
|
55
|
+
[](https://badge.fury.io/py/immlib)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
**Author**: Noah C. Benson <[nben@uw.edu](mailto:nben@uw.edu)>
|
|
60
|
+
**License**: MIT
|
|
61
|
+
**[Documentation](https://noahbenson.github.io/immlib)**
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
`immlib` is a lightweight Python library that simplifies the design of
|
|
66
|
+
application programming interfaces (APIs) for scientific libraries. The name
|
|
67
|
+
immlib comes from the library’s philosophy of using immutable data to simplify
|
|
68
|
+
scientific workflows.
|
|
69
|
+
|
|
70
|
+
`immlib` is heavily based on the library
|
|
71
|
+
[`pimms`](https://github.com/noahbenson/pimms), which effectively served as a
|
|
72
|
+
prototype for `immlib`. Both libraries were motivated by a number of observations
|
|
73
|
+
about the design of scientific software and are an attempt to make some of these
|
|
74
|
+
problems easier to manage.
|
|
75
|
+
|
|
76
|
+
For more information, see the [documentation](https://noahbenson.github.io/immlib).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
immlib/__init__.py,sha256=fecv8u3tjOwKH4QEzbEoMzZM9ujg7HGf5whbiSG2_Ho,5172
|
|
2
|
+
immlib/_init.py,sha256=iKUNLoI78R7TdT5tp4zofOauTSgFrYfkRyrpbBiaDVU,4836
|
|
3
|
+
immlib/_version.py,sha256=K35pXPrOxntfy7H7G7zEct0zT4rR7fY1kOdLeveAiK8,9735
|
|
4
|
+
immlib/doc/__init__.py,sha256=oBumAnslnULXFYGCPFfe300juR0kYOs2Sg_yVjoW30w,1583
|
|
5
|
+
immlib/doc/_core.py,sha256=8zqulVeRLMkbsiqxuJA9wcvSZC-1UMxjx6P36gZL5BY,12363
|
|
6
|
+
immlib/iolib/__init__.py,sha256=VKXix_P56CYq9PzRYa3zdfnCG704rAuG5U5gcxiyJ5I,732
|
|
7
|
+
immlib/iolib/_core.py,sha256=Ccbq210K0ZOr26aae9W090wyei6mBwJblpn70HNH0ds,29779
|
|
8
|
+
immlib/pathlib/__init__.py,sha256=oH4vA4qlGvVXJZGV5i_1R9s-QgFtymZV-ouhgdNixMw,1532
|
|
9
|
+
immlib/pathlib/_cache.py,sha256=7enOW6hyr9lk_QpGucUdF1QMTtRN5zUkSZVk4HNkYAM,6457
|
|
10
|
+
immlib/pathlib/_core.py,sha256=8pQFDtDYBnveMXn8EEESbDC1lhnpqL-cC891rznkiQk,33104
|
|
11
|
+
immlib/pathlib/_osf.py,sha256=v9SRouin1nsnIVwtvNcOIysAbWF3HuQY1tw2CPhqOn0,23157
|
|
12
|
+
immlib/test/__init__.py,sha256=jQUoerBEmIrZU0k3lcDueL3I2MucqtBTug0xS-4Pvz0,422
|
|
13
|
+
immlib/test/__main__.py,sha256=2dVA5jVU-8Nz6JuECZfAxOpmvAAOeCNhur0mHgBCMcE,326
|
|
14
|
+
immlib/test/doc/__init__.py,sha256=Ne7qgHidq6_-77vFb73M3-TIWHxo9m550ayg-b_G20E,183
|
|
15
|
+
immlib/test/doc/test_core.py,sha256=poLHtPVHGt5hjIpTSy_J6sAN4LiA37WDB3y3hIK1LaE,3317
|
|
16
|
+
immlib/test/iolib/__init__.py,sha256=k17eFvA2NVB-nlRkvc7eqOb13qhg0js5QQHHnWejd3U,204
|
|
17
|
+
immlib/test/iolib/test_core.py,sha256=N1w_XbPXJLnX39_RRx4YqSo-QJso5tuu3p5jAmXp6Zw,3246
|
|
18
|
+
immlib/test/pathlib/__init__.py,sha256=fztC586ptuFNVYUqhGmMDGH4QiUPZfZCC169bjT7v5Q,285
|
|
19
|
+
immlib/test/pathlib/test_core.py,sha256=0oq8L-xlZtgkgCbNOrkm5YNOVK0OHAUXjckE7EAayk0,6251
|
|
20
|
+
immlib/test/pathlib/test_osf.py,sha256=_nc880p41bz8g7_UmWVFCPbraacIMzNi6oFgMC7Zqyw,1980
|
|
21
|
+
immlib/test/types/__init__.py,sha256=rjdFsszZ1o3h5X6bicGQYFhkJ6S7yzZKsRcwkHdmhEY,176
|
|
22
|
+
immlib/test/types/test_core.py,sha256=kMQ9M6GxMHLSYsY8YpD3Pn6IMPB_d7NDMSZ3CZV1Sog,4546
|
|
23
|
+
immlib/test/util/__init__.py,sha256=g7PJ2azOhPEGm6O_VwvSRiSbvOi84L4qJSho3etkkCY,344
|
|
24
|
+
immlib/test/util/test_core.py,sha256=OP62KnR_DBpHVp7mqc3gYjQhLlDbqfZE8UYVq_Vqdgo,30410
|
|
25
|
+
immlib/test/util/test_numeric.py,sha256=8ZMm8d5o1A8u7hV_jqViAypXZ0seV7dpQE6nAAhP2jo,69693
|
|
26
|
+
immlib/test/util/test_quantity.py,sha256=WGhmTOgriERj099erRAMQ00geQBirRsiwjJ7Tv6iqJk,10528
|
|
27
|
+
immlib/test/util/test_url.py,sha256=27nj07zdHLPkWrETUgfAhn_r9FBBh8E99q_phrY_tIk,1953
|
|
28
|
+
immlib/test/workflow/__init__.py,sha256=ADNWqVFMf-HJeLlr-yMjLVTVAkokDIo1g9FOAyZ552A,275
|
|
29
|
+
immlib/test/workflow/test_core.py,sha256=Jz-DmiNBN9OWMhlpXfpk9rlnsZBepz4f8ebkBvbZcag,19684
|
|
30
|
+
immlib/test/workflow/test_plantype.py,sha256=zCr4_D7heoaK3p_4QKWaZfvFdqHqHURL8z_-7XjOZeA,9507
|
|
31
|
+
immlib/types/__init__.py,sha256=1io0pYlD8qGbl5oxC62TXImi374QGp1Ie4nkOjepNdo,786
|
|
32
|
+
immlib/types/_core.py,sha256=KOMrwG2gnAk634wvu7cPgdyvAjGsMUfV2QOTNEuRn4I,12894
|
|
33
|
+
immlib/util/__init__.py,sha256=jLIybcflDJzu6J-Ow4CzQfhLIqm1jPS3P6QvUfr2SJ0,5056
|
|
34
|
+
immlib/util/_core.py,sha256=X5ZZJCa41G-9vbR-Y-ccQmt-JftsTqf_v175gKU2tvQ,86895
|
|
35
|
+
immlib/util/_numeric.py,sha256=UEtaOGsDo3ac4utI98zzp8_t4fRSakFc9UIRXEYILXY,109774
|
|
36
|
+
immlib/util/_quantity.py,sha256=Y5uEOzuVjyqFZ0mKaAVu2NA02TQljxbYW3dUx9pWBYY,19704
|
|
37
|
+
immlib/util/_url.py,sha256=HXZ7e_DwvZclBf8GxmQXptPytTX7jUH8RatpTXOJU8E,4414
|
|
38
|
+
immlib/workflow/__init__.py,sha256=eKln38YHuI2yyduSgO3tuk-QNlUdqiqDTT2ikJZcN3c,1065
|
|
39
|
+
immlib/workflow/_core.py,sha256=Dufeh9DdyQt4XSGwF0ItB_9KybU_6OVQkjA7EKOEGHs,72939
|
|
40
|
+
immlib/workflow/_plantype.py,sha256=Q6tFqPmCUt9KblwfgGGWgdZCnZLn4KSzak7yK47RFu0,14595
|
|
41
|
+
immlib-1.0.0.dev2.dist-info/licenses/LICENSE,sha256=SmSO0nCJkuibrAEOAuq8HNUn6SJ52Pk_NQ2bf7HHw-g,1071
|
|
42
|
+
immlib-1.0.0.dev2.dist-info/METADATA,sha256=jMek4toNqYFpgZiQj-HK30LTDKjGUrWeOb14D_Pelk4,3216
|
|
43
|
+
immlib-1.0.0.dev2.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
44
|
+
immlib-1.0.0.dev2.dist-info/top_level.txt,sha256=xZpW_wIFaEUjhV4ZcdIsyDupd1RZKv43W3XV8frCkUk,7
|
|
45
|
+
immlib-1.0.0.dev2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Noah C. Benson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
immlib
|