calkit-python 0.41.10__py3-none-any.whl → 0.41.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.
- calkit/cli/describe.py +45 -0
- calkit/cli/list.py +112 -30
- calkit/cli/main/core.py +6 -1
- calkit/cli/update.py +558 -4
- calkit/core.py +12 -0
- calkit/detect.py +433 -75
- calkit/pipeline.py +25 -10
- calkit/tests/cli/main/test_core.py +16 -1
- calkit/tests/cli/test_list.py +19 -2
- calkit/tests/cli/test_update.py +136 -0
- calkit/tests/test_detect.py +116 -0
- calkit/tests/test_pipeline.py +2 -0
- {calkit_python-0.41.10.dist-info → calkit_python-0.41.12.dist-info}/METADATA +1 -1
- {calkit_python-0.41.10.dist-info → calkit_python-0.41.12.dist-info}/RECORD +32 -32
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/install.json +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/909.e3f9cc3408834a7fdcc3.js +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.65469af996e7a96aa983.js +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
- {calkit_python-0.41.10.data → calkit_python-0.41.12.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
- {calkit_python-0.41.10.dist-info → calkit_python-0.41.12.dist-info}/WHEEL +0 -0
- {calkit_python-0.41.10.dist-info → calkit_python-0.41.12.dist-info}/entry_points.txt +0 -0
- {calkit_python-0.41.10.dist-info → calkit_python-0.41.12.dist-info}/licenses/LICENSE +0 -0
calkit/cli/describe.py
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
from typing import Annotated
|
|
6
7
|
|
|
7
8
|
import typer
|
|
8
9
|
|
|
9
10
|
import calkit
|
|
11
|
+
from calkit.cli import raise_error
|
|
12
|
+
from calkit.environments import get_env_lock_fpath
|
|
10
13
|
|
|
11
14
|
describe_app = typer.Typer(no_args_is_help=True)
|
|
12
15
|
|
|
@@ -16,3 +19,45 @@ def describe_system():
|
|
|
16
19
|
"""Describe the system."""
|
|
17
20
|
system_info = calkit.get_system_info()
|
|
18
21
|
typer.echo(json.dumps(system_info, indent=2))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@describe_app.command(name="env")
|
|
25
|
+
def describe_env(
|
|
26
|
+
name: Annotated[
|
|
27
|
+
str,
|
|
28
|
+
typer.Option("--name", "-n", help="Environment name."),
|
|
29
|
+
],
|
|
30
|
+
):
|
|
31
|
+
"""Describe a single environment, including spec and lock file paths."""
|
|
32
|
+
ck_info = calkit.load_calkit_info()
|
|
33
|
+
envs: dict = ck_info.get("environments", {})
|
|
34
|
+
if name not in envs:
|
|
35
|
+
raise_error(f"Environment '{name}' not found.")
|
|
36
|
+
env = envs[name]
|
|
37
|
+
lock_path = get_env_lock_fpath(env=env, env_name=name)
|
|
38
|
+
result = {
|
|
39
|
+
"kind": env.get("kind"),
|
|
40
|
+
"spec_path": env.get("path"),
|
|
41
|
+
"lock_path": lock_path,
|
|
42
|
+
"prefix": env.get("prefix"),
|
|
43
|
+
"python": env.get("python"),
|
|
44
|
+
}
|
|
45
|
+
typer.echo(json.dumps(result, indent=2))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@describe_app.command(name="envs")
|
|
49
|
+
def describe_envs():
|
|
50
|
+
"""Describe all environments, including spec and lock file paths."""
|
|
51
|
+
ck_info = calkit.load_calkit_info()
|
|
52
|
+
envs: dict = ck_info.get("environments", {})
|
|
53
|
+
result = {}
|
|
54
|
+
for env_name, env in envs.items():
|
|
55
|
+
lock_path = get_env_lock_fpath(env=env, env_name=env_name)
|
|
56
|
+
result[env_name] = {
|
|
57
|
+
"kind": env.get("kind"),
|
|
58
|
+
"spec_path": env.get("path"),
|
|
59
|
+
"lock_path": lock_path,
|
|
60
|
+
"prefix": env.get("prefix"),
|
|
61
|
+
"python": env.get("python"),
|
|
62
|
+
}
|
|
63
|
+
typer.echo(json.dumps(result, indent=2))
|
calkit/cli/list.py
CHANGED
|
@@ -2,16 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
from typing import Annotated, Literal
|
|
6
7
|
|
|
7
8
|
import typer
|
|
8
9
|
|
|
9
10
|
import calkit
|
|
10
|
-
from calkit.cli import AliasGroup, warn
|
|
11
|
+
from calkit.cli import AliasGroup, raise_error, warn
|
|
11
12
|
|
|
12
13
|
list_app = typer.Typer(cls=AliasGroup, no_args_is_help=True)
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
def _echo_object(obj: dict) -> None:
|
|
17
|
+
"""Print a single object in the human-readable YAML-ish listing format."""
|
|
18
|
+
# Copy so popping 'path' doesn't mutate the caller's dict.
|
|
19
|
+
obj = dict(obj)
|
|
20
|
+
path = obj.pop("path", None)
|
|
21
|
+
typer.echo(f"- path: {path}")
|
|
22
|
+
for k, v in obj.items():
|
|
23
|
+
if isinstance(v, dict):
|
|
24
|
+
typer.echo(f" {k}:")
|
|
25
|
+
for k1, v1 in v.items():
|
|
26
|
+
typer.echo(f" {k1}: {v1}")
|
|
27
|
+
elif isinstance(v, list):
|
|
28
|
+
typer.echo(f" {k}:")
|
|
29
|
+
for item in v:
|
|
30
|
+
if isinstance(item, dict):
|
|
31
|
+
for n, (k1, v1) in enumerate(item.items()):
|
|
32
|
+
if n == 0:
|
|
33
|
+
typer.echo(f" - {k1}: {v1}")
|
|
34
|
+
else:
|
|
35
|
+
typer.echo(f" {k1}: {v1}")
|
|
36
|
+
else:
|
|
37
|
+
typer.echo(f" - {item}")
|
|
38
|
+
else:
|
|
39
|
+
typer.echo(f" {k}: {v}")
|
|
40
|
+
|
|
41
|
+
|
|
15
42
|
def _list_objects(
|
|
16
43
|
kind: Literal[
|
|
17
44
|
"notebooks",
|
|
@@ -21,33 +48,44 @@ def _list_objects(
|
|
|
21
48
|
"publications",
|
|
22
49
|
],
|
|
23
50
|
):
|
|
24
|
-
"""List objects.
|
|
51
|
+
"""List objects."""
|
|
52
|
+
ck_info = calkit.load_calkit_info()
|
|
53
|
+
for obj in ck_info.get(kind, []) or []:
|
|
54
|
+
_echo_object(obj)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _list_artifacts(
|
|
58
|
+
kind: Literal["figures", "datasets"],
|
|
59
|
+
json_output: bool,
|
|
60
|
+
declared_only: bool,
|
|
61
|
+
):
|
|
62
|
+
"""List figures or datasets, optionally including auto-detected ones.
|
|
25
63
|
|
|
26
|
-
|
|
64
|
+
By default, artifacts declared in ``calkit.yaml`` are merged with any
|
|
65
|
+
auto-detected from the project's files; ``--declared-only`` returns just the
|
|
66
|
+
declared ones. Each entry carries a ``detected`` flag so callers can tell
|
|
67
|
+
declared and auto-detected artifacts apart.
|
|
27
68
|
"""
|
|
28
69
|
ck_info = calkit.load_calkit_info()
|
|
29
|
-
|
|
30
|
-
for
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
typer.echo(f" - {item}")
|
|
49
|
-
else:
|
|
50
|
-
typer.echo(f" {k}: {v}")
|
|
70
|
+
declared = ck_info.get(kind, []) or []
|
|
71
|
+
declared_paths = {o.get("path") for o in declared if isinstance(o, dict)}
|
|
72
|
+
detected: list[dict] = []
|
|
73
|
+
if not declared_only:
|
|
74
|
+
found = calkit.detect.detect_project_artifacts(ck_info=ck_info)
|
|
75
|
+
for path in found.get(kind, []):
|
|
76
|
+
if path not in declared_paths:
|
|
77
|
+
detected.append({"path": path})
|
|
78
|
+
if json_output:
|
|
79
|
+
result = [
|
|
80
|
+
{**o, "detected": False} for o in declared if isinstance(o, dict)
|
|
81
|
+
]
|
|
82
|
+
result += [{**o, "detected": True} for o in detected]
|
|
83
|
+
typer.echo(json.dumps(result))
|
|
84
|
+
return
|
|
85
|
+
for obj in declared:
|
|
86
|
+
_echo_object(obj)
|
|
87
|
+
for obj in detected:
|
|
88
|
+
_echo_object({**obj, "detected": True})
|
|
51
89
|
|
|
52
90
|
|
|
53
91
|
@list_app.command(name="notebooks|nb")
|
|
@@ -57,15 +95,43 @@ def list_notebooks():
|
|
|
57
95
|
|
|
58
96
|
|
|
59
97
|
@list_app.command(name="figures|figs")
|
|
60
|
-
def list_figures(
|
|
98
|
+
def list_figures(
|
|
99
|
+
json_output: Annotated[
|
|
100
|
+
bool, typer.Option("--json", help="Output result as JSON.")
|
|
101
|
+
] = False,
|
|
102
|
+
declared_only: Annotated[
|
|
103
|
+
bool,
|
|
104
|
+
typer.Option(
|
|
105
|
+
"--declared-only",
|
|
106
|
+
help=(
|
|
107
|
+
"Only list figures declared in calkit.yaml; "
|
|
108
|
+
"skip auto-detection."
|
|
109
|
+
),
|
|
110
|
+
),
|
|
111
|
+
] = False,
|
|
112
|
+
):
|
|
61
113
|
"""List figures in the project."""
|
|
62
|
-
|
|
114
|
+
_list_artifacts("figures", json_output, declared_only)
|
|
63
115
|
|
|
64
116
|
|
|
65
117
|
@list_app.command(name="datasets")
|
|
66
|
-
def list_datasets(
|
|
118
|
+
def list_datasets(
|
|
119
|
+
json_output: Annotated[
|
|
120
|
+
bool, typer.Option("--json", help="Output result as JSON.")
|
|
121
|
+
] = False,
|
|
122
|
+
declared_only: Annotated[
|
|
123
|
+
bool,
|
|
124
|
+
typer.Option(
|
|
125
|
+
"--declared-only",
|
|
126
|
+
help=(
|
|
127
|
+
"Only list datasets declared in calkit.yaml; "
|
|
128
|
+
"skip auto-detection."
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
] = False,
|
|
132
|
+
):
|
|
67
133
|
"""List datasets in the project."""
|
|
68
|
-
|
|
134
|
+
_list_artifacts("datasets", json_output, declared_only)
|
|
69
135
|
|
|
70
136
|
|
|
71
137
|
@list_app.command(name="publications|pubs")
|
|
@@ -167,12 +233,28 @@ def list_stages(
|
|
|
167
233
|
list[str] | None,
|
|
168
234
|
typer.Option("--kind", "-k", help="Filter stages by kind."),
|
|
169
235
|
] = None,
|
|
236
|
+
stale_only: Annotated[
|
|
237
|
+
bool, typer.Option("--stale", help="Show only stale stages.")
|
|
238
|
+
] = False,
|
|
170
239
|
):
|
|
171
240
|
"""List pipeline stages."""
|
|
172
|
-
|
|
241
|
+
ck_info = calkit.load_calkit_info()
|
|
242
|
+
stages = ck_info.get("pipeline", {}).get("stages", {})
|
|
243
|
+
# If we only want stale stages, we need to get the status first.
|
|
244
|
+
# This compiles the pipeline, cleans notebooks, and checks environments,
|
|
245
|
+
# all of which can affect whether a stage is stale, so we don't skip them.
|
|
246
|
+
if stale_only:
|
|
247
|
+
status = calkit.pipeline.get_status(ck_info=ck_info)
|
|
248
|
+
if status.errors:
|
|
249
|
+
raise_error(
|
|
250
|
+
"Failed to determine stale stages: " + "; ".join(status.errors)
|
|
251
|
+
)
|
|
252
|
+
stale_stage_names = status.stale_stage_names
|
|
173
253
|
for name, stage in stages.items():
|
|
174
254
|
if kinds is not None and stage.get("kind") not in kinds:
|
|
175
255
|
continue
|
|
256
|
+
if stale_only and name not in stale_stage_names:
|
|
257
|
+
continue
|
|
176
258
|
typer.echo(name)
|
|
177
259
|
|
|
178
260
|
|
calkit/cli/main/core.py
CHANGED
|
@@ -196,7 +196,12 @@ def init(
|
|
|
196
196
|
repo = calkit.git.get_repo()
|
|
197
197
|
repo.git.add(".dvc")
|
|
198
198
|
repo.git.commit("-m", "Initialize DVC")
|
|
199
|
-
#
|
|
199
|
+
# Create an empty calkit.yaml if one doesn't already exist
|
|
200
|
+
if not os.path.isfile("calkit.yaml"):
|
|
201
|
+
with open("calkit.yaml", "w"):
|
|
202
|
+
pass
|
|
203
|
+
repo.git.add("calkit.yaml")
|
|
204
|
+
repo.git.commit("-m", "Initialize Calkit")
|
|
200
205
|
# TODO: Initialize `dvc.yaml`
|
|
201
206
|
# TODO: Add a sane .gitignore file
|
|
202
207
|
# TODO: Add a sane LICENSE file?
|