data-annotations 2.8.1__tar.gz → 2.9.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.
- {data_annotations-2.8.1 → data_annotations-2.9.0}/PKG-INFO +12 -6
- {data_annotations-2.8.1 → data_annotations-2.9.0}/README.md +11 -5
- {data_annotations-2.8.1 → data_annotations-2.9.0}/pyproject.toml +1 -1
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/answers.py +29 -21
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/annotate/__init__.py +69 -13
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/annotate/helpers.py +48 -12
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/common.py +7 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/LICENSE +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/__init__.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/_decorators.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/__init__.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/decorators.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/models.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/writers.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/__init__.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/answers.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/prompts.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/provenance_commands.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/publish.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/__init__.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/decorators.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/models.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/writers.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/__init__.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/decorators.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/git.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/models.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/__init__.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/chain.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/manifest.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/matching.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/sources.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/types.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/runtime.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/writers.py +0 -0
- {data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/publish.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: data-annotations
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: Annotate data artifacts with provenance and descriptions
|
|
5
5
|
Keywords: annotations,data,metadata,provenance,reproducibility
|
|
6
6
|
Author: Rodrigo C. G. Pena
|
|
@@ -30,6 +30,11 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
|
|
31
31
|
# data-annotations
|
|
32
32
|
|
|
33
|
+
[](https://pypi.org/project/data-annotations/)
|
|
34
|
+
[](https://ceda-unibas.gitlab.io/tools/data-annotations/)
|
|
35
|
+
[](https://gitlab.com/ceda-unibas/tools/data-annotations/-/blob/main/LICENSE)
|
|
36
|
+
[](https://gitlab.com/ceda-unibas/tools/data-annotations/-/pipelines)
|
|
37
|
+
|
|
33
38
|
`data-annotations` is a Python package for attaching provenance and structured
|
|
34
39
|
descriptions to the files and directories your workflows produce.
|
|
35
40
|
|
|
@@ -43,12 +48,13 @@ Optional Markdown README sidecars can be generated for human-readable summaries.
|
|
|
43
48
|
|
|
44
49
|
## Documentation
|
|
45
50
|
|
|
46
|
-
The full documentation is organized as a [Diátaxis](https://diataxis.fr/) site
|
|
47
|
-
|
|
51
|
+
The [full documentation](https://ceda-unibas.gitlab.io/tools/data-annotations/) is organized as a [Diátaxis](https://diataxis.fr/) site.
|
|
52
|
+
|
|
53
|
+
Other links:
|
|
48
54
|
|
|
49
|
-
- Source
|
|
50
|
-
- Changelog
|
|
51
|
-
- Work items
|
|
55
|
+
- [Source code](https://gitlab.com/ceda-unibas/tools/data-annotations)
|
|
56
|
+
- [Changelog](https://gitlab.com/ceda-unibas/tools/data-annotations/-/blob/main/CHANGELOG.md)
|
|
57
|
+
- [Work items](https://gitlab.com/ceda-unibas/tools/data-annotations/-/work_items)
|
|
52
58
|
|
|
53
59
|
## Installation
|
|
54
60
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# data-annotations
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/data-annotations/)
|
|
4
|
+
[](https://ceda-unibas.gitlab.io/tools/data-annotations/)
|
|
5
|
+
[](https://gitlab.com/ceda-unibas/tools/data-annotations/-/blob/main/LICENSE)
|
|
6
|
+
[](https://gitlab.com/ceda-unibas/tools/data-annotations/-/pipelines)
|
|
7
|
+
|
|
3
8
|
`data-annotations` is a Python package for attaching provenance and structured
|
|
4
9
|
descriptions to the files and directories your workflows produce.
|
|
5
10
|
|
|
@@ -13,12 +18,13 @@ Optional Markdown README sidecars can be generated for human-readable summaries.
|
|
|
13
18
|
|
|
14
19
|
## Documentation
|
|
15
20
|
|
|
16
|
-
The full documentation is organized as a [Diátaxis](https://diataxis.fr/) site
|
|
17
|
-
|
|
21
|
+
The [full documentation](https://ceda-unibas.gitlab.io/tools/data-annotations/) is organized as a [Diátaxis](https://diataxis.fr/) site.
|
|
22
|
+
|
|
23
|
+
Other links:
|
|
18
24
|
|
|
19
|
-
- Source
|
|
20
|
-
- Changelog
|
|
21
|
-
- Work items
|
|
25
|
+
- [Source code](https://gitlab.com/ceda-unibas/tools/data-annotations)
|
|
26
|
+
- [Changelog](https://gitlab.com/ceda-unibas/tools/data-annotations/-/blob/main/CHANGELOG.md)
|
|
27
|
+
- [Work items](https://gitlab.com/ceda-unibas/tools/data-annotations/-/work_items)
|
|
22
28
|
|
|
23
29
|
## Installation
|
|
24
30
|
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/answers.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
3
|
import shlex
|
|
4
|
-
from collections.abc import Mapping
|
|
4
|
+
from collections.abc import Iterable, Mapping
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any, Literal, TypeAlias
|
|
7
7
|
|
|
@@ -124,6 +124,32 @@ _EXPLICIT_PROVENANCE_OVERRIDE_FIELDS = {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
def _validate_runtime_inference_fields(values: Iterable[str]) -> list[str]:
|
|
128
|
+
normalized: list[str] = []
|
|
129
|
+
for value in values:
|
|
130
|
+
if value in _UNSUPPORTED_RUNTIME_INFERENCE_FIELDS:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
"runtime inference is not supported for "
|
|
133
|
+
f"provenance.{value}; provide it explicitly"
|
|
134
|
+
)
|
|
135
|
+
if value not in _RUNTIME_INFERENCE_FIELDS:
|
|
136
|
+
allowed = sorted(_RUNTIME_INFERENCE_FIELDS)
|
|
137
|
+
raise ValueError(
|
|
138
|
+
f"unknown runtime inference field {value!r}; "
|
|
139
|
+
"expected one of: " + ", ".join(allowed)
|
|
140
|
+
)
|
|
141
|
+
if value not in normalized:
|
|
142
|
+
normalized.append(value)
|
|
143
|
+
return normalized
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _expand_runtime_inference_fields(values: Iterable[str]) -> set[str]:
|
|
147
|
+
fields: set[str] = set()
|
|
148
|
+
for field in values:
|
|
149
|
+
fields.update(_RUNTIME_INFERENCE_GROUPS.get(field, {field}))
|
|
150
|
+
return fields
|
|
151
|
+
|
|
152
|
+
|
|
127
153
|
class ProvenanceAnswers(BaseModel):
|
|
128
154
|
"""Optional provenance overrides supplied through an answers payload."""
|
|
129
155
|
|
|
@@ -194,22 +220,7 @@ class ProvenanceAnswers(BaseModel):
|
|
|
194
220
|
@field_validator("infer_from_runtime")
|
|
195
221
|
@classmethod
|
|
196
222
|
def _validate_runtime_inference_fields(cls, values: list[str]) -> list[str]:
|
|
197
|
-
|
|
198
|
-
for value in values:
|
|
199
|
-
if value in _UNSUPPORTED_RUNTIME_INFERENCE_FIELDS:
|
|
200
|
-
raise ValueError(
|
|
201
|
-
"runtime inference is not supported for "
|
|
202
|
-
f"provenance.{value}; provide it explicitly"
|
|
203
|
-
)
|
|
204
|
-
if value not in _RUNTIME_INFERENCE_FIELDS:
|
|
205
|
-
allowed = sorted(_RUNTIME_INFERENCE_FIELDS)
|
|
206
|
-
raise ValueError(
|
|
207
|
-
f"unknown runtime inference field {value!r}; "
|
|
208
|
-
"expected one of: " + ", ".join(allowed)
|
|
209
|
-
)
|
|
210
|
-
if value not in normalized:
|
|
211
|
-
normalized.append(value)
|
|
212
|
-
return normalized
|
|
223
|
+
return _validate_runtime_inference_fields(values)
|
|
213
224
|
|
|
214
225
|
@model_validator(mode="after")
|
|
215
226
|
def _validate_runtime_inference_conflicts(self) -> "ProvenanceAnswers":
|
|
@@ -240,10 +251,7 @@ class ProvenanceAnswers(BaseModel):
|
|
|
240
251
|
def runtime_inference_fields(self) -> set[str]:
|
|
241
252
|
"""Expand runtime inference groups into concrete provenance fields."""
|
|
242
253
|
|
|
243
|
-
|
|
244
|
-
for field in self.infer_from_runtime:
|
|
245
|
-
fields.update(_RUNTIME_INFERENCE_GROUPS.get(field, {field}))
|
|
246
|
-
return fields
|
|
254
|
+
return _expand_runtime_inference_fields(self.infer_from_runtime)
|
|
247
255
|
|
|
248
256
|
|
|
249
257
|
class BaseAnswers(BaseModel):
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/annotate/__init__.py
RENAMED
|
@@ -11,6 +11,7 @@ from .helpers import (
|
|
|
11
11
|
_child_bundle_from_annotation,
|
|
12
12
|
_child_bundles_from_answers,
|
|
13
13
|
_collect_post_hoc_provenance_from_sources,
|
|
14
|
+
_documented_artifacts_from_discovered_files,
|
|
14
15
|
_documented_artifact_groups_from_answers,
|
|
15
16
|
_documented_artifacts_from_answers,
|
|
16
17
|
_load_directory_answers,
|
|
@@ -38,6 +39,7 @@ from ..common import (
|
|
|
38
39
|
InputValuesOption,
|
|
39
40
|
MaxChecksumBytesOption,
|
|
40
41
|
ParamValuesOption,
|
|
42
|
+
RuntimeInferenceValuesOption,
|
|
41
43
|
ScriptOption,
|
|
42
44
|
ScriptRepoPathOption,
|
|
43
45
|
Sha256Option,
|
|
@@ -125,6 +127,7 @@ def annotate_file_command(
|
|
|
125
127
|
source_path: SourcePathOption = None,
|
|
126
128
|
source_revision: SourceRevisionOption = None,
|
|
127
129
|
source_sha256: SourceSha256Option = None,
|
|
130
|
+
infer_from_runtime: RuntimeInferenceValuesOption = None,
|
|
128
131
|
checksum_policy: ChecksumPolicyOption = "auto",
|
|
129
132
|
max_checksum_bytes: MaxChecksumBytesOption = 10 * 1024**3,
|
|
130
133
|
sha256: Sha256Option = None,
|
|
@@ -146,8 +149,6 @@ def annotate_file_command(
|
|
|
146
149
|
file_answers.target if file_answers is not None else None,
|
|
147
150
|
expected_kind="file",
|
|
148
151
|
)
|
|
149
|
-
if not is_interactive and file_answers is None:
|
|
150
|
-
_error("--no-interactive requires --answers")
|
|
151
152
|
if not is_interactive and file_answers is not None:
|
|
152
153
|
try:
|
|
153
154
|
answer_files.require_complete_file_answers(
|
|
@@ -215,6 +216,7 @@ def annotate_file_command(
|
|
|
215
216
|
source_path=source_path,
|
|
216
217
|
source_revision=source_revision,
|
|
217
218
|
source_sha256=source_sha256,
|
|
219
|
+
infer_from_runtime=infer_from_runtime,
|
|
218
220
|
)
|
|
219
221
|
|
|
220
222
|
fields: list[FieldDefinition] = list(file_answers.fields) if file_answers else []
|
|
@@ -295,6 +297,7 @@ def annotate_directory_command(
|
|
|
295
297
|
source_path: SourcePathOption = None,
|
|
296
298
|
source_revision: SourceRevisionOption = None,
|
|
297
299
|
source_sha256: SourceSha256Option = None,
|
|
300
|
+
infer_from_runtime: RuntimeInferenceValuesOption = None,
|
|
298
301
|
recursive: bool = typer.Option(
|
|
299
302
|
False,
|
|
300
303
|
"--recursive",
|
|
@@ -326,6 +329,14 @@ def annotate_directory_command(
|
|
|
326
329
|
"--group-kind",
|
|
327
330
|
help="Artifact kind for the corresponding --group-selector.",
|
|
328
331
|
),
|
|
332
|
+
include_discovered: bool = typer.Option(
|
|
333
|
+
False,
|
|
334
|
+
"--include-discovered",
|
|
335
|
+
help=(
|
|
336
|
+
"Include discovered files and child bundles non-interactively using "
|
|
337
|
+
"sparse descriptions."
|
|
338
|
+
),
|
|
339
|
+
),
|
|
329
340
|
checksum_policy: ChecksumPolicyOption = "auto",
|
|
330
341
|
max_checksum_bytes: MaxChecksumBytesOption = 10 * 1024**3,
|
|
331
342
|
checksum_values: ChecksumValuesOption = None,
|
|
@@ -346,17 +357,6 @@ def annotate_directory_command(
|
|
|
346
357
|
directory_answers.target if directory_answers is not None else None,
|
|
347
358
|
expected_kind="directory",
|
|
348
359
|
)
|
|
349
|
-
if not is_interactive and directory_answers is None:
|
|
350
|
-
_error("--no-interactive requires --answers")
|
|
351
|
-
if not is_interactive and directory_answers is not None:
|
|
352
|
-
try:
|
|
353
|
-
answer_files.require_complete_directory_answers(
|
|
354
|
-
directory_answers,
|
|
355
|
-
target=output_dir,
|
|
356
|
-
)
|
|
357
|
-
except answer_files.AnswersError as exc:
|
|
358
|
-
_error(str(exc))
|
|
359
|
-
|
|
360
360
|
discovered_files, discovered_child_bundle_annotations = _discover_directory_entries(
|
|
361
361
|
output_dir,
|
|
362
362
|
recursive=recursive,
|
|
@@ -382,6 +382,21 @@ def annotate_directory_command(
|
|
|
382
382
|
or directory_answers.child_bundles
|
|
383
383
|
)
|
|
384
384
|
)
|
|
385
|
+
has_cli_inventory = include_discovered or bool(artifact_groups)
|
|
386
|
+
if not is_interactive:
|
|
387
|
+
if directory_answers is not None and not has_cli_inventory:
|
|
388
|
+
try:
|
|
389
|
+
answer_files.require_complete_directory_answers(
|
|
390
|
+
directory_answers,
|
|
391
|
+
target=output_dir,
|
|
392
|
+
)
|
|
393
|
+
except answer_files.AnswersError as exc:
|
|
394
|
+
_error(str(exc))
|
|
395
|
+
elif not has_answers_inventory and not has_cli_inventory:
|
|
396
|
+
_error(
|
|
397
|
+
"--no-interactive directory annotation requires "
|
|
398
|
+
"--include-discovered, --group-selector, or --answers inventory"
|
|
399
|
+
)
|
|
385
400
|
if (
|
|
386
401
|
not has_answers_inventory
|
|
387
402
|
and not discovered_files
|
|
@@ -442,6 +457,7 @@ def annotate_directory_command(
|
|
|
442
457
|
source_path=source_path,
|
|
443
458
|
source_revision=source_revision,
|
|
444
459
|
source_sha256=source_sha256,
|
|
460
|
+
infer_from_runtime=infer_from_runtime,
|
|
445
461
|
)
|
|
446
462
|
|
|
447
463
|
default_kind = _validate_artifact_kind(kind) if kind is not None else None
|
|
@@ -460,6 +476,46 @@ def annotate_directory_command(
|
|
|
460
476
|
output_dir,
|
|
461
477
|
directory_answers.child_bundles,
|
|
462
478
|
)
|
|
479
|
+
if include_discovered:
|
|
480
|
+
excluded_relative_paths = {artifact.path for artifact in artifacts} | {
|
|
481
|
+
relative_path
|
|
482
|
+
for group in artifact_groups
|
|
483
|
+
for relative_path in group.paths
|
|
484
|
+
}
|
|
485
|
+
artifacts.extend(
|
|
486
|
+
_documented_artifacts_from_discovered_files(
|
|
487
|
+
output_dir,
|
|
488
|
+
discovered_files=discovered_files,
|
|
489
|
+
artifact_kind=default_kind or "other",
|
|
490
|
+
excluded_relative_paths=excluded_relative_paths,
|
|
491
|
+
)
|
|
492
|
+
)
|
|
493
|
+
known_child_annotation_paths = {
|
|
494
|
+
child_bundle.annotation_path for child_bundle in child_bundles
|
|
495
|
+
}
|
|
496
|
+
child_bundles.extend(
|
|
497
|
+
_child_bundle_from_annotation(output_dir, annotation_path)
|
|
498
|
+
for annotation_path in discovered_child_bundle_annotations
|
|
499
|
+
if annotation_path.relative_to(output_dir).as_posix()
|
|
500
|
+
not in known_child_annotation_paths
|
|
501
|
+
)
|
|
502
|
+
elif include_discovered:
|
|
503
|
+
grouped_relative_paths = {
|
|
504
|
+
relative_path for group in artifact_groups for relative_path in group.paths
|
|
505
|
+
}
|
|
506
|
+
artifacts = _documented_artifacts_from_discovered_files(
|
|
507
|
+
output_dir,
|
|
508
|
+
discovered_files=discovered_files,
|
|
509
|
+
artifact_kind=default_kind or "other",
|
|
510
|
+
excluded_relative_paths=grouped_relative_paths,
|
|
511
|
+
)
|
|
512
|
+
child_bundles = [
|
|
513
|
+
_child_bundle_from_annotation(output_dir, annotation_path)
|
|
514
|
+
for annotation_path in discovered_child_bundle_annotations
|
|
515
|
+
]
|
|
516
|
+
elif not is_interactive:
|
|
517
|
+
artifacts = []
|
|
518
|
+
child_bundles = []
|
|
463
519
|
else:
|
|
464
520
|
artifacts = []
|
|
465
521
|
grouped_relative_paths = {
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/cli_app/annotate/helpers.py
RENAMED
|
@@ -3,6 +3,10 @@ from collections.abc import Mapping
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from data_annotations.annotations.answers import (
|
|
7
|
+
_expand_runtime_inference_fields,
|
|
8
|
+
_validate_runtime_inference_fields,
|
|
9
|
+
)
|
|
6
10
|
from data_annotations.annotations import (
|
|
7
11
|
write_directory_annotation,
|
|
8
12
|
write_file_annotation,
|
|
@@ -268,6 +272,7 @@ def _selected_git_tags(
|
|
|
268
272
|
def _selected_source_code(
|
|
269
273
|
*,
|
|
270
274
|
answers: answer_files.BaseAnswers | None,
|
|
275
|
+
inferred_fields: set[str],
|
|
271
276
|
source_kind: str | None,
|
|
272
277
|
source_uri: str | None,
|
|
273
278
|
source_download_uri: str | None,
|
|
@@ -284,10 +289,7 @@ def _selected_source_code(
|
|
|
284
289
|
source_sha256,
|
|
285
290
|
]
|
|
286
291
|
if not any(value is not None for value in source_values):
|
|
287
|
-
if
|
|
288
|
-
answers is not None
|
|
289
|
-
and "source_code" in answers.provenance.runtime_inference_fields()
|
|
290
|
-
):
|
|
292
|
+
if "source_code" in inferred_fields:
|
|
291
293
|
return None
|
|
292
294
|
return answers.provenance.source_code if answers is not None else None
|
|
293
295
|
|
|
@@ -308,19 +310,27 @@ def _selected_source_code(
|
|
|
308
310
|
)
|
|
309
311
|
|
|
310
312
|
|
|
311
|
-
def _runtime_inferred_fields(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
313
|
+
def _runtime_inferred_fields(
|
|
314
|
+
answers: answer_files.BaseAnswers | None,
|
|
315
|
+
infer_from_runtime: list[str] | None,
|
|
316
|
+
) -> set[str]:
|
|
317
|
+
selected_fields: list[str] = []
|
|
318
|
+
if answers is not None:
|
|
319
|
+
selected_fields.extend(answers.provenance.infer_from_runtime)
|
|
320
|
+
selected_fields.extend(infer_from_runtime or [])
|
|
321
|
+
try:
|
|
322
|
+
normalized = _validate_runtime_inference_fields(selected_fields)
|
|
323
|
+
except ValueError as exc:
|
|
324
|
+
_error(str(exc))
|
|
325
|
+
return _expand_runtime_inference_fields(normalized)
|
|
315
326
|
|
|
316
327
|
|
|
317
328
|
def _filter_runtime_inferred_overrides(
|
|
318
329
|
overrides: dict[str, Any],
|
|
319
330
|
*,
|
|
320
|
-
|
|
331
|
+
inferred_fields: set[str],
|
|
321
332
|
explicit_fields: set[str],
|
|
322
333
|
) -> dict[str, Any]:
|
|
323
|
-
inferred_fields = _runtime_inferred_fields(answers)
|
|
324
334
|
if not inferred_fields:
|
|
325
335
|
return overrides
|
|
326
336
|
if "source_code" in inferred_fields:
|
|
@@ -409,10 +419,12 @@ def _collect_post_hoc_provenance_from_sources(
|
|
|
409
419
|
source_path: str | None,
|
|
410
420
|
source_revision: str | None,
|
|
411
421
|
source_sha256: str | None,
|
|
422
|
+
infer_from_runtime: list[str] | None,
|
|
412
423
|
) -> tuple[list[str], dict[str, Any], dict[str, Any]]:
|
|
413
424
|
selected_inputs = _selected_inputs(input_values, answers)
|
|
414
425
|
selected_params = _selected_params(param_values, answers)
|
|
415
426
|
answer_command_tokens = _command_tokens_from_answers(answers)
|
|
427
|
+
inferred_fields = _runtime_inferred_fields(answers, infer_from_runtime)
|
|
416
428
|
source_code_cli_values = {
|
|
417
429
|
source_kind,
|
|
418
430
|
source_uri,
|
|
@@ -451,6 +463,7 @@ def _collect_post_hoc_provenance_from_sources(
|
|
|
451
463
|
selected_git_tags = _selected_git_tags(git_tags, answers)
|
|
452
464
|
selected_source_code = _selected_source_code(
|
|
453
465
|
answers=answers,
|
|
466
|
+
inferred_fields=inferred_fields,
|
|
454
467
|
source_kind=source_kind,
|
|
455
468
|
source_uri=source_uri,
|
|
456
469
|
source_download_uri=source_download_uri,
|
|
@@ -505,7 +518,7 @@ def _collect_post_hoc_provenance_from_sources(
|
|
|
505
518
|
params,
|
|
506
519
|
_filter_runtime_inferred_overrides(
|
|
507
520
|
overrides,
|
|
508
|
-
|
|
521
|
+
inferred_fields=inferred_fields,
|
|
509
522
|
explicit_fields=explicit_override_fields,
|
|
510
523
|
),
|
|
511
524
|
)
|
|
@@ -553,12 +566,35 @@ def _collect_post_hoc_provenance_from_sources(
|
|
|
553
566
|
selected_params or {},
|
|
554
567
|
_filter_runtime_inferred_overrides(
|
|
555
568
|
overrides,
|
|
556
|
-
|
|
569
|
+
inferred_fields=inferred_fields,
|
|
557
570
|
explicit_fields=explicit_override_fields,
|
|
558
571
|
),
|
|
559
572
|
)
|
|
560
573
|
|
|
561
574
|
|
|
575
|
+
def _documented_artifacts_from_discovered_files(
|
|
576
|
+
output_dir: Path,
|
|
577
|
+
*,
|
|
578
|
+
discovered_files: list[Path],
|
|
579
|
+
artifact_kind: ArtifactKind,
|
|
580
|
+
excluded_relative_paths: set[str],
|
|
581
|
+
) -> list[DocumentedArtifact]:
|
|
582
|
+
artifacts: list[DocumentedArtifact] = []
|
|
583
|
+
for discovered_file in discovered_files:
|
|
584
|
+
relative_path = discovered_file.relative_to(output_dir).as_posix()
|
|
585
|
+
if relative_path in excluded_relative_paths:
|
|
586
|
+
continue
|
|
587
|
+
artifacts.append(
|
|
588
|
+
DocumentedArtifact(
|
|
589
|
+
path=relative_path,
|
|
590
|
+
kind=artifact_kind,
|
|
591
|
+
title=relative_path,
|
|
592
|
+
summary=None,
|
|
593
|
+
)
|
|
594
|
+
)
|
|
595
|
+
return artifacts
|
|
596
|
+
|
|
597
|
+
|
|
562
598
|
def _documented_artifacts_from_answers(
|
|
563
599
|
artifacts: list[answer_files.DirectoryArtifactAnswers],
|
|
564
600
|
) -> list[DocumentedArtifact]:
|
|
@@ -109,6 +109,12 @@ SOURCE_SHA256_OPTION = typer.Option(
|
|
|
109
109
|
"--source-sha256",
|
|
110
110
|
help="SHA-256 checksum for downloaded source archives or files.",
|
|
111
111
|
)
|
|
112
|
+
RUNTIME_INFERENCE_OPTION = typer.Option(
|
|
113
|
+
"--infer-from-runtime",
|
|
114
|
+
help=(
|
|
115
|
+
"Runtime provenance field or group to infer instead of overriding. Repeatable."
|
|
116
|
+
),
|
|
117
|
+
)
|
|
112
118
|
CHECKSUM_POLICY_OPTION = typer.Option(
|
|
113
119
|
"--checksum-policy",
|
|
114
120
|
help="Checksum behavior for local files: always, auto, or never.",
|
|
@@ -150,6 +156,7 @@ SourceDownloadUriOption = Annotated[str | None, SOURCE_DOWNLOAD_URI_OPTION]
|
|
|
150
156
|
SourcePathOption = Annotated[str | None, SOURCE_PATH_OPTION]
|
|
151
157
|
SourceRevisionOption = Annotated[str | None, SOURCE_REVISION_OPTION]
|
|
152
158
|
SourceSha256Option = Annotated[str | None, SOURCE_SHA256_OPTION]
|
|
159
|
+
RuntimeInferenceValuesOption = Annotated[list[str] | None, RUNTIME_INFERENCE_OPTION]
|
|
153
160
|
ChecksumPolicyOption = Annotated[str, CHECKSUM_POLICY_OPTION]
|
|
154
161
|
MaxChecksumBytesOption = Annotated[int, MAX_CHECKSUM_BYTES_OPTION]
|
|
155
162
|
Sha256Option = Annotated[str | None, SHA256_OPTION]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/__init__.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/decorators.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/models.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/annotations/writers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/__init__.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/decorators.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/models.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/description/writers.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/__init__.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/decorators.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/chain.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/recovery/types.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/runtime.py
RENAMED
|
File without changes
|
{data_annotations-2.8.1 → data_annotations-2.9.0}/src/data_annotations/provenance/writers.py
RENAMED
|
File without changes
|
|
File without changes
|