immlib 1.0.0.dev2__tar.gz → 1.0.0rc2__tar.gz

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 (75) hide show
  1. {immlib-1.0.0.dev2/src/immlib.egg-info → immlib-1.0.0rc2}/PKG-INFO +48 -13
  2. immlib-1.0.0rc2/README.md +58 -0
  3. immlib-1.0.0rc2/pyproject.toml +95 -0
  4. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/__init__.py +22 -30
  5. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/_version.py +70 -56
  6. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/iolib/__init__.py +3 -9
  7. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/iolib/_core.py +133 -66
  8. immlib-1.0.0rc2/src/immlib/math/__init__.py +348 -0
  9. immlib-1.0.0rc2/src/immlib/math/_core.py +3253 -0
  10. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/pathlib/__init__.py +0 -5
  11. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/pathlib/_cache.py +23 -15
  12. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/pathlib/_core.py +88 -87
  13. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/pathlib/_osf.py +94 -42
  14. immlib-1.0.0rc2/src/immlib/py.typed +0 -0
  15. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/__init__.py +3 -2
  16. immlib-1.0.0rc2/src/immlib/test/concurrency/__init__.py +8 -0
  17. immlib-1.0.0rc2/src/immlib/test/concurrency/test_threads.py +537 -0
  18. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/iolib/__init__.py +2 -2
  19. immlib-1.0.0rc2/src/immlib/test/iolib/test_core.py +539 -0
  20. immlib-1.0.0rc2/src/immlib/test/math/__init__.py +9 -0
  21. immlib-1.0.0rc2/src/immlib/test/math/test_math.py +1089 -0
  22. immlib-1.0.0rc2/src/immlib/test/math/test_rules.py +552 -0
  23. immlib-1.0.0rc2/src/immlib/test/pathlib/__init__.py +12 -0
  24. immlib-1.0.0rc2/src/immlib/test/pathlib/_osf_fixture.py +98 -0
  25. immlib-1.0.0rc2/src/immlib/test/pathlib/test_cache.py +244 -0
  26. immlib-1.0.0rc2/src/immlib/test/pathlib/test_core.py +347 -0
  27. immlib-1.0.0rc2/src/immlib/test/pathlib/test_osf.py +225 -0
  28. immlib-1.0.0rc2/src/immlib/test/test_version.py +197 -0
  29. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/util/__init__.py +3 -0
  30. immlib-1.0.0rc2/src/immlib/test/util/test_arrayindex.py +139 -0
  31. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/util/test_core.py +29 -1
  32. immlib-1.0.0rc2/src/immlib/test/util/test_docs.py +211 -0
  33. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/util/test_numeric.py +122 -14
  34. immlib-1.0.0rc2/src/immlib/test/util/test_pint.py +136 -0
  35. immlib-1.0.0rc2/src/immlib/test/util/test_quantity.py +2675 -0
  36. immlib-1.0.0rc2/src/immlib/test/workflow/test_core.py +933 -0
  37. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/workflow/test_plantype.py +37 -6
  38. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/util/__init__.py +24 -12
  39. immlib-1.0.0.dev2/src/immlib/types/_core.py → immlib-1.0.0rc2/src/immlib/util/_arrayindex.py +42 -158
  40. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/util/_core.py +240 -194
  41. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/util/_numeric.py +397 -344
  42. immlib-1.0.0rc2/src/immlib/util/_quantity.py +3415 -0
  43. immlib-1.0.0rc2/src/immlib/util/_url.py +242 -0
  44. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/workflow/__init__.py +6 -5
  45. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/workflow/_core.py +475 -81
  46. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/workflow/_plantype.py +22 -9
  47. {immlib-1.0.0.dev2 → immlib-1.0.0rc2/src/immlib.egg-info}/PKG-INFO +48 -13
  48. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib.egg-info/SOURCES.txt +15 -9
  49. immlib-1.0.0rc2/src/immlib.egg-info/requires.txt +25 -0
  50. immlib-1.0.0.dev2/README.md +0 -26
  51. immlib-1.0.0.dev2/pyproject.toml +0 -48
  52. immlib-1.0.0.dev2/src/immlib/_init.py +0 -108
  53. immlib-1.0.0.dev2/src/immlib/doc/__init__.py +0 -38
  54. immlib-1.0.0.dev2/src/immlib/doc/_core.py +0 -311
  55. immlib-1.0.0.dev2/src/immlib/test/doc/__init__.py +0 -6
  56. immlib-1.0.0.dev2/src/immlib/test/doc/test_core.py +0 -91
  57. immlib-1.0.0.dev2/src/immlib/test/iolib/test_core.py +0 -81
  58. immlib-1.0.0.dev2/src/immlib/test/pathlib/__init__.py +0 -11
  59. immlib-1.0.0.dev2/src/immlib/test/pathlib/test_core.py +0 -146
  60. immlib-1.0.0.dev2/src/immlib/test/pathlib/test_osf.py +0 -54
  61. immlib-1.0.0.dev2/src/immlib/test/types/__init__.py +0 -5
  62. immlib-1.0.0.dev2/src/immlib/test/types/test_core.py +0 -110
  63. immlib-1.0.0.dev2/src/immlib/test/util/test_quantity.py +0 -218
  64. immlib-1.0.0.dev2/src/immlib/test/workflow/test_core.py +0 -418
  65. immlib-1.0.0.dev2/src/immlib/types/__init__.py +0 -29
  66. immlib-1.0.0.dev2/src/immlib/util/_quantity.py +0 -523
  67. immlib-1.0.0.dev2/src/immlib/util/_url.py +0 -114
  68. immlib-1.0.0.dev2/src/immlib.egg-info/requires.txt +0 -19
  69. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/LICENSE +0 -0
  70. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/setup.cfg +0 -0
  71. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/__main__.py +0 -0
  72. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/util/test_url.py +0 -0
  73. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib/test/workflow/__init__.py +0 -0
  74. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib.egg-info/dependency_links.txt +0 -0
  75. {immlib-1.0.0.dev2 → immlib-1.0.0rc2}/src/immlib.egg-info/top_level.txt +0 -0
@@ -1,23 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: immlib
3
- Version: 1.0.0.dev2
4
- Summary: A library of utilites for immutable scientific data.
3
+ Version: 1.0.0rc2
4
+ Summary: A library of utilities for immutable scientific data.
5
5
  Author-email: "Noah C. Benson" <nben@uw.edu>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: homepage, https://github.com/noahbenson/immlib
8
8
  Project-URL: documentation, https://github.com/noahbenson/immlib
9
9
  Project-URL: repository, https://github.com/noahbenson/immlib
10
10
  Keywords: persistent,immutable,functional,scientific,workflow
11
11
  Classifier: Intended Audience :: Science/Research
12
12
  Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
14
  Classifier: Programming Language :: Python :: 3.10
18
15
  Classifier: Programming Language :: Python :: 3.11
19
16
  Classifier: Programming Language :: Python :: 3.12
20
17
  Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
21
19
  Classifier: Topic :: Software Development
22
20
  Classifier: Topic :: Software Development :: Libraries
23
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -27,25 +25,30 @@ Classifier: Operating System :: Microsoft :: Windows
27
25
  Classifier: Operating System :: POSIX
28
26
  Classifier: Operating System :: Unix
29
27
  Classifier: Operating System :: MacOS
30
- Requires-Python: >=3.8
28
+ Requires-Python: >=3.10
31
29
  Description-Content-Type: text/markdown
32
30
  License-File: LICENSE
33
- Requires-Dist: pcollections>=0.3.2
31
+ Requires-Dist: pcollections>=1.0.0rc1
34
32
  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
33
+ Requires-Dist: scipy>=1.8.0
34
+ Requires-Dist: pint<0.27,>=0.24.0
35
+ Requires-Dist: docshare>=0.2.0
38
36
  Requires-Dist: joblib>=1.3.0
39
- Requires-Dist: cloudpathlib[azure,gs,s3]>=0.18.0
37
+ Requires-Dist: cloudpathlib[azure,gs,s3]<0.26,>=0.18.0
40
38
  Requires-Dist: pyyaml>=6.0
41
39
  Provides-Extra: dev
42
40
  Requires-Dist: torch>=2.2.0; extra == "dev"
41
+ Requires-Dist: pandas>=1.5.0; extra == "dev"
42
+ Requires-Dist: mypy>=1.8; extra == "dev"
43
43
  Provides-Extra: test
44
44
  Requires-Dist: torch>=2.2.0; extra == "test"
45
+ Requires-Dist: pandas>=1.5.0; extra == "test"
46
+ Provides-Extra: pandas
47
+ Requires-Dist: pandas>=1.5.0; extra == "pandas"
45
48
  Provides-Extra: docs
46
49
  Requires-Dist: torch>=2.2.0; extra == "docs"
47
- Requires-Dist: jupyter-book>=1.0.0; extra == "docs"
48
50
  Requires-Dist: matplotlib>=3.4.0; extra == "docs"
51
+ Requires-Dist: jupyter-book>=2; extra == "docs"
49
52
  Dynamic: license-file
50
53
 
51
54
  ![immlib](https://noahbenson.github.io/immlib/_static/logo.svg "immlib")
@@ -67,6 +70,38 @@ application programming interfaces (APIs) for scientific libraries. The name
67
70
  immlib comes from the library’s philosophy of using immutable data to simplify
68
71
  scientific workflows.
69
72
 
73
+ ## What's in it
74
+
75
+ - **`immlib.workflow`** — immutable workflows built from `calc` units that are
76
+ assembled into `plan` DAGs. Inputs and outputs are tracked and wired up
77
+ automatically, results are computed lazily, and calculations can be cached in
78
+ memory or on disk.
79
+ - **`immlib.pathlib` / `immlib.iolib`** — a single `path` function that resolves
80
+ local and remote paths (S3, Google Storage, Azure, and OSF), downloads remote
81
+ data into a local cache, and `save`/`load` functions that read and write a
82
+ range of file formats.
83
+ - **quantities and units** — `immlib.Quantity` (a `pint.Quantity` that also
84
+ supports having no units at all) and `immlib.math`, a small common namespace
85
+ that operates on quantities, NumPy arrays, and PyTorch tensors alike, so the
86
+ two backends are interchangeable and units are tracked through the
87
+ arithmetic.
88
+
89
+ Alongside these are a set of utilities for testing and coercing types, handling
90
+ persistent and lazy collections, and working with numerical arguments.
91
+
92
+ ## Requirements
93
+
94
+ `immlib` supports Python 3.10 through 3.14, including the free-threaded (no-GIL)
95
+ builds, and depends on NumPy, SciPy, Pint, `pcollections`, `docshare`, `joblib`,
96
+ `cloudpathlib`, and PyYAML.
97
+
98
+ PyTorch and pandas are optional. `immlib` never imports PyTorch at import time,
99
+ so it can be used without it (only operations that actually need a tensor
100
+ raise); pandas is needed only by the `csv`/`tsv` save/load formats. See the
101
+ "Stability and Compatibility" page of the
102
+ [documentation](https://noahbenson.github.io/immlib) for the supported version
103
+ ranges of each dependency and what the library promises across releases.
104
+
70
105
  `immlib` is heavily based on the library
71
106
  [`pimms`](https://github.com/noahbenson/pimms), which effectively served as a
72
107
  prototype for `immlib`. Both libraries were motivated by a number of observations
@@ -0,0 +1,58 @@
1
+ ![immlib](https://noahbenson.github.io/immlib/_static/logo.svg "immlib")
2
+
3
+ ![Build Status](https://github.com/noahbenson/immlib/actions/workflows/tests.yml/badge.svg)
4
+ [![codecov](https://codecov.io/gh/noahbenson/immlib/graph/badge.svg?token=8KO3K6DUX4)](https://codecov.io/gh/noahbenson/immlib)
5
+ [![PyPI version](https://badge.fury.io/py/immlib.svg)](https://badge.fury.io/py/immlib)
6
+
7
+ ---
8
+
9
+ **Author**: Noah C. Benson &lt;[nben@uw.edu](mailto:nben@uw.edu)&gt;
10
+ **License**: MIT
11
+ **[Documentation](https://noahbenson.github.io/immlib)**
12
+
13
+ ---
14
+
15
+ `immlib` is a lightweight Python library that simplifies the design of
16
+ application programming interfaces (APIs) for scientific libraries. The name
17
+ immlib comes from the library’s philosophy of using immutable data to simplify
18
+ scientific workflows.
19
+
20
+ ## What's in it
21
+
22
+ - **`immlib.workflow`** — immutable workflows built from `calc` units that are
23
+ assembled into `plan` DAGs. Inputs and outputs are tracked and wired up
24
+ automatically, results are computed lazily, and calculations can be cached in
25
+ memory or on disk.
26
+ - **`immlib.pathlib` / `immlib.iolib`** — a single `path` function that resolves
27
+ local and remote paths (S3, Google Storage, Azure, and OSF), downloads remote
28
+ data into a local cache, and `save`/`load` functions that read and write a
29
+ range of file formats.
30
+ - **quantities and units** — `immlib.Quantity` (a `pint.Quantity` that also
31
+ supports having no units at all) and `immlib.math`, a small common namespace
32
+ that operates on quantities, NumPy arrays, and PyTorch tensors alike, so the
33
+ two backends are interchangeable and units are tracked through the
34
+ arithmetic.
35
+
36
+ Alongside these are a set of utilities for testing and coercing types, handling
37
+ persistent and lazy collections, and working with numerical arguments.
38
+
39
+ ## Requirements
40
+
41
+ `immlib` supports Python 3.10 through 3.14, including the free-threaded (no-GIL)
42
+ builds, and depends on NumPy, SciPy, Pint, `pcollections`, `docshare`, `joblib`,
43
+ `cloudpathlib`, and PyYAML.
44
+
45
+ PyTorch and pandas are optional. `immlib` never imports PyTorch at import time,
46
+ so it can be used without it (only operations that actually need a tensor
47
+ raise); pandas is needed only by the `csv`/`tsv` save/load formats. See the
48
+ "Stability and Compatibility" page of the
49
+ [documentation](https://noahbenson.github.io/immlib) for the supported version
50
+ ranges of each dependency and what the library promises across releases.
51
+
52
+ `immlib` is heavily based on the library
53
+ [`pimms`](https://github.com/noahbenson/pimms), which effectively served as a
54
+ prototype for `immlib`. Both libraries were motivated by a number of observations
55
+ about the design of scientific software and are an attempt to make some of these
56
+ problems easier to manage.
57
+
58
+ For more information, see the [documentation](https://noahbenson.github.io/immlib).
@@ -0,0 +1,95 @@
1
+ [build-system]
2
+ # PEP 639 (SPDX license expressions and license-files) requires setuptools 77;
3
+ # immlib requires Python 3.10, so there is no need for the older-setuptools
4
+ # fallback that a Python 3.8-compatible project would need.
5
+ requires = ["setuptools >= 77.0.3"]
6
+ build-backend = "setuptools.build_meta"
7
+
8
+ [project]
9
+ name = "immlib"
10
+ version = "1.0.0rc2"
11
+ description = """A library of utilities for immutable scientific data."""
12
+ authors = [{name="Noah C. Benson", email="nben@uw.edu"}]
13
+ license = "MIT"
14
+ license-files = ["LICENSE"]
15
+ readme = "README.md"
16
+ requires-python = ">=3.10"
17
+ keywords = ["persistent", "immutable", "functional", "scientific", "workflow"]
18
+ classifiers = [
19
+ 'Intended Audience :: Science/Research',
20
+ 'Intended Audience :: Developers',
21
+ 'Programming Language :: Python :: 3',
22
+ 'Programming Language :: Python :: 3.10',
23
+ 'Programming Language :: Python :: 3.11',
24
+ 'Programming Language :: Python :: 3.12',
25
+ 'Programming Language :: Python :: 3.13',
26
+ 'Programming Language :: Python :: 3.14',
27
+ 'Topic :: Software Development',
28
+ 'Topic :: Software Development :: Libraries',
29
+ 'Topic :: Software Development :: Libraries :: Python Modules',
30
+ 'Topic :: Scientific/Engineering',
31
+ 'Topic :: Scientific/Engineering :: Information Analysis',
32
+ 'Operating System :: Microsoft :: Windows',
33
+ 'Operating System :: POSIX',
34
+ 'Operating System :: Unix',
35
+ 'Operating System :: MacOS']
36
+ dependencies = [
37
+ 'pcollections >= 1.0.0rc1',
38
+ 'numpy >= 1.24.0',
39
+ # The sparse *array* classes (scipy.sparse.csr_array, etc.) that
40
+ # immlib.util._numeric uses to describe sparse layouts were added in
41
+ # scipy 1.8.0.
42
+ 'scipy >= 1.8.0',
43
+ # immlib.Quantity subclasses pint.Quantity and overrides some of Pint's
44
+ # private methods and attributes (see immlib.util._quantity), so an upper
45
+ # bound is kept to the minor series it has been tested against. The test
46
+ # suite passes against pint 0.24.4, 0.25.3, and 0.26.1 (see the "Pint
47
+ # contract" tests in immlib.test.util.test_pint); note that pint 0.25+
48
+ # requires Python 3.11 and pint 0.26+ requires Python 3.12, so a Python
49
+ # 3.10 install resolves to pint 0.24.x.
50
+ 'pint >= 0.24.0, < 0.27',
51
+ 'docshare >= 0.2.0',
52
+ 'joblib >= 1.3.0',
53
+ # immlib reaches into a few cloudpathlib internals (notably the private
54
+ # Client._local_cache_dir, and the CloudPath._no_prefix helpers), so an
55
+ # upper bound is kept to the minor series it has been tested against
56
+ # (0.25.0).
57
+ 'cloudpathlib[s3,gs,azure] >= 0.18.0, < 0.26',
58
+ 'pyyaml >= 6.0']
59
+
60
+ [project.optional-dependencies]
61
+ dev = ["torch >= 2.2.0", "pandas >= 1.5.0", "mypy >= 1.8"]
62
+ # pandas is needed only by the csv/tsv formats of immlib.save/immlib.load, so
63
+ # it is an optional dependency of the library and a test dependency of the
64
+ # suite (which exercises those formats when pandas is present).
65
+ test = ["torch >= 2.2.0", "pandas >= 1.5.0"]
66
+ pandas = ["pandas >= 1.5.0"]
67
+ # Jupyter Book 2 builds with mystmd and uses its own myst.yml configuration
68
+ # (the docs were migrated from Jupyter Book 1); the API-reference pages are
69
+ # generated by docs/generate_api.py before the book is built.
70
+ docs = ["torch >= 2.2.0", "matplotlib >= 3.4.0", "jupyter-book >= 2"]
71
+
72
+ [tool.setuptools.package-data]
73
+ # Distribute the PEP 561 marker alongside the package, so that type checkers
74
+ # know immlib's inline annotations are usable.
75
+ immlib = ["py.typed"]
76
+
77
+ [tool.mypy]
78
+ # immlib is annotated progressively, so the checker is kept lenient on bodies
79
+ # that are not annotated yet; the point of running it in CI is to keep the
80
+ # annotations that do exist honest, not to require every function to have them.
81
+ #
82
+ # The target Python version is deliberately not pinned: mypy then follows the
83
+ # interpreter that runs it. Pinning it to an older version makes mypy parse
84
+ # with that version's grammar, which fails on dependency stubs that use newer
85
+ # syntax (numpy's stubs use PEP 695 `type` statements, which need 3.12+), and
86
+ # the CI matrix checks the annotations under more than one interpreter.
87
+ files = ["src/immlib"]
88
+ ignore_missing_imports = true
89
+ check_untyped_defs = false
90
+ warn_unused_ignores = false
91
+
92
+ [project.urls]
93
+ homepage = "https://github.com/noahbenson/immlib"
94
+ documentation = "https://github.com/noahbenson/immlib"
95
+ repository = "https://github.com/noahbenson/immlib"
@@ -33,37 +33,16 @@ submodules : tuple of str
33
33
  A tuple of strings, each of which is the name of one of the submodules in
34
34
  ``immlib``. The modules are listed in load-order and all ``immlib``
35
35
  submodules, including private submodules, are included.
36
- docproc: docrep.DocstringProcessor object
37
- This object is used to process all of the doc-strings in the ``immlib``
38
- library; it should be used only with the ``immlib.docwrap`` decorator,
39
- which can safely be applied anywhere in a sequence of decorators and which
40
- correctly applies the ``wraps`` decorator to its argument. Function
41
- documentation is always processed using the ``sections=('Parameters',
42
- 'Returns', 'Raises', 'Examples', 'Inputs', 'Outputs')`` parameter and the
43
- ``with_indent(4)`` decorator. The base-name for the function ``f`` is
44
- ``f.__module__ + '.' + f.__name__``.
45
36
  '''
46
37
 
47
38
 
48
39
  # Imports #####################################################################
49
40
 
50
- # We always load _init first.
51
- from ._init import reclaim
52
- # Then the core library.
53
- from .doc import *
54
41
  from .util import *
55
42
  from .pathlib import *
56
43
  from .iolib import *
57
44
  from .workflow import *
58
- from .types import *
59
- # Import the Global UnitRegistry object to the global immlib scope. This is the
60
- # value that gets updated when one runs `immlib.default_ureg()`, and this is
61
- # the UnitRegistry that is used as the default registry for all ``immlib``
62
- # functions.
63
- from .util._quantity import _initial_global_ureg as units
64
- # Do the same for the global DocstringProcessor (from the docrep library) from
65
- # the doc subpackage.
66
- from .doc._core import _initial_global_docproc as docproc
45
+ from . import math
67
46
  # We want the version object from the ._version namespace; this is always last.
68
47
  from ._version import (version, Version)
69
48
 
@@ -71,9 +50,6 @@ from ._version import (version, Version)
71
50
  # Modules/Reloading ###########################################################
72
51
 
73
52
  submodules = (
74
- 'immlib._init',
75
- 'immlib.doc._core',
76
- 'immlib.doc',
77
53
  'immlib.util._core',
78
54
  'immlib.util._numeric',
79
55
  'immlib.util._quantity',
@@ -87,8 +63,8 @@ submodules = (
87
63
  'immlib.workflow._core',
88
64
  'immlib.workflow._plantype',
89
65
  'immlib.workflow',
90
- 'immlib.types._core',
91
- 'immlib.types',
66
+ 'immlib.math._core',
67
+ 'immlib.math',
92
68
  'immlib._version')
93
69
  def reload_immlib():
94
70
  """Reload and return the entire ``immlib`` package.
@@ -126,6 +102,22 @@ __all__ = tuple(
126
102
  if k[0] != '_'
127
103
  if k != 'submodules'
128
104
  if k != 'version'
129
- if ('immlib.' + k) not in submodules])
130
- # We want to mark our functions as being from the immlib module.
131
- reclaim(__name__, __all__, del_reclaim=True)
105
+ if ('immlib.' + k) not in submodules]
106
+ + ['units'])
107
+ # immlib.units is a property of the module: it returns the unit registry set
108
+ # by an enclosing immlib.default_ureg block in the current thread, if any, or
109
+ # the global default registry otherwise; assigning to it sets the global
110
+ # default registry.
111
+ import sys as _sys
112
+ import types as _types
113
+ class _ImmlibModule(_types.ModuleType):
114
+ @property
115
+ def units(self):
116
+ from .util._core import _default_ureg
117
+ return _default_ureg()
118
+ @units.setter
119
+ def units(self, ureg):
120
+ from .util._core import _global_ureg
121
+ _global_ureg[0] = ureg
122
+ _sys.modules[__name__].__class__ = _ImmlibModule
123
+ del _sys, _types
@@ -1,17 +1,21 @@
1
1
  # -*- coding: utf-8 -*-
2
- ################################################################################
2
+ ###############################################################################
3
3
  # immlib/_version.py
4
4
 
5
5
 
6
- # Dependencies #################################################################
6
+ # Dependencies ################################################################
7
+
8
+ from __future__ import annotations
7
9
 
8
10
  from ast import literal_eval
11
+ from typing import Iterator
9
12
  from collections import namedtuple
10
13
  from pathlib import Path
14
+ from os import PathLike
11
15
  from warnings import warn
12
16
 
13
17
 
14
- # Version Type #################################################################
18
+ # Version Type ################################################################
15
19
 
16
20
  VersionTuple = namedtuple(
17
21
  'VersionTuple',
@@ -20,50 +24,53 @@ class Version(VersionTuple):
20
24
  """A type that represents a Python package version.
21
25
 
22
26
  Python packages are represented simultaneously as version strings, version
23
- tuples, and by the version components `major`, `minor`, `micro`, and
24
- `stage`.
27
+ tuples, and by the version components ``major``, ``minor``, ``micro``, and
28
+ ``stage``.
25
29
 
26
30
  Parameters
27
31
  ----------
28
32
  string : str or None, optional
29
33
  The version string to be represented. If this argument is not provided,
30
- then one or both of the `package_name` and `pyproject_path` options must
31
- be provided so that the version string can be obtained via the package
32
- version or the `pyproject.toml` file.
34
+ then one or both of the ``package_name`` and ``pyproject_path`` options
35
+ must be provided so that the version string can be obtained via the
36
+ package version or the ``pyproject.toml`` file.
33
37
  package_name : str or None, optional
34
- If the first argument (`string`) is provided, then this argument is
38
+ If the first argument (``string``) is provided, then this argument is
35
39
  ignored; otherwise, the version string is first searched for by this
36
- package name using the `importlib` or `importlib_metadata` packages. If
37
- found, then this version string is represented in the `Version` object.
40
+ package name using the ``importlib.metadata`` package. If found, then
41
+ this version string is represented in the
42
+ ``Version`` object.
38
43
  pyproject_path : path-like or None, optional
39
- If the first argument (`string`) is not given and the `package_name` is
40
- not given, then the version is searched for in the `pyproject.toml` file
41
- given by this path. In order for such a file to be valid, it must
42
- contain a line that, when stripped of whitespace, begins with the string
43
- `'version='` followed by a string representation (e.g.,
44
- `'version="1.12.5"'`). If such a line is found in the `[project]`
45
- section of the TOM: file pointed to by this argument, then it is
46
- represented as the version string in the `Version` object.
44
+ If the first argument (``string``) is not given and the
45
+ ``package_name`` is not given, then the version is searched for in the
46
+ ``pyproject.toml`` file given by this path. In order for such a file to
47
+ be valid, it must contain a line that, when stripped of whitespace,
48
+ begins with the string ``'version='`` followed by a string
49
+ representation (e.g., ``'version="1.12.5"'``). If such a line is found
50
+ in the ``[project]`` section of the TOM: file pointed to by this
51
+ argument, then it is represented as the version string in the
52
+ ``Version`` object.
47
53
  on_error : {'warn' | 'ignore' | 'raise'}, optional
48
54
  How to handle failures to deduce or parse the version number. If
49
- `'raise'` is given, then the errors are allowed to be raised. If
50
- `'warn'`, then a warning is raised and a null version is returned. If
51
- `'ignore'`, then errors are ignored and a null version is returned. The
52
- default is `'raise'`.
55
+ ``'raise'`` is given, then the errors are allowed to be raised. If
56
+ ``'warn'``, then a warning is raised and a null version is returned. If
57
+ ``'ignore'``, then errors are ignored and a null version is
58
+ returned. The default is ``'raise'``.
59
+
53
60
  tag_prefixes : tuple of str, optional
54
61
  An optional tuple of strings that can appear as the prefixes of stage
55
- tagss at the end of the version string. By default, this is `('rc', 'a',
56
- 'b')`, so version strings like `'1.1.12a6'` and `'1.1.12rc6'` are valid
57
- but `'1.1.12c6'` is not.
62
+ tagss at the end of the version string. By default, this is ``('rc',
63
+ 'a', 'b')``, so version strings like ``'1.1.12a6'`` and ``'1.1.12rc6'``
64
+ are valid but ``'1.1.12c6'`` is not.
58
65
 
59
66
  Attributes
60
67
  ----------
61
68
  string : str
62
- The string representing the package version. For example `"1.2.15"` or
63
- `"0.2.2.dev1"`.
69
+ The string representing the package version. For example ``"1.2.15"``
70
+ or ``"0.2.2.dev1"``.
64
71
  tuple : tuple of int and str
65
- The components of the version string, for example, `(1, 2, 15)` or
66
- `(0, 2, 2, 'dev1')`. Any missing component is excluded.
72
+ The components of the version string, for example, ``(1, 2, 15)`` or
73
+ ``(0, 2, 2, 'dev1')``. Any missing component is excluded.
67
74
  major : int
68
75
  The major version number, typically indicates major API version.
69
76
  minor : int
@@ -71,32 +78,33 @@ class Version(VersionTuple):
71
78
  micro : int
72
79
  The micro version number, typically indicates patch increment number.
73
80
  stage : str
74
- The development stage of the version. For example `'dev1'` or `'rc2'`.
81
+ The development stage of the version. For example ``'dev1'`` or
82
+ ``'rc2'``.
75
83
  """
76
84
 
77
- # Static Methods -----------------------------------------------------------
78
- def getstring(package_name=None, pyproject_path=None):
85
+ # Static Methods ----------------------------------------------------------
86
+ @staticmethod
87
+ def getstring(package_name: str | None = None,
88
+ pyproject_path: str | PathLike[str] | None = None) -> str:
79
89
  """Returns the current version string for the given package name.
80
90
 
81
- `Version.getstring(package_name)` returns the version string of the
91
+ ``Version.getstring(package_name)`` returns the version string of the
82
92
  package with the given package name.
83
93
 
84
- `Version.getstring(pyproject_path=path)` returns the version string
85
- found in the pyproject.toml file found at the given `path`.
94
+ ``Version.getstring(pyproject_path=path)`` returns the version string
95
+ found in the pyproject.toml file found at the given ``path``.
86
96
 
87
- `Version.getstring(package_name, path)` returns
88
- `Version.getstring(package_name)` if the given `package_name` is found,
89
- otherwise returns `Version.getstring(pyproject_path=path)`.
97
+ ``Version.getstring(package_name, path)`` returns
98
+ ``Version.getstring(package_name)`` if the given ``package_name`` is
99
+ found, otherwise returns ``Version.getstring(pyproject_path=path)``.
90
100
  """
91
101
  if package_name is None and pyproject_path is None:
92
102
  raise ValueError("Version.getstring() requires 1 or 2 arguments")
93
103
  if package_name is not None:
94
- try:
95
- from importlib.metadata import version
96
- from importlib.metadata import PackageNotFoundError
97
- except ModuleNotFoundError:
98
- from importlib_metadata import version
99
- from importlib_metadata import PackageNotFoundError
104
+ # immlib requires Python 3.10 or later, so importlib.metadata is
105
+ # always available (the importlib_metadata backport is not needed).
106
+ from importlib.metadata import version
107
+ from importlib.metadata import PackageNotFoundError
100
108
  if pyproject_path is None:
101
109
  return version(package_name)
102
110
  # Try to deduce the version string but don't raise if this fails.
@@ -106,13 +114,19 @@ class Version(VersionTuple):
106
114
  pass
107
115
  # Either a package name wasn't given or the package wasn't found; check
108
116
  # the pyproject.toml if possible.
109
- path = Path(pyproject_path)
117
+ path = Path(pyproject_path) # type: ignore[arg-type]
110
118
  with path.open('rt') as fl:
111
119
  toml_lines = fl.read().split('\n')
112
120
  in_project_section = False
113
121
  for ln in toml_lines:
114
122
  ln = ln.strip()
115
- if ln == '[project]':
123
+ if not ln or ln.startswith('#'):
124
+ # A blank line or a comment: neither starts a section nor
125
+ # declares anything. (The emptiness test must come first;
126
+ # ln[0] below would raise for a blank line, and every real
127
+ # pyproject.toml has blank lines in it.)
128
+ continue
129
+ elif ln == '[project]':
116
130
  in_project_section = True
117
131
  elif ln[0] == '[' and ln[-1] == ']':
118
132
  in_project_section = False
@@ -131,7 +145,7 @@ class Version(VersionTuple):
131
145
  f"Version.getstring() found no 'version = ...' line in file"
132
146
  f" {path}")
133
147
 
134
- # Construction -------------------------------------------------------------
148
+ # Construction ------------------------------------------------------------
135
149
  __slots__ = ()
136
150
  null = None
137
151
  def __new__(cls, string=None, /, *,
@@ -172,7 +186,7 @@ class Version(VersionTuple):
172
186
  micro = '0'
173
187
  last = minor
174
188
  elif nss == 1:
175
- major = ss
189
+ (major,) = ss
176
190
  (minor, micro) = ('0', '0')
177
191
  last = major
178
192
  else:
@@ -203,20 +217,20 @@ class Version(VersionTuple):
203
217
  minor=minor,
204
218
  micro=micro,
205
219
  stage=stage)
206
- def __str__(self):
220
+ def __str__(self) -> str:
207
221
  return self.string
208
- def __repr__(self):
222
+ def __repr__(self) -> str:
209
223
  return f"Version({repr(self.string)})"
210
- def __iter__(self):
224
+ def __iter__(self) -> Iterator[object]:
211
225
  return iter(self.tuple)
212
- def __reversed__(self):
226
+ def __reversed__(self) -> Iterator[object]:
213
227
  return reversed(self.tuple)
214
- def __contains__(self, k):
228
+ def __contains__(self, k: object) -> bool:
215
229
  if isinstance(k, str):
216
230
  return k in self.string
217
231
  else:
218
232
  return k in self.tuple
219
- Version.null = VersionTuple.__new__(
233
+ Version.null = VersionTuple.__new__( # type: ignore[assignment]
220
234
  Version,
221
235
  string='',
222
236
  tuple=(),
@@ -226,7 +240,7 @@ Version.null = VersionTuple.__new__(
226
240
  stage=None)
227
241
 
228
242
 
229
- # Variables ####################################################################
243
+ # Variables ###################################################################
230
244
 
231
245
  # The path of immlib's pyproject.toml file.
232
246
  pyproject_path = Path(__file__).parent.parent.parent / 'pyproject.toml'
@@ -1,10 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  ################################################################################
3
- # pimms/iolib/__init__.py
3
+ # immlib/iolib/__init__.py
4
4
 
5
- """Input/output tools managed by pimms; primarily the save and load functions.
5
+ """Input/output tools managed by immlib; primarily the save and load functions.
6
6
 
7
- The `pimms.iolib` module contains tools for saving and loading data to/from
7
+ The `immlib.iolib` module contains tools for saving and loading data to/from
8
8
  paths or streams. This functionality is primarily supported via the `save` and
9
9
  `load` objects that behave as general (de)serializers to which formats can be
10
10
  registered.
@@ -21,9 +21,3 @@ __all__ = (
21
21
  'save',
22
22
  'Load',
23
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__