deriva-ml 1.17.10__py3-none-any.whl → 1.17.11__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 (74) hide show
  1. deriva_ml/__init__.py +43 -1
  2. deriva_ml/asset/__init__.py +17 -0
  3. deriva_ml/asset/asset.py +357 -0
  4. deriva_ml/asset/aux_classes.py +100 -0
  5. deriva_ml/bump_version.py +254 -11
  6. deriva_ml/catalog/__init__.py +21 -0
  7. deriva_ml/catalog/clone.py +1199 -0
  8. deriva_ml/catalog/localize.py +426 -0
  9. deriva_ml/core/__init__.py +29 -0
  10. deriva_ml/core/base.py +817 -1067
  11. deriva_ml/core/config.py +169 -21
  12. deriva_ml/core/constants.py +120 -19
  13. deriva_ml/core/definitions.py +123 -13
  14. deriva_ml/core/enums.py +47 -73
  15. deriva_ml/core/ermrest.py +226 -193
  16. deriva_ml/core/exceptions.py +297 -14
  17. deriva_ml/core/filespec.py +99 -28
  18. deriva_ml/core/logging_config.py +225 -0
  19. deriva_ml/core/mixins/__init__.py +42 -0
  20. deriva_ml/core/mixins/annotation.py +915 -0
  21. deriva_ml/core/mixins/asset.py +384 -0
  22. deriva_ml/core/mixins/dataset.py +237 -0
  23. deriva_ml/core/mixins/execution.py +408 -0
  24. deriva_ml/core/mixins/feature.py +365 -0
  25. deriva_ml/core/mixins/file.py +263 -0
  26. deriva_ml/core/mixins/path_builder.py +145 -0
  27. deriva_ml/core/mixins/rid_resolution.py +204 -0
  28. deriva_ml/core/mixins/vocabulary.py +400 -0
  29. deriva_ml/core/mixins/workflow.py +322 -0
  30. deriva_ml/core/validation.py +389 -0
  31. deriva_ml/dataset/__init__.py +2 -1
  32. deriva_ml/dataset/aux_classes.py +20 -4
  33. deriva_ml/dataset/catalog_graph.py +575 -0
  34. deriva_ml/dataset/dataset.py +1242 -1008
  35. deriva_ml/dataset/dataset_bag.py +1311 -182
  36. deriva_ml/dataset/history.py +27 -14
  37. deriva_ml/dataset/upload.py +225 -38
  38. deriva_ml/demo_catalog.py +126 -110
  39. deriva_ml/execution/__init__.py +46 -2
  40. deriva_ml/execution/base_config.py +639 -0
  41. deriva_ml/execution/execution.py +543 -242
  42. deriva_ml/execution/execution_configuration.py +26 -11
  43. deriva_ml/execution/execution_record.py +592 -0
  44. deriva_ml/execution/find_caller.py +298 -0
  45. deriva_ml/execution/model_protocol.py +175 -0
  46. deriva_ml/execution/multirun_config.py +153 -0
  47. deriva_ml/execution/runner.py +595 -0
  48. deriva_ml/execution/workflow.py +223 -34
  49. deriva_ml/experiment/__init__.py +8 -0
  50. deriva_ml/experiment/experiment.py +411 -0
  51. deriva_ml/feature.py +6 -1
  52. deriva_ml/install_kernel.py +143 -6
  53. deriva_ml/interfaces.py +862 -0
  54. deriva_ml/model/__init__.py +99 -0
  55. deriva_ml/model/annotations.py +1278 -0
  56. deriva_ml/model/catalog.py +286 -60
  57. deriva_ml/model/database.py +144 -649
  58. deriva_ml/model/deriva_ml_database.py +308 -0
  59. deriva_ml/model/handles.py +14 -0
  60. deriva_ml/run_model.py +319 -0
  61. deriva_ml/run_notebook.py +507 -38
  62. deriva_ml/schema/__init__.py +18 -2
  63. deriva_ml/schema/annotations.py +62 -33
  64. deriva_ml/schema/create_schema.py +169 -69
  65. deriva_ml/schema/validation.py +601 -0
  66. {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.11.dist-info}/METADATA +4 -4
  67. deriva_ml-1.17.11.dist-info/RECORD +77 -0
  68. {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.11.dist-info}/WHEEL +1 -1
  69. {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.11.dist-info}/entry_points.txt +1 -0
  70. deriva_ml/protocols/dataset.py +0 -19
  71. deriva_ml/test.py +0 -94
  72. deriva_ml-1.17.10.dist-info/RECORD +0 -45
  73. {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.11.dist-info}/licenses/LICENSE +0 -0
  74. {deriva_ml-1.17.10.dist-info → deriva_ml-1.17.11.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
- Release helper: seed initial semver tag if none exists, otherwise bump with bump-my-version.
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
- Usage:
6
- python release.py [patch|minor|major]
114
+ Releasing a bug fix::
7
115
 
8
- Env vars:
9
- START - initial version if no tag exists (default: 0.1.0)
10
- PREFIX - tag prefix (default: v)
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,21 @@
1
+ """Catalog management utilities for DerivaML."""
2
+
3
+ from deriva_ml.catalog.clone import (
4
+ AssetCopyMode,
5
+ AssetFilter,
6
+ CloneCatalogResult,
7
+ clone_catalog,
8
+ )
9
+ from deriva_ml.catalog.localize import (
10
+ LocalizeResult,
11
+ localize_assets,
12
+ )
13
+
14
+ __all__ = [
15
+ "AssetCopyMode",
16
+ "AssetFilter",
17
+ "CloneCatalogResult",
18
+ "LocalizeResult",
19
+ "clone_catalog",
20
+ "localize_assets",
21
+ ]