deriva-ml 1.17.10__py3-none-any.whl → 1.17.12__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.
- deriva_ml/__init__.py +69 -1
- deriva_ml/asset/__init__.py +17 -0
- deriva_ml/asset/asset.py +357 -0
- deriva_ml/asset/aux_classes.py +100 -0
- deriva_ml/bump_version.py +254 -11
- deriva_ml/catalog/__init__.py +31 -0
- deriva_ml/catalog/clone.py +1939 -0
- deriva_ml/catalog/localize.py +426 -0
- deriva_ml/core/__init__.py +29 -0
- deriva_ml/core/base.py +845 -1067
- deriva_ml/core/config.py +169 -21
- deriva_ml/core/constants.py +120 -19
- deriva_ml/core/definitions.py +123 -13
- deriva_ml/core/enums.py +47 -73
- deriva_ml/core/ermrest.py +226 -193
- deriva_ml/core/exceptions.py +297 -14
- deriva_ml/core/filespec.py +99 -28
- deriva_ml/core/logging_config.py +225 -0
- deriva_ml/core/mixins/__init__.py +42 -0
- deriva_ml/core/mixins/annotation.py +915 -0
- deriva_ml/core/mixins/asset.py +384 -0
- deriva_ml/core/mixins/dataset.py +237 -0
- deriva_ml/core/mixins/execution.py +408 -0
- deriva_ml/core/mixins/feature.py +365 -0
- deriva_ml/core/mixins/file.py +263 -0
- deriva_ml/core/mixins/path_builder.py +145 -0
- deriva_ml/core/mixins/rid_resolution.py +204 -0
- deriva_ml/core/mixins/vocabulary.py +400 -0
- deriva_ml/core/mixins/workflow.py +322 -0
- deriva_ml/core/validation.py +389 -0
- deriva_ml/dataset/__init__.py +2 -1
- deriva_ml/dataset/aux_classes.py +20 -4
- deriva_ml/dataset/catalog_graph.py +575 -0
- deriva_ml/dataset/dataset.py +1242 -1008
- deriva_ml/dataset/dataset_bag.py +1311 -182
- deriva_ml/dataset/history.py +27 -14
- deriva_ml/dataset/upload.py +225 -38
- deriva_ml/demo_catalog.py +126 -110
- deriva_ml/execution/__init__.py +46 -2
- deriva_ml/execution/base_config.py +639 -0
- deriva_ml/execution/execution.py +543 -242
- deriva_ml/execution/execution_configuration.py +26 -11
- deriva_ml/execution/execution_record.py +592 -0
- deriva_ml/execution/find_caller.py +298 -0
- deriva_ml/execution/model_protocol.py +175 -0
- deriva_ml/execution/multirun_config.py +153 -0
- deriva_ml/execution/runner.py +595 -0
- deriva_ml/execution/workflow.py +223 -34
- deriva_ml/experiment/__init__.py +8 -0
- deriva_ml/experiment/experiment.py +411 -0
- deriva_ml/feature.py +6 -1
- deriva_ml/install_kernel.py +143 -6
- deriva_ml/interfaces.py +862 -0
- deriva_ml/model/__init__.py +99 -0
- deriva_ml/model/annotations.py +1278 -0
- deriva_ml/model/catalog.py +286 -60
- deriva_ml/model/database.py +144 -649
- deriva_ml/model/deriva_ml_database.py +308 -0
- deriva_ml/model/handles.py +14 -0
- deriva_ml/run_model.py +319 -0
- deriva_ml/run_notebook.py +507 -38
- deriva_ml/schema/__init__.py +18 -2
- deriva_ml/schema/annotations.py +62 -33
- deriva_ml/schema/create_schema.py +169 -69
- deriva_ml/schema/validation.py +601 -0
- {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.12.dist-info}/METADATA +4 -4
- deriva_ml-1.17.12.dist-info/RECORD +77 -0
- {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.12.dist-info}/WHEEL +1 -1
- {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.12.dist-info}/entry_points.txt +1 -0
- deriva_ml/protocols/dataset.py +0 -19
- deriva_ml/test.py +0 -94
- deriva_ml-1.17.10.dist-info/RECORD +0 -45
- {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.12.dist-info}/licenses/LICENSE +0 -0
- {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.12.dist-info}/top_level.txt +0 -0
deriva_ml/bump_version.py
CHANGED
|
@@ -1,13 +1,141 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
|
|
2
|
+
"""Release helper for semantic versioning with git tags.
|
|
3
|
+
|
|
4
|
+
This module provides a command-line tool for managing semantic version tags in a
|
|
5
|
+
git repository. It handles both initial version seeding and version bumping using
|
|
6
|
+
the bump-my-version tool.
|
|
7
|
+
|
|
8
|
+
Semantic Versioning
|
|
9
|
+
-------------------
|
|
10
|
+
This tool follows semantic versioning (semver) conventions:
|
|
11
|
+
|
|
12
|
+
- **major**: Increment for incompatible API changes (1.0.0 -> 2.0.0)
|
|
13
|
+
- **minor**: Increment for new functionality in a backward-compatible manner (1.0.0 -> 1.1.0)
|
|
14
|
+
- **patch**: Increment for backward-compatible bug fixes (1.0.0 -> 1.0.1)
|
|
15
|
+
|
|
16
|
+
How It Works
|
|
17
|
+
------------
|
|
18
|
+
1. If no semver tag exists, creates an initial tag (default: v0.1.0)
|
|
19
|
+
2. If a tag exists, uses bump-my-version to increment the specified component
|
|
20
|
+
3. Pushes the new tag and any commits to the remote repository
|
|
21
|
+
|
|
22
|
+
Dynamic Versioning with setuptools_scm
|
|
23
|
+
--------------------------------------
|
|
24
|
+
This project uses **setuptools_scm** to derive the package version dynamically
|
|
25
|
+
from git tags. This means there is no hardcoded version string in the source
|
|
26
|
+
code - the version is always determined from the most recent git tag.
|
|
27
|
+
|
|
28
|
+
**How it works:**
|
|
29
|
+
|
|
30
|
+
1. When the package is built or installed, setuptools_scm reads the git history
|
|
31
|
+
2. It finds the most recent tag matching the semver pattern (e.g., ``v1.2.3``)
|
|
32
|
+
3. The version is derived from that tag, with additional metadata for commits
|
|
33
|
+
since the tag
|
|
34
|
+
|
|
35
|
+
**Version formats:**
|
|
36
|
+
|
|
37
|
+
- **At a tag**: If HEAD is exactly at tag ``v1.2.3``, version is ``1.2.3``
|
|
38
|
+
- **After a tag**: If there are commits after the tag, version includes distance
|
|
39
|
+
and commit hash, e.g., ``1.2.3.post2+g1234abc`` (2 commits after v1.2.3)
|
|
40
|
+
- **Dirty working tree**: Adds ``.dirty`` suffix if uncommitted changes exist
|
|
41
|
+
|
|
42
|
+
**Configuration in pyproject.toml**::
|
|
43
|
+
|
|
44
|
+
[project]
|
|
45
|
+
dynamic = ["version"] # Version is not hardcoded
|
|
46
|
+
|
|
47
|
+
[build-system]
|
|
48
|
+
requires = ["setuptools>=80", "setuptools_scm[toml]>=8", "wheel"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools_scm]
|
|
51
|
+
version_scheme = "post-release" # Use .postN for commits after a tag
|
|
52
|
+
|
|
53
|
+
**Accessing the version at runtime**::
|
|
54
|
+
|
|
55
|
+
from importlib.metadata import version
|
|
56
|
+
__version__ = version("deriva_ml") # e.g., "1.2.3" or "1.2.3.post2+g1234abc"
|
|
57
|
+
|
|
58
|
+
**Why this approach:**
|
|
59
|
+
|
|
60
|
+
- Single source of truth: The git tag IS the version
|
|
61
|
+
- No manual version updates in source files
|
|
62
|
+
- Automatic dev versions between releases
|
|
63
|
+
- Works seamlessly with CI/CD pipelines
|
|
64
|
+
- Released versions are always clean (e.g., ``1.2.3``)
|
|
65
|
+
|
|
66
|
+
Requirements
|
|
67
|
+
------------
|
|
68
|
+
- git: Version control system
|
|
69
|
+
- uv: Python package manager (used to run bump-my-version)
|
|
70
|
+
- bump-my-version: Configured in pyproject.toml
|
|
71
|
+
|
|
72
|
+
Configuration
|
|
73
|
+
-------------
|
|
74
|
+
The tool can be configured via environment variables:
|
|
75
|
+
|
|
76
|
+
- **START**: Initial version if no tag exists (default: "0.1.0")
|
|
77
|
+
- **PREFIX**: Tag prefix (default: "v")
|
|
78
|
+
|
|
79
|
+
The bump-my-version tool should be configured in pyproject.toml with the
|
|
80
|
+
appropriate version locations and tag format (see ``[tool.bumpversion]`` section).
|
|
81
|
+
|
|
82
|
+
Usage
|
|
83
|
+
-----
|
|
84
|
+
Command line::
|
|
85
|
+
|
|
86
|
+
# Bump patch version (default): v1.0.0 -> v1.0.1
|
|
87
|
+
python bump_version.py
|
|
88
|
+
|
|
89
|
+
# Bump minor version: v1.0.0 -> v1.1.0
|
|
90
|
+
python bump_version.py minor
|
|
91
|
+
|
|
92
|
+
# Bump major version: v1.0.0 -> v2.0.0
|
|
93
|
+
python bump_version.py major
|
|
94
|
+
|
|
95
|
+
# Use custom initial version
|
|
96
|
+
START=1.0.0 python bump_version.py
|
|
97
|
+
|
|
98
|
+
As a module::
|
|
99
|
+
|
|
100
|
+
from deriva_ml.bump_version import main
|
|
101
|
+
exit_code = main()
|
|
102
|
+
|
|
103
|
+
Examples
|
|
104
|
+
--------
|
|
105
|
+
First release of a new project::
|
|
106
|
+
|
|
107
|
+
$ python bump_version.py
|
|
108
|
+
Latest semver tag: None
|
|
109
|
+
No existing semver tag found. Seeding initial tag: v0.1.0
|
|
110
|
+
$ git tag v0.1.0 -m Initial release v0.1.0
|
|
111
|
+
$ git push --tags
|
|
112
|
+
Seeded v0.1.0. Done.
|
|
4
113
|
|
|
5
|
-
|
|
6
|
-
python release.py [patch|minor|major]
|
|
114
|
+
Releasing a bug fix::
|
|
7
115
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
116
|
+
$ python bump_version.py patch
|
|
117
|
+
Latest semver tag: v1.2.3
|
|
118
|
+
Bumping version: patch
|
|
119
|
+
$ uv run bump-my-version bump patch --verbose
|
|
120
|
+
...
|
|
121
|
+
$ git push --follow-tags
|
|
122
|
+
New version tag: v1.2.4
|
|
123
|
+
Release process complete!
|
|
124
|
+
|
|
125
|
+
Releasing a new feature::
|
|
126
|
+
|
|
127
|
+
$ python bump_version.py minor
|
|
128
|
+
Latest semver tag: v1.2.3
|
|
129
|
+
Bumping version: minor
|
|
130
|
+
...
|
|
131
|
+
New version tag: v1.3.0
|
|
132
|
+
Release process complete!
|
|
133
|
+
|
|
134
|
+
See Also
|
|
135
|
+
--------
|
|
136
|
+
- Semantic Versioning: https://semver.org/
|
|
137
|
+
- bump-my-version: https://github.com/callowayproject/bump-my-version
|
|
138
|
+
- setuptools_scm: https://github.com/pypa/setuptools_scm
|
|
11
139
|
"""
|
|
12
140
|
|
|
13
141
|
from __future__ import annotations
|
|
@@ -23,6 +151,24 @@ from typing import Sequence
|
|
|
23
151
|
def run(
|
|
24
152
|
cmd: Sequence[str], check: bool = True, capture: bool = False, quiet: bool = False
|
|
25
153
|
) -> subprocess.CompletedProcess:
|
|
154
|
+
"""Execute a shell command and optionally capture output.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
cmd: Command and arguments as a sequence of strings.
|
|
158
|
+
check: If True, raise CalledProcessError on non-zero exit code.
|
|
159
|
+
capture: If True, capture stdout and stderr.
|
|
160
|
+
quiet: If True, don't print the command being executed.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
CompletedProcess instance with return code and optionally captured output.
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
subprocess.CalledProcessError: If check=True and the command fails.
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
>>> result = run(["git", "status"], capture=True)
|
|
170
|
+
>>> print(result.stdout)
|
|
171
|
+
"""
|
|
26
172
|
if not quiet:
|
|
27
173
|
print(f"$ {' '.join(cmd)}")
|
|
28
174
|
return subprocess.run(
|
|
@@ -34,22 +180,58 @@ def run(
|
|
|
34
180
|
|
|
35
181
|
|
|
36
182
|
def in_git_repo() -> bool:
|
|
183
|
+
"""Check if the current directory is inside a git repository.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
True if inside a git working tree, False otherwise.
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
>>> if not in_git_repo():
|
|
190
|
+
... print("Not a git repository")
|
|
191
|
+
"""
|
|
37
192
|
try:
|
|
38
|
-
run(["git", "rev-parse", "--is-inside-work-tree"], capture=True)
|
|
193
|
+
run(["git", "rev-parse", "--is-inside-work-tree"], capture=True, quiet=True)
|
|
39
194
|
return True
|
|
40
195
|
except subprocess.CalledProcessError:
|
|
41
196
|
return False
|
|
42
197
|
|
|
43
198
|
|
|
44
199
|
def has_commits() -> bool:
|
|
200
|
+
"""Check if the repository has at least one commit.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
True if the repository has commits, False if it's empty.
|
|
204
|
+
|
|
205
|
+
Example:
|
|
206
|
+
>>> if not has_commits():
|
|
207
|
+
... print("Repository has no commits yet")
|
|
208
|
+
"""
|
|
45
209
|
try:
|
|
46
|
-
run(["git", "log", "-1"], capture=True)
|
|
210
|
+
run(["git", "log", "-1"], capture=True, quiet=True)
|
|
47
211
|
return True
|
|
48
212
|
except subprocess.CalledProcessError:
|
|
49
213
|
return False
|
|
50
214
|
|
|
51
215
|
|
|
52
216
|
def latest_semver_tag(prefix: str) -> str | None:
|
|
217
|
+
"""Find the most recent semantic version tag in the repository.
|
|
218
|
+
|
|
219
|
+
Searches for tags matching the pattern ``{prefix}X.Y.Z`` where X, Y, and Z
|
|
220
|
+
are version numbers. Uses git describe to find the most recent matching tag.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
prefix: The tag prefix to match (e.g., "v" for tags like "v1.0.0").
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
The full tag string (e.g., "v1.2.3") if found, None if no matching tag exists.
|
|
227
|
+
|
|
228
|
+
Example:
|
|
229
|
+
>>> tag = latest_semver_tag("v")
|
|
230
|
+
>>> if tag:
|
|
231
|
+
... print(f"Current version: {tag}")
|
|
232
|
+
... else:
|
|
233
|
+
... print("No version tag found")
|
|
234
|
+
"""
|
|
53
235
|
# Use git's matcher to keep parity with Bash: prefix + x.y.z
|
|
54
236
|
pattern = f"{prefix}[0-9]*.[0-9]*.[0-9]*"
|
|
55
237
|
try:
|
|
@@ -61,6 +243,20 @@ def latest_semver_tag(prefix: str) -> str | None:
|
|
|
61
243
|
|
|
62
244
|
|
|
63
245
|
def seed_initial_tag(tag: str) -> None:
|
|
246
|
+
"""Create and push the initial version tag for a new project.
|
|
247
|
+
|
|
248
|
+
This is called when no semantic version tag exists in the repository.
|
|
249
|
+
Creates an annotated tag with a release message and pushes it to the remote.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
tag: The full tag string to create (e.g., "v0.1.0").
|
|
253
|
+
|
|
254
|
+
Example:
|
|
255
|
+
>>> seed_initial_tag("v0.1.0")
|
|
256
|
+
No existing semver tag found. Seeding initial tag: v0.1.0
|
|
257
|
+
$ git tag v0.1.0 -m Initial release v0.1.0
|
|
258
|
+
$ git push --tags
|
|
259
|
+
"""
|
|
64
260
|
print(f"No existing semver tag found. Seeding initial tag: {tag}")
|
|
65
261
|
run(["git", "tag", tag, "-m", f"Initial release {tag}"])
|
|
66
262
|
# Push tags (ignore failure to keep parity with bash's simple flow)
|
|
@@ -68,12 +264,59 @@ def seed_initial_tag(tag: str) -> None:
|
|
|
68
264
|
|
|
69
265
|
|
|
70
266
|
def require_tool(name: str) -> None:
|
|
267
|
+
"""Verify that a required command-line tool is available.
|
|
268
|
+
|
|
269
|
+
Checks if the specified tool exists on the system PATH. If not found,
|
|
270
|
+
prints an error message and exits the program.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
name: Name of the tool to check (e.g., "git", "uv").
|
|
274
|
+
|
|
275
|
+
Raises:
|
|
276
|
+
SystemExit: If the tool is not found on PATH.
|
|
277
|
+
|
|
278
|
+
Example:
|
|
279
|
+
>>> require_tool("git") # Passes silently if git is installed
|
|
280
|
+
>>> require_tool("nonexistent")
|
|
281
|
+
Error: required tool 'nonexistent' not found on PATH.
|
|
282
|
+
"""
|
|
71
283
|
if shutil.which(name) is None:
|
|
72
284
|
print(f"Error: required tool '{name}' not found on PATH.", file=sys.stderr)
|
|
73
285
|
sys.exit(1)
|
|
74
286
|
|
|
75
287
|
|
|
76
288
|
def main() -> int:
|
|
289
|
+
"""Main entry point for the version bumping tool.
|
|
290
|
+
|
|
291
|
+
Parses command-line arguments and orchestrates the version bump process:
|
|
292
|
+
|
|
293
|
+
1. Validates the environment (git repo, required tools)
|
|
294
|
+
2. Fetches existing tags from remote
|
|
295
|
+
3. Either seeds an initial tag or bumps the existing version
|
|
296
|
+
4. Pushes changes to the remote repository
|
|
297
|
+
|
|
298
|
+
The bump type can be specified as a command-line argument:
|
|
299
|
+
|
|
300
|
+
- ``patch`` (default): Bug fixes, backward-compatible (1.0.0 -> 1.0.1)
|
|
301
|
+
- ``minor``: New features, backward-compatible (1.0.0 -> 1.1.0)
|
|
302
|
+
- ``major``: Breaking changes (1.0.0 -> 2.0.0)
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Exit code: 0 on success, non-zero on failure.
|
|
306
|
+
|
|
307
|
+
Environment Variables:
|
|
308
|
+
START: Initial version if no tag exists (default: "0.1.0")
|
|
309
|
+
PREFIX: Tag prefix (default: "v")
|
|
310
|
+
|
|
311
|
+
Example:
|
|
312
|
+
>>> # Called from command line
|
|
313
|
+
>>> # python bump_version.py minor
|
|
314
|
+
|
|
315
|
+
>>> # Called programmatically
|
|
316
|
+
>>> import sys
|
|
317
|
+
>>> sys.argv = ["bump_version.py", "patch"]
|
|
318
|
+
>>> exit_code = main()
|
|
319
|
+
"""
|
|
77
320
|
parser = argparse.ArgumentParser(
|
|
78
321
|
description="Set a new version tag for the current repository, and push to remote."
|
|
79
322
|
)
|
|
@@ -95,7 +338,7 @@ def main() -> int:
|
|
|
95
338
|
|
|
96
339
|
# Ensure tags visible in shallow clones
|
|
97
340
|
try:
|
|
98
|
-
run(["git", "fetch", "--tags", "--quiet"], check=False)
|
|
341
|
+
run(["git", "fetch", "--tags", "--quiet"], check=False, quiet=True)
|
|
99
342
|
except Exception:
|
|
100
343
|
pass # non-fatal
|
|
101
344
|
|
|
@@ -128,7 +371,7 @@ def main() -> int:
|
|
|
128
371
|
|
|
129
372
|
# Retrieve new version tag
|
|
130
373
|
try:
|
|
131
|
-
cp = run(["git", "describe", "--tags", "--abbrev=0"], capture=True)
|
|
374
|
+
cp = run(["git", "describe", "--tags", "--abbrev=0"], capture=True, quiet=True)
|
|
132
375
|
new_tag = cp.stdout.strip()
|
|
133
376
|
print(f"New version tag: {new_tag}")
|
|
134
377
|
except subprocess.CalledProcessError:
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Catalog management utilities for DerivaML."""
|
|
2
|
+
|
|
3
|
+
from deriva_ml.catalog.clone import (
|
|
4
|
+
AssetCopyMode,
|
|
5
|
+
AssetFilter,
|
|
6
|
+
CatalogCreationMethod,
|
|
7
|
+
CatalogProvenance,
|
|
8
|
+
CloneCatalogResult,
|
|
9
|
+
CloneDetails,
|
|
10
|
+
clone_catalog,
|
|
11
|
+
get_catalog_provenance,
|
|
12
|
+
set_catalog_provenance,
|
|
13
|
+
)
|
|
14
|
+
from deriva_ml.catalog.localize import (
|
|
15
|
+
LocalizeResult,
|
|
16
|
+
localize_assets,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"AssetCopyMode",
|
|
21
|
+
"AssetFilter",
|
|
22
|
+
"CatalogCreationMethod",
|
|
23
|
+
"CatalogProvenance",
|
|
24
|
+
"CloneCatalogResult",
|
|
25
|
+
"CloneDetails",
|
|
26
|
+
"LocalizeResult",
|
|
27
|
+
"clone_catalog",
|
|
28
|
+
"get_catalog_provenance",
|
|
29
|
+
"localize_assets",
|
|
30
|
+
"set_catalog_provenance",
|
|
31
|
+
]
|