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
immlib/doc/_core.py ADDED
@@ -0,0 +1,311 @@
1
+ # -*- coding: utf-8 -*-
2
+ ###############################################################################
3
+ # immlib/doc/_core.py
4
+
5
+
6
+ # Dependencies ################################################################
7
+
8
+ from re import compile as re_compile
9
+ from functools import wraps
10
+ from docrep import DocstringProcessor
11
+
12
+
13
+ # The Document Processor ######################################################
14
+ def make_docproc():
15
+ """Creates and returns a document preprocessor."""
16
+ docproc = DocstringProcessor()
17
+ # We need to add a few features to the docproc's members so that we can
18
+ # process the Inputs and Outputs sections when present.
19
+ docproc.param_like_sections = \
20
+ docproc.param_like_sections + ['Inputs','Outputs']
21
+ docproc.patterns['Inputs'] = re_compile(
22
+ docproc.patterns['Parameters'].pattern
23
+ .replace('Parameters', 'Inputs')
24
+ .replace('----------', '------'))
25
+ docproc.patterns['Outputs'] = re_compile(
26
+ docproc.patterns['Parameters'].pattern
27
+ .replace('Parameters', 'Outputs')
28
+ .replace('----------', '-------'))
29
+ return docproc
30
+ # This gets imported into `immlib` as `immlib.docproc`, which is the name it
31
+ # should be known by, but we create it here in this submodule.
32
+ _initial_global_docproc = make_docproc()
33
+
34
+
35
+ # The docwrap Decorator #######################################################
36
+ def _docwrap_helper(f, fnname, indent=None, proc=Ellipsis):
37
+ # If no ident number was provided, deduce it.
38
+ if indent is None:
39
+ if not hasattr(f, '__doc__') or f.__doc__ is None:
40
+ # Doesn't matter, no documentation.
41
+ indent = 0
42
+ else:
43
+ lines = f.__doc__.split('\n')
44
+ # We always skip the first line (the one that starts with """).
45
+ lines = lines[1:]
46
+ # Strip the lines.
47
+ striplines = [s.lstrip() for s in lines]
48
+ # Pick out the ones with text in them and calculate the
49
+ # indentation.
50
+ indents = [
51
+ len(ws_s) - len(s)
52
+ for (ws_s,s) in zip(lines, striplines)
53
+ if len(s) > 0]
54
+ # The minimum is the one we want.
55
+ indent = min(indents) if len(indents) > 0 else 0
56
+ if proc is Ellipsis:
57
+ # We need to be able to obtain the default docproc object, even during
58
+ # the process of importing the library, when we use the
59
+ # _initial_global_docproc. Once immlib has been loaded, we use
60
+ # `immlib.docproc` instead.
61
+ try:
62
+ from immlib import docproc as proc
63
+ except ImportError:
64
+ proc = _initial_global_docproc
65
+ ff = f
66
+ ff = proc.with_indent(indent)(ff)
67
+ fd = proc.get_sections(base=fnname, sections=proc.param_like_sections)
68
+ ff = fd(ff)
69
+ ff = wraps(f)(ff) if f is not ff else f
70
+ # Post-process the documentation sections.
71
+ for section in ('parameters', 'other_parameters', 'inputs', 'outputs'):
72
+ k = fnname + '.' + section
73
+ v = proc.params.get(k, '')
74
+ if len(v) == 0: continue
75
+ for ln in v.split('\n'):
76
+ # Skip lines that start with whitespace.
77
+ if ln[0].strip() == '': continue
78
+ pname = ln.split(':')[0].strip()
79
+ proc.keep_params(k, pname)
80
+ return ff
81
+ def docwrap(f=None, /, *, indent=None, proc=Ellipsis):
82
+ """Applies standard doc-string processing to the decorated function.
83
+
84
+ The ``immlib.docwrap`` decorator applies a standard set of pre-processing
85
+ to the docstring of the function that follows it. This processing amounts
86
+ to using the ``docrep`` module's ``DocstringProcessor`` as a filter on the
87
+ documentation of the function. The function's documentation is always
88
+ placed in the base-name equal to its fully-qualified namespace name.
89
+
90
+ When called as ``@docwrap(name)`` for a string ``name``, the documentation
91
+ for the decorated function is instead placed under the base-name ``name``.
92
+
93
+ Parameters
94
+ ----------
95
+ f : function or str or None, optional
96
+ The function to be decorated, when ``@docwrap`` is used alone as a
97
+ decorator, or when used as a decorator with only the other options
98
+ given, such as ``@docwrap(indent=8)``. If a string is given, as in
99
+ ``@docwrap('immlib.dictmap')`` then the given string is used as the
100
+ function's name instead of its ``__module__`` plus its
101
+ ``__name__``. This is mostly useful when using ``@docwrap`` with a
102
+ function defined in a private submodule; for example ``immlib.dictmap``
103
+ is defined in ``immlib.util._core`` but is imported into a reclaimed by
104
+ the ``immlib`` core namespace, so it is typically considered to belong
105
+ to that namespace.
106
+
107
+ Typically this argument does not need to be provided as it is given
108
+ after the decorator line; the exception to this is when a string is
109
+ given.
110
+ indent : None or int, optional
111
+ The number of spaces that are used as indentation before lines in the
112
+ docstring. This is mostly useful when decorated functions appear in
113
+ indented contexts and thus the default indentation of 4 is
114
+ inappropriate. If ``None`` is given, then the decorator finds the
115
+ non-empty line, not including the first line, with the smallest
116
+ indentation and uses that.
117
+ proc : docrep.DocstringProcessor or Ellipsis, optional
118
+ The `proc` option provides the document processor object from the
119
+ ``docrep`` library that should be used to process the decorated
120
+ object. Because these objects can be specifically configured to enable
121
+ different docstring formats, this option is provided to the user. The
122
+ default value is ``Ellipsis``, in which case the ``immlib.docproc``
123
+ object is used. The ``docproc`` object has been configured to work with
124
+ the ``Input`` and ``Output`` sections that are used with calculations
125
+ and plans. The ``immlib.with_docproc`` function can be used to change
126
+ the ``immlib.docproc`` object that is used in a local code-block.
127
+
128
+ Returns
129
+ -------
130
+ object
131
+ The decorated function or object, after its docstring has been parsed.
132
+
133
+ See Also
134
+ --------
135
+ default_docproc :
136
+ Run a code-block with a specific default docstring processor.
137
+
138
+ """
139
+ # If we've been given a string, then we've been called as @docwrap(name)
140
+ # instead of @docwrap.
141
+ if f is None:
142
+ return lambda fn: _docwrap_helper(
143
+ fn, fn.__module__ + '.' + fn.__name__,
144
+ indent=indent,
145
+ proc=proc)
146
+ if isinstance(f, str):
147
+ return lambda fn: _docwrap_helper(fn, f, indent=indent, proc=proc)
148
+ else:
149
+ return _docwrap_helper(
150
+ f, f.__module__ + '.' + f.__name__,
151
+ indent=indent,
152
+ proc=proc)
153
+ class default_docproc:
154
+ """Context manager for setting the default ``immlib.docproc`` document
155
+ processing object.
156
+
157
+ The following code-block can be used to evaluate the code represented by
158
+ ``...`` using the ``docrep.DocstringProcessor`` object ``docproc`` as the
159
+ default ``immlib.docproc`` processor:
160
+
161
+ .. code-block:: python
162
+
163
+ with immlib.default_docproc(docproc):
164
+ ...
165
+
166
+ If the ``immlib.docproc`` value has accidentally been corrupted, then it
167
+ can be reset using the following:
168
+
169
+ .. code-block:: python
170
+
171
+ immlib.default_docproc.reset()
172
+
173
+ Parameters
174
+ ----------
175
+ docproc : docrep.DocstringProcessor object
176
+ The docstring processing object to use as the default in ``immlib`` in
177
+ the contextualized code.
178
+
179
+ See Also
180
+ --------
181
+ docwrap : decorator that simplifies the use of the ``docrep`` library.
182
+
183
+ """
184
+ __slots__ = ('original', 'docproc')
185
+ def __init__(self, docproc):
186
+ if not isinstance(docproc, DocstringProcessor):
187
+ raise TypeError("docproc must be a docrep.DocstringProcessor")
188
+ object.__setattr__(self, 'original', None)
189
+ object.__setattr__(self, 'docproc', docproc)
190
+ def __enter__(self):
191
+ import immlib
192
+ object.__setattr__(self, 'original', immlib.docproc)
193
+ immlib.docproc = self.docproc
194
+ return self.ureg
195
+ def __exit__(self, exc_type, exc_val, exc_tb):
196
+ import immlib
197
+ immlib.docproc = self.original
198
+ return False
199
+ def __setattr__(self, name, val):
200
+ raise TypeError("default_docproc is immutable")
201
+ @staticmethod
202
+ def reset():
203
+ """Resets the value of ``immlib.docproc`` to its value when the
204
+ ``immlib`` library was originally loaded."""
205
+ import immlib
206
+ immlib.docproc = _initial_global_docproc
207
+
208
+
209
+ # Other Utilities #############################################################
210
+
211
+ @docwrap
212
+ def detect_indentation(text, /, skip_first=True, tabsize=8):
213
+ """Given a block of text that is part of a docstring, guess the level of
214
+ indentation used to write it.
215
+
216
+ This function accepts a string that contains multiple lines and guesses the
217
+ indentation level used to write it. It does this by splitting the lines and
218
+ finding the line that starts with the smallest number of spaces. That
219
+ number of spaces is the indentation guess.
220
+
221
+ By default, this function skips the first line because it is customary to
222
+ start docstrings out with an unindented line. This behavior can be changed
223
+ by setting the optional argument `skip_first` to ``False``.
224
+
225
+ Parameters
226
+ ----------
227
+ text : str
228
+ The text whose indentation is to be guessed.
229
+ skip_first : bool, optional
230
+ Whether to skip the first line when detecting the indentation level.
231
+ The default is ``True``.
232
+ tabsize : int, optional
233
+ The number of spaces in a tab-stop; used to replace the tab characters
234
+ in each line using the method ``str.expandtabs``. The default is ``8``.
235
+
236
+ Returns
237
+ -------
238
+ int
239
+ The number of spaces of indentation detected.
240
+ """
241
+ if not isinstance(text, str):
242
+ raise TypeError(
243
+ f"detect_indentation requires str but got {type(text)}")
244
+ lns = text.split('\n')
245
+ ident = None
246
+ if skip_first:
247
+ lns = lns[1:]
248
+ for ln in lns:
249
+ ln = ln.expandtabs(tabsize)
250
+ if ln.strip() == '':
251
+ continue
252
+ ln_ident = len(ln) - len(ln.lstrip())
253
+ if ident is None:
254
+ ident = ln_ident
255
+ elif ln_ident < ident:
256
+ ident = ln_ident
257
+ return ident
258
+ @docwrap
259
+ def reindent(text, new_indent=0, /,
260
+ skip_first=True, tabsize=8, final_endline=True):
261
+ """Returns a block of text with a different indentation.
262
+
263
+ ``reindent(text, n)`` returns a copy of `text` after removing its current
264
+ indentation level and uniformly reindenting the text with ``n`` spaces. The
265
+ first line is skipped entirely, and the current indentation level is
266
+ detected using ``detect_indentation``.
267
+
268
+ Parameters
269
+ ----------
270
+ text : str
271
+ The text that is to be reindented.
272
+ new_indent : int, optional
273
+ The new indentation level. If this is not provided, then the default is
274
+ 0, meaning that the text will be unindented.
275
+ skip_first : bool, optional
276
+ Whether or not to skip the first line.
277
+ tabside : int, optional
278
+ How large to consider tab characters in the text; this is used with the
279
+ ``str.expandtabs`` method. The default is 8.
280
+ final_endline : bool, optional
281
+ Whether the returned string should end with a newline or not. The
282
+ default is ``True``.
283
+
284
+ Returns
285
+ -------
286
+ str
287
+ A duplicate of `text` with updated indentation.
288
+ """
289
+ # Get the current indentation level:
290
+ currind = detect_indentation(text, skip_first=skip_first, tabsize=tabsize)
291
+ # Split the text into lines.
292
+ lns = text.split('\n')
293
+ # Remove all the current indentations:
294
+ newlns = []
295
+ if skip_first:
296
+ newlns.append(lns[0].expandtabs(tabsize))
297
+ lns = lns[1:]
298
+ head = ' ' * currind
299
+ newhead = ' ' * new_indent
300
+ for ln in lns:
301
+ ln = ln.expandtabs(tabsize)
302
+ if ln.strip() == '':
303
+ newlns.append('')
304
+ continue
305
+ if ln.startswith(head):
306
+ ln = ln[currind:]
307
+ newlns.append(newhead + ln)
308
+ newtext = '\n'.join(newlns)
309
+ if final_endline and newtext[-1] != '\n':
310
+ newtext += '\n'
311
+ return newtext
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+ ################################################################################
3
+ # pimms/iolib/__init__.py
4
+
5
+ """Input/output tools managed by pimms; primarily the save and load functions.
6
+
7
+ The `pimms.iolib` module contains tools for saving and loading data to/from
8
+ paths or streams. This functionality is primarily supported via the `save` and
9
+ `load` objects that behave as general (de)serializers to which formats can be
10
+ registered.
11
+ """
12
+
13
+ from ._core import (
14
+ Save,
15
+ save,
16
+ Load,
17
+ load)
18
+
19
+ __all__ = (
20
+ 'Save',
21
+ 'save',
22
+ 'Load',
23
+ 'load')
24
+
25
+ # Mark these as native to this module.
26
+ Save.__module__ = __name__
27
+ save.__module__ = __name__
28
+ Load.__module__ = __name__
29
+ load.__module__ = __name__