stringjax-tools 0.1.0__tar.gz → 0.2.0__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 (26) hide show
  1. {stringjax_tools-0.1.0/src/stringjax_tools.egg-info → stringjax_tools-0.2.0}/PKG-INFO +47 -11
  2. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/README.md +39 -3
  3. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/pyproject.toml +8 -7
  4. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/__init__.py +9 -2
  5. stringjax_tools-0.2.0/src/stringjax_tools/_worker.py +112 -0
  6. stringjax_tools-0.2.0/src/stringjax_tools/process.py +312 -0
  7. stringjax_tools-0.2.0/src/stringjax_tools/reproducibility.py +230 -0
  8. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0/src/stringjax_tools.egg-info}/PKG-INFO +47 -11
  9. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools.egg-info/SOURCES.txt +5 -0
  10. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/tests/test_auto_vectorise.py +5 -0
  11. stringjax_tools-0.2.0/tests/test_process.py +424 -0
  12. stringjax_tools-0.2.0/tests/test_reproducibility.py +166 -0
  13. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/LICENSE +0 -0
  14. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/setup.cfg +0 -0
  15. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/__main__.py +0 -0
  16. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/_utils.py +0 -0
  17. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/auto_vectorise.py +0 -0
  18. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/cache.py +0 -0
  19. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/jit.py +0 -0
  20. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/py.typed +0 -0
  21. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/pytrees.py +0 -0
  22. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools/vmap.py +0 -0
  23. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools.egg-info/dependency_links.txt +0 -0
  24. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools.egg-info/requires.txt +0 -0
  25. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/src/stringjax_tools.egg-info/top_level.txt +0 -0
  26. {stringjax_tools-0.1.0 → stringjax_tools-0.2.0}/tests/test_transforms_and_pytrees.py +0 -0
@@ -1,22 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stringjax-tools
3
- Version: 0.1.0
4
- Summary: Reusable JAX transform, vectorisation, cache, and pytree helpers for StringJAX packages.
3
+ Version: 0.2.0
4
+ Summary: Reusable JAX transform, vectorisation, cache and pytree helpers, and general-purpose infrastructure (child processes with enforced timeouts, provenance), for StringJAX packages.
5
5
  Author-email: Andreas Schachner <as3475@cornell.edu>
6
6
  License-Expression: GPL-3.0-or-later
7
- Project-URL: Homepage, https://github.com/StringJAX/stringjax-tools
8
- Project-URL: Issues, https://github.com/StringJAX/stringjax-tools/issues
9
- Keywords: jax,vmap,jit,pytrees,automatic-vectorisation,scientific-computing
7
+ Project-URL: Homepage, https://github.com/StringJAX/stringjax_tools
8
+ Project-URL: Documentation, https://stringjax-tools.readthedocs.io
9
+ Project-URL: Changelog, https://github.com/StringJAX/stringjax_tools/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/StringJAX/stringjax_tools/issues
11
+ Keywords: jax,stringjax,vmap,jit,pytrees,automatic-vectorisation,scientific-computing
10
12
  Classifier: Development Status :: 3 - Alpha
11
13
  Classifier: Intended Audience :: Developers
12
14
  Classifier: Intended Audience :: Science/Research
13
15
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.10
15
- Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Topic :: Scientific/Engineering
19
- Requires-Python: >=3.10
19
+ Requires-Python: >=3.12
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: jax>=0.4.26
@@ -44,13 +44,20 @@ Dynamic: license-file
44
44
  # StringJAX Tools
45
45
 
46
46
  `stringjax_tools` is a small utility package for reusable JAX transform
47
- patterns used across StringJAX projects. It currently focuses on:
47
+ patterns used across [StringJAX](https://github.com/StringJAX) projects. Use
48
+ it when a package needs the same JAX mechanics in several places, but should
49
+ not carry domain-specific names or conventions into a shared helper layer.
50
+
51
+ It currently focuses on:
48
52
 
49
53
  - conservative rank-checked automatic vectorisation;
50
54
  - manual cached `jax.jit(jax.vmap(...))` wrappers;
51
55
  - small helpers for explicit static-argument JIT compilation;
52
56
  - explicit JAX persistent-compilation-cache setup;
53
- - configurable pytree registration for stateful model classes.
57
+ - configurable pytree registration for stateful model classes;
58
+ - running a call in a child process with an enforced timeout
59
+ (`run_in_subprocess`);
60
+ - recording which code produced a result (`provenance`).
54
61
 
55
62
  The package keeps reusable transformation and pytree machinery separate from
56
63
  domain-specific StringJAX model code.
@@ -63,7 +70,7 @@ From this directory:
63
70
  pip install -e ".[dev,docs]"
64
71
  ```
65
72
 
66
- The intended published package name is:
73
+ From PyPI:
67
74
 
68
75
  ```bash
69
76
  pip install stringjax-tools
@@ -92,6 +99,29 @@ make -C documentation html SPHINXOPTS="-W -q"
92
99
  The feature notebooks are included under `documentation/source/notebooks/` and
93
100
  are linked from the Sphinx tutorials page.
94
101
 
102
+ Start with the compact feature tour:
103
+
104
+ ```text
105
+ documentation/source/notebooks/00_crucial_features.ipynb
106
+ ```
107
+
108
+ It shows the pieces most StringJAX packages usually need first:
109
+
110
+ - `auto_vmap` for scalar-or-leading-batch public methods;
111
+ - exact shape validation for package-local model conventions;
112
+ - `vmapping_func_cached` for explicit `in_axes` kernels;
113
+ - static-argument JIT helpers;
114
+ - `PytreePolicy` for stateful model classes;
115
+ - explicit persistent compilation-cache setup.
116
+
117
+ ## Package Boundary
118
+
119
+ `stringjax_tools` is intentionally application-agnostic. Consuming packages
120
+ should define their own shape names, static keys, ignored cache attributes and
121
+ state policies locally. For example, a downstream package can wrap
122
+ `stringjax_tools.auto_vmap` with package-specific defaults, while
123
+ `stringjax_tools` itself remains free of domain language.
124
+
95
125
  ## Automatic Vectorisation
96
126
 
97
127
  Keep the single-sample function explicit, then wrap it at the boundary where
@@ -143,6 +173,12 @@ class Model:
143
173
  String dimensions are resolved from a bound object, usually `self`. Derived
144
174
  dimensions should be callables rather than expression strings.
145
175
 
176
+ The same public API can be imported from the package root when convenient:
177
+
178
+ ```python
179
+ from stringjax_tools import auto_vmap
180
+ ```
181
+
146
182
  ## Manual Transform Helpers
147
183
 
148
184
  When a function already has a deliberate `in_axes` structure, use:
@@ -1,13 +1,20 @@
1
1
  # StringJAX Tools
2
2
 
3
3
  `stringjax_tools` is a small utility package for reusable JAX transform
4
- patterns used across StringJAX projects. It currently focuses on:
4
+ patterns used across [StringJAX](https://github.com/StringJAX) projects. Use
5
+ it when a package needs the same JAX mechanics in several places, but should
6
+ not carry domain-specific names or conventions into a shared helper layer.
7
+
8
+ It currently focuses on:
5
9
 
6
10
  - conservative rank-checked automatic vectorisation;
7
11
  - manual cached `jax.jit(jax.vmap(...))` wrappers;
8
12
  - small helpers for explicit static-argument JIT compilation;
9
13
  - explicit JAX persistent-compilation-cache setup;
10
- - configurable pytree registration for stateful model classes.
14
+ - configurable pytree registration for stateful model classes;
15
+ - running a call in a child process with an enforced timeout
16
+ (`run_in_subprocess`);
17
+ - recording which code produced a result (`provenance`).
11
18
 
12
19
  The package keeps reusable transformation and pytree machinery separate from
13
20
  domain-specific StringJAX model code.
@@ -20,7 +27,7 @@ From this directory:
20
27
  pip install -e ".[dev,docs]"
21
28
  ```
22
29
 
23
- The intended published package name is:
30
+ From PyPI:
24
31
 
25
32
  ```bash
26
33
  pip install stringjax-tools
@@ -49,6 +56,29 @@ make -C documentation html SPHINXOPTS="-W -q"
49
56
  The feature notebooks are included under `documentation/source/notebooks/` and
50
57
  are linked from the Sphinx tutorials page.
51
58
 
59
+ Start with the compact feature tour:
60
+
61
+ ```text
62
+ documentation/source/notebooks/00_crucial_features.ipynb
63
+ ```
64
+
65
+ It shows the pieces most StringJAX packages usually need first:
66
+
67
+ - `auto_vmap` for scalar-or-leading-batch public methods;
68
+ - exact shape validation for package-local model conventions;
69
+ - `vmapping_func_cached` for explicit `in_axes` kernels;
70
+ - static-argument JIT helpers;
71
+ - `PytreePolicy` for stateful model classes;
72
+ - explicit persistent compilation-cache setup.
73
+
74
+ ## Package Boundary
75
+
76
+ `stringjax_tools` is intentionally application-agnostic. Consuming packages
77
+ should define their own shape names, static keys, ignored cache attributes and
78
+ state policies locally. For example, a downstream package can wrap
79
+ `stringjax_tools.auto_vmap` with package-specific defaults, while
80
+ `stringjax_tools` itself remains free of domain language.
81
+
52
82
  ## Automatic Vectorisation
53
83
 
54
84
  Keep the single-sample function explicit, then wrap it at the boundary where
@@ -100,6 +130,12 @@ class Model:
100
130
  String dimensions are resolved from a bound object, usually `self`. Derived
101
131
  dimensions should be callables rather than expression strings.
102
132
 
133
+ The same public API can be imported from the package root when convenient:
134
+
135
+ ```python
136
+ from stringjax_tools import auto_vmap
137
+ ```
138
+
103
139
  ## Manual Transform Helpers
104
140
 
105
141
  When a function already has a deliberate `in_axes` structure, use:
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "stringjax-tools"
7
- version = "0.1.0"
8
- description = "Reusable JAX transform, vectorisation, cache, and pytree helpers for StringJAX packages."
7
+ version = "0.2.0"
8
+ description = "Reusable JAX transform, vectorisation, cache and pytree helpers, and general-purpose infrastructure (child processes with enforced timeouts, provenance), for StringJAX packages."
9
9
  readme = "README.md"
10
- requires-python = ">=3.10"
10
+ requires-python = ">=3.12"
11
11
  license = "GPL-3.0-or-later"
12
12
  license-files = ["LICENSE"]
13
13
  authors = [
@@ -15,6 +15,7 @@ authors = [
15
15
  ]
16
16
  keywords = [
17
17
  "jax",
18
+ "stringjax",
18
19
  "vmap",
19
20
  "jit",
20
21
  "pytrees",
@@ -26,8 +27,6 @@ classifiers = [
26
27
  "Intended Audience :: Developers",
27
28
  "Intended Audience :: Science/Research",
28
29
  "Programming Language :: Python :: 3",
29
- "Programming Language :: Python :: 3.10",
30
- "Programming Language :: Python :: 3.11",
31
30
  "Programming Language :: Python :: 3.12",
32
31
  "Programming Language :: Python :: 3.13",
33
32
  "Topic :: Scientific/Engineering",
@@ -60,8 +59,10 @@ docs = [
60
59
  ]
61
60
 
62
61
  [project.urls]
63
- Homepage = "https://github.com/StringJAX/stringjax-tools"
64
- Issues = "https://github.com/StringJAX/stringjax-tools/issues"
62
+ Homepage = "https://github.com/StringJAX/stringjax_tools"
63
+ Documentation = "https://stringjax-tools.readthedocs.io"
64
+ Changelog = "https://github.com/StringJAX/stringjax_tools/blob/main/CHANGELOG.md"
65
+ Issues = "https://github.com/StringJAX/stringjax_tools/issues"
65
66
 
66
67
  [tool.setuptools.packages.find]
67
68
  where = ["src"]
@@ -15,7 +15,7 @@
15
15
  # You should have received a copy of the GNU General Public License
16
16
  # along with StringJAX Tools. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
- """Reusable JAX transform helpers for StringJAX packages."""
18
+ """Reusable JAX transform helpers and general-purpose infrastructure for StringJAX packages."""
19
19
 
20
20
  from .auto_vectorise import (
21
21
  ArgSpec,
@@ -32,18 +32,21 @@ from .auto_vectorise import (
32
32
  )
33
33
  from .cache import configure_compilation_cache
34
34
  from .jit import is_static, jit_with_dynamic_static_args, jit_with_static_args
35
+ from .process import ChildProcessFailed, run_in_subprocess
35
36
  from .pytrees import (
36
37
  PytreePolicy,
37
38
  flatten_func,
38
39
  make_pytree_flatteners,
39
40
  unflatten_func_class,
40
41
  )
42
+ from .reproducibility import git_state, package_info, provenance
41
43
  from .vmap import clear_vmap_caches, vmapping_func, vmapping_func_cached
42
44
 
43
- __version__ = "0.1.0"
45
+ __version__ = "0.2.0"
44
46
 
45
47
  __all__ = [
46
48
  "ArgSpec",
49
+ "ChildProcessFailed",
47
50
  "PytreePolicy",
48
51
  "auto_vmap",
49
52
  "auto_vmap_default_shapes",
@@ -54,12 +57,16 @@ __all__ = [
54
57
  "flatten_func",
55
58
  "get_auto_vmap_default_shapes",
56
59
  "get_auto_vmap_defaults",
60
+ "git_state",
57
61
  "is_static",
58
62
  "jit_with_dynamic_static_args",
59
63
  "jit_with_static_args",
60
64
  "make_pytree_flatteners",
65
+ "package_info",
66
+ "provenance",
61
67
  "reset_auto_vmap_default_shapes",
62
68
  "reset_auto_vmap_defaults",
69
+ "run_in_subprocess",
63
70
  "set_auto_vmap_default_shapes",
64
71
  "set_auto_vmap_defaults",
65
72
  "unflatten_func_class",
@@ -0,0 +1,112 @@
1
+ # Copyright 2023-2026 Andreas Schachner
2
+ #
3
+ # This file is part of StringJAX Tools.
4
+ #
5
+ # StringJAX Tools is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # StringJAX Tools is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with StringJAX Tools. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ """Child process of :func:`stringjax_tools.process.run_in_subprocess`.
19
+
20
+ Run as a script, ``python _worker.py <task> <result> <lifeline fd>``, so that it does not import
21
+ the ``stringjax_tools`` package (nor JAX), only the module of the target. The task file holds two
22
+ pickles: ``(target, sys.path)``, where ``target`` is ``"module:qualname"``, and then
23
+ ``(args, kwargs)``, unpickled after ``sys.path`` is set; the file is removed once read. The
24
+ outcome is written atomically to ``<result>``: ``("ok", value, warnings)`` or ``("error",
25
+ exception type name, message, traceback)``; then the worker kills its process group, so nothing
26
+ the target started outlives the call. A daemon thread reads the lifeline pipe from the parent; at
27
+ end of file (the parent is gone) it removes the temporary directory and kills the process group.
28
+ The ``__main__`` guard is required: a target that starts workers with ``multiprocessing`` under
29
+ ``spawn`` (the macOS default) re-imports this file.
30
+ """
31
+
32
+ import os
33
+ import sys
34
+
35
+ # Run as a script, Python puts this script's directory (inside the package) first on the path;
36
+ # drop it before anything else is imported, so that no module of the package can shadow a
37
+ # top-level one. Only then: a ``multiprocessing`` worker started under ``spawn`` re-runs this
38
+ # file after restoring the parent's path, whose first entry must stay.
39
+ _HERE = os.path.dirname(os.path.abspath(__file__))
40
+ if sys.path and os.path.abspath(sys.path[0] or os.curdir) == _HERE:
41
+ del sys.path[0]
42
+
43
+ import importlib # noqa: E402
44
+ import pickle # noqa: E402
45
+ import shutil # noqa: E402
46
+ import signal # noqa: E402
47
+ import threading # noqa: E402
48
+ import traceback # noqa: E402
49
+ import warnings # noqa: E402
50
+
51
+
52
+ def _watch_lifeline(fd, tmp_dir):
53
+ """Once the parent's end of the pipe closes: remove ``tmp_dir``, kill this process group."""
54
+ try:
55
+ while os.read(fd, 1):
56
+ pass
57
+ except OSError:
58
+ pass
59
+ shutil.rmtree(tmp_dir, ignore_errors=True)
60
+ os.killpg(0, signal.SIGKILL)
61
+
62
+
63
+ def _resolve(target):
64
+ module_name, function_name = target.split(":", 1)
65
+ obj = importlib.import_module(module_name)
66
+ for part in function_name.split("."):
67
+ obj = getattr(obj, part)
68
+ return obj
69
+
70
+
71
+ def _describe(exc):
72
+ try:
73
+ return str(exc)
74
+ except Exception: # an exception whose __str__ fails
75
+ return f"<{type(exc).__name__}: str() failed>"
76
+
77
+
78
+ def _flush():
79
+ for stream in (sys.stdout, sys.stderr):
80
+ try:
81
+ stream.flush()
82
+ except Exception:
83
+ pass
84
+
85
+
86
+ def main(task_path, result_path, lifeline_fd):
87
+ threading.Thread(
88
+ target=_watch_lifeline, args=(lifeline_fd, os.path.dirname(task_path)), daemon=True
89
+ ).start()
90
+ try:
91
+ with open(task_path, "rb") as f:
92
+ target, path = pickle.load(f)
93
+ sys.path[:] = path
94
+ args, kwargs = pickle.load(f)
95
+ os.remove(task_path)
96
+ function = _resolve(target)
97
+ with warnings.catch_warnings(record=True) as caught:
98
+ warnings.simplefilter("always")
99
+ value = function(*args, **kwargs)
100
+ records = [(_describe(w.message), [c.__name__ for c in w.category.__mro__]) for w in caught]
101
+ payload = pickle.dumps(("ok", value, records))
102
+ except BaseException as e: # report everything to the parent, including an interrupt here
103
+ payload = pickle.dumps(("error", type(e).__name__, _describe(e), traceback.format_exc()))
104
+ with open(result_path + ".tmp", "wb") as f:
105
+ f.write(payload)
106
+ os.replace(result_path + ".tmp", result_path)
107
+ _flush()
108
+ os.killpg(0, signal.SIGKILL) # this process and anything the target left running
109
+
110
+
111
+ if __name__ == "__main__":
112
+ main(sys.argv[1], sys.argv[2], int(sys.argv[3]))