calkit-python 0.41.2__py3-none-any.whl → 0.41.3__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 (27) hide show
  1. calkit/cli/main/core.py +14 -1
  2. calkit/environments.py +6 -0
  3. calkit/jupyterlab/routes.py +6 -3
  4. calkit/pipeline.py +62 -5
  5. calkit/server.py +3 -5
  6. calkit/tests/test_environments.py +34 -0
  7. calkit/tests/test_pipeline.py +92 -0
  8. {calkit_python-0.41.2.dist-info → calkit_python-0.41.3.dist-info}/METADATA +1 -1
  9. {calkit_python-0.41.2.dist-info → calkit_python-0.41.3.dist-info}/RECORD +27 -27
  10. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
  11. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/install.json +0 -0
  12. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
  13. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
  14. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
  15. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js +0 -0
  16. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js +0 -0
  17. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js +0 -0
  18. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/909.e3f9cc3408834a7fdcc3.js +0 -0
  19. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js +0 -0
  20. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  21. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png +0 -0
  22. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.65469af996e7a96aa983.js +0 -0
  23. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
  24. {calkit_python-0.41.2.data → calkit_python-0.41.3.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
  25. {calkit_python-0.41.2.dist-info → calkit_python-0.41.3.dist-info}/WHEEL +0 -0
  26. {calkit_python-0.41.2.dist-info → calkit_python-0.41.3.dist-info}/entry_points.txt +0 -0
  27. {calkit_python-0.41.2.dist-info → calkit_python-0.41.3.dist-info}/licenses/LICENSE +0 -0
calkit/cli/main/core.py CHANGED
@@ -579,6 +579,8 @@ def get_status(
579
579
  if stale_stage is None:
580
580
  continue
581
581
  typer.echo(f" {typer.style(stage_name, fg='yellow')}:")
582
+ if stale_stage.always_run:
583
+ typer.echo(" always runs")
582
584
  if stale_stage.modified_command:
583
585
  typer.echo(" modified command")
584
586
  # Show stale outputs for this stage
@@ -596,8 +598,16 @@ def get_status(
596
598
  typer.echo(" modified inputs:")
597
599
  for input_path in stale_stage.modified_inputs:
598
600
  typer.echo(f" {input_path}")
601
+ if pipeline_status.always_run_stage_names:
602
+ typer.echo("Always-run stages:")
603
+ for stage_name in pipeline_status.always_run_stage_names:
604
+ typer.echo(f" {typer.style(stage_name, fg='cyan')}")
599
605
  elif pipeline_status:
600
606
  typer.echo("Pipeline is up to date.")
607
+ if pipeline_status.always_run_stage_names:
608
+ typer.echo("Always-run stages:")
609
+ for stage_name in pipeline_status.always_run_stage_names:
610
+ typer.echo(f" {typer.style(stage_name, fg='cyan')}")
601
611
 
602
612
 
603
613
  @app.command(name="diff")
@@ -2421,7 +2431,10 @@ def run_in_env(
2421
2431
  elif env["kind"] == "pixi":
2422
2432
  env_cmd = []
2423
2433
  if "name" in env:
2424
- env_cmd = ["--environment", env["name"]]
2434
+ env_cmd += ["--environment", env["name"]]
2435
+ env_path = env.get("path")
2436
+ if env_path and os.path.dirname(env_path):
2437
+ env_cmd += ["--manifest-path", os.path.abspath(env_path)]
2425
2438
  cmd = ["pixi", "run"] + env_cmd + cmd
2426
2439
  if verbose:
2427
2440
  typer.echo(f"Running command: {cmd}")
calkit/environments.py CHANGED
@@ -314,6 +314,12 @@ def get_env_lock_fpath(
314
314
  lock_fpath = os.path.join(env_dir, "uv.lock")
315
315
  else:
316
316
  lock_fpath = "uv.lock"
317
+ elif env_kind == "pixi":
318
+ env_dir = os.path.dirname(env.get("path") or "")
319
+ if env_dir:
320
+ lock_fpath = os.path.join(env_dir, "pixi.lock")
321
+ else:
322
+ lock_fpath = "pixi.lock"
317
323
  elif env_kind in ["venv", "uv-venv"]:
318
324
  if legacy:
319
325
  lock_fpath += ".txt"
@@ -903,11 +903,14 @@ class PipelineStatusRouteHandler(APIHandler):
903
903
  pipeline_status = {
904
904
  k.split("dvc.yaml:")[-1]: v
905
905
  for k, v in raw_status.items()
906
- if v != ["always changed"]
907
- and not k.endswith(".dvc")
906
+ if not k.endswith(".dvc")
908
907
  and k.split("dvc.yaml:")[-1].split("@")[0] not in frozen_stages
909
908
  }
910
- is_outdated = len(pipeline_status) > 0
909
+ # Always-run-only stages are surfaced in pipeline_status but do
910
+ # not count toward is_outdated, matching the CLI's is_stale logic.
911
+ is_outdated = any(
912
+ v != ["always changed"] for v in pipeline_status.values()
913
+ )
911
914
  self.finish(
912
915
  json.dumps(
913
916
  {
calkit/pipeline.py CHANGED
@@ -91,7 +91,32 @@ class PipelineStatus(BaseModel):
91
91
  @computed_field
92
92
  @property
93
93
  def stale_stage_names(self) -> list[str]:
94
- return list(self.stale_stages.keys())
94
+ # Pure always-run stages aren't truly stale; surface them via
95
+ # always_run_stage_names instead.
96
+ return [
97
+ name
98
+ for name, stage in self.stale_stages.items()
99
+ if not stage.always_run
100
+ or stage.modified_command
101
+ or stage.modified_inputs
102
+ or stage.modified_outputs
103
+ or stage.stale_outputs
104
+ ]
105
+
106
+ @computed_field
107
+ @property
108
+ def always_run_stage_names(self) -> list[str]:
109
+ # Only list stages whose sole change indicator is always_run; stages
110
+ # that also have real changes are reported under stale_stage_names.
111
+ return [
112
+ name
113
+ for name, stage in self.stale_stages.items()
114
+ if stage.always_run
115
+ and not stage.modified_command
116
+ and not stage.modified_inputs
117
+ and not stage.modified_outputs
118
+ and not stage.stale_outputs
119
+ ]
95
120
 
96
121
  @computed_field
97
122
  @property
@@ -107,7 +132,20 @@ class PipelineStatus(BaseModel):
107
132
  @computed_field
108
133
  @property
109
134
  def is_stale(self) -> bool:
110
- return bool(self.stale_stages)
135
+ # A stage whose only listed change is "always_run" is not actually
136
+ # stale — it just always re-executes by design. Treat the pipeline
137
+ # as stale only if some stage has real change indicators.
138
+ for stage in self.stale_stages.values():
139
+ if not stage.always_run:
140
+ return True
141
+ if (
142
+ stage.modified_command
143
+ or stage.modified_inputs
144
+ or stage.modified_outputs
145
+ or stage.stale_outputs
146
+ ):
147
+ return True
148
+ return False
111
149
 
112
150
 
113
151
  class StaleStage(BaseModel):
@@ -118,6 +156,7 @@ class StaleStage(BaseModel):
118
156
  modified_inputs: list[str] = Field(default_factory=list)
119
157
  modified_outputs: list[str] = Field(default_factory=list)
120
158
  modified_command: bool = False
159
+ always_run: bool = False
121
160
 
122
161
  @staticmethod
123
162
  def _as_path_list(paths: object) -> list[str]:
@@ -206,17 +245,22 @@ class StaleStage(BaseModel):
206
245
  modified_outputs = []
207
246
  status_blocks = []
208
247
  modified_command = False
248
+ always_run = False
209
249
  if isinstance(status_data, dict):
210
250
  status_blocks = [status_data]
211
251
  elif isinstance(status_data, list):
212
252
  status_blocks = [
213
253
  item for item in status_data if isinstance(item, dict)
214
254
  ]
215
- # DVC may return plain markers like ["changed command"].
255
+ # DVC may return plain markers like ["changed command"] or
256
+ # ["always changed"], possibly alongside other change blocks.
216
257
  if "changed command" in status_data:
217
258
  modified_command = True
259
+ if "always changed" in status_data:
260
+ always_run = True
218
261
  elif isinstance(status_data, str):
219
262
  modified_command = status_data == "changed command"
263
+ always_run = status_data == "always changed"
220
264
  for block in status_blocks:
221
265
  if "changed command" in block:
222
266
  changed_command_value = block.get("changed command")
@@ -264,7 +308,16 @@ class StaleStage(BaseModel):
264
308
  stale_outputs.extend(
265
309
  [path for path in output_paths if path not in modified_outputs]
266
310
  )
267
- if not stale_outputs and not modified_outputs and configured_outputs:
311
+ # Fallback: if DVC flagged the stage but we couldn't attribute the
312
+ # change to specific paths, mark all configured outputs as stale.
313
+ # Skip the fallback for pure "always_run" stages — they always
314
+ # re-execute by design, not because outputs are out of date.
315
+ if (
316
+ not stale_outputs
317
+ and not modified_outputs
318
+ and configured_outputs
319
+ and not always_run
320
+ ):
268
321
  stale_outputs.extend(configured_outputs)
269
322
  stale_outputs = list(dict.fromkeys(stale_outputs))
270
323
  return cls(
@@ -273,6 +326,7 @@ class StaleStage(BaseModel):
273
326
  modified_inputs=modified_inputs,
274
327
  modified_outputs=modified_outputs,
275
328
  modified_command=modified_command,
329
+ always_run=always_run,
276
330
  )
277
331
 
278
332
 
@@ -666,7 +720,10 @@ def get_status(
666
720
  # collisions when a subproject stage shares a name with a root stage.
667
721
  raw_stale_stages: dict[str, tuple[str, str | None, list]] = {}
668
722
  for k, v in raw_status.items():
669
- if v == ["always changed"] or k.endswith(".dvc"):
723
+ # Skip DVC-tracked files (.dvc entries) — they are not pipeline
724
+ # stages. Stages marked ["always changed"] are kept so the user
725
+ # sees them in the pipeline status output.
726
+ if k.endswith(".dvc"):
670
727
  continue
671
728
  if "dvc.yaml:" in k:
672
729
  prefix, bare_name = k.split("dvc.yaml:", 1)
calkit/server.py CHANGED
@@ -303,18 +303,16 @@ def get_status(
303
303
  # ]
304
304
  # }
305
305
  dvc_pipeline_status = dvc.repo.status.status(dvc_repo)
306
- # Remove any always changed entries so the pipeline doesn't look
307
- # out of date
308
306
  logger.info(f"Raw DVC pipeline status: {dvc_pipeline_status}")
309
- # Frozen stages are intentionally pinned and hidden from status
307
+ # Frozen stages are intentionally pinned and hidden from status.
308
+ # Always-run entries are kept so clients can surface them.
310
309
  frozen_stages = calkit.pipeline.frozen_stage_base_names(
311
310
  wdir=project.wdir
312
311
  )
313
312
  dvc_pipeline_status = {
314
313
  k.split("dvc.yaml:")[-1]: v
315
314
  for k, v in dvc_pipeline_status.items()
316
- if v != ["always changed"]
317
- and not k.endswith(".dvc")
315
+ if not k.endswith(".dvc")
318
316
  and k.split("dvc.yaml:")[-1].split("@")[0] not in frozen_stages
319
317
  }
320
318
  logger.info(
@@ -1017,6 +1017,40 @@ def test_nix_env(tmp_dir):
1017
1017
  assert "devShells" in content
1018
1018
 
1019
1019
 
1020
+ def test_pixi_env_lock_fpath(tmp_dir):
1021
+ # Manifest at the repo root: pixi.lock lives next to pixi.toml.
1022
+ root_env = {"kind": "pixi", "path": "pixi.toml"}
1023
+ assert (
1024
+ calkit.environments.get_env_lock_fpath(
1025
+ env=root_env, env_name="main", as_posix=True
1026
+ )
1027
+ == "pixi.lock"
1028
+ )
1029
+ # Manifest in a subdirectory: lock follows the manifest.
1030
+ nested_env = {"kind": "pixi", "path": ".calkit/envs/my-env/pixi.toml"}
1031
+ assert (
1032
+ calkit.environments.get_env_lock_fpath(
1033
+ env=nested_env, env_name="my-env", as_posix=True
1034
+ )
1035
+ == ".calkit/envs/my-env/pixi.lock"
1036
+ )
1037
+ # path may be omitted/None without raising.
1038
+ assert (
1039
+ calkit.environments.get_env_lock_fpath(
1040
+ env={"kind": "pixi"}, env_name="main", as_posix=True
1041
+ )
1042
+ == "pixi.lock"
1043
+ )
1044
+ assert (
1045
+ calkit.environments.get_env_lock_fpath(
1046
+ env={"kind": "pixi", "path": None},
1047
+ env_name="main",
1048
+ as_posix=True,
1049
+ )
1050
+ == "pixi.lock"
1051
+ )
1052
+
1053
+
1020
1054
  def test_add_packages_to_nix_flake(tmp_dir):
1021
1055
  # Round-trip: generate a flake, add some packages, verify they appear
1022
1056
  # in the packages list, that duplicates aren't re-inserted, and that
@@ -1465,6 +1465,98 @@ def test_get_status_excludes_frozen_stage(tmp_dir):
1465
1465
  assert "get-data" not in status.stale_stage_names
1466
1466
 
1467
1467
 
1468
+ def test_get_status_includes_always_run_stage(tmp_dir):
1469
+ subprocess.check_call(["calkit", "init"])
1470
+ ck_info = {
1471
+ "environments": {
1472
+ "py": {
1473
+ "kind": "uv-venv",
1474
+ "path": "requirements.txt",
1475
+ "prefix": ".venv",
1476
+ }
1477
+ },
1478
+ "pipeline": {
1479
+ "stages": {
1480
+ "ticker": {
1481
+ "kind": "python-script",
1482
+ "environment": "py",
1483
+ "script_path": "something/my-cool-script.py",
1484
+ "outputs": ["ticker.out"],
1485
+ "always_run": True,
1486
+ },
1487
+ "normal": {
1488
+ "kind": "python-script",
1489
+ "environment": "py",
1490
+ "script_path": "something/normal-script.py",
1491
+ "outputs": ["normal.out"],
1492
+ },
1493
+ }
1494
+ },
1495
+ }
1496
+ with open("calkit.yaml", "w") as f:
1497
+ calkit.ryaml.dump(ck_info, f)
1498
+ with open("requirements.txt", "w") as f:
1499
+ f.write("requests\n")
1500
+ os.makedirs("something", exist_ok=True)
1501
+ with open("something/my-cool-script.py", "w") as f:
1502
+ f.write("open('ticker.out', 'w').write('tick')\n")
1503
+ with open("something/normal-script.py", "w") as f:
1504
+ f.write("open('normal.out', 'w').write('hi')\n")
1505
+ # The compiled DVC stage must carry always_changed: true so DVC's status
1506
+ # actually emits the "always changed" marker
1507
+ dvc_stages = calkit.pipeline.to_dvc(ck_info=ck_info)
1508
+ assert dvc_stages["ticker"]["always_changed"] is True
1509
+ # Initial status: outputs missing, both stages are stale for real reasons.
1510
+ # The always-run stage carries the always_run flag too.
1511
+ status = calkit.pipeline.get_status(ck_info=ck_info)
1512
+ assert status.is_stale
1513
+ assert "ticker" in status.stale_stages
1514
+ assert status.stale_stages["ticker"].always_run
1515
+ assert "normal" in status.stale_stage_names
1516
+ assert "ticker" in status.stale_stage_names
1517
+ # Run the pipeline so outputs exist and nothing is "really" stale
1518
+ subprocess.check_call(["calkit", "run"])
1519
+ status = calkit.pipeline.get_status(ck_info=ck_info)
1520
+ # The always-run stage must still be visible — that was the bug
1521
+ assert "ticker" in status.stale_stages
1522
+ assert status.stale_stages["ticker"].always_run
1523
+ assert status.stale_stages["ticker"].stale_outputs == []
1524
+ assert status.stale_stages["ticker"].modified_outputs == []
1525
+ assert status.stale_stages["ticker"].modified_inputs == []
1526
+ assert not status.stale_stages["ticker"].modified_command
1527
+ assert status.always_run_stage_names == ["ticker"]
1528
+ # A pipeline whose only "stale" stage is always_run must not be marked
1529
+ # stale — calkit update relies on is_stale to gate release publication
1530
+ assert not status.is_stale
1531
+ assert "ticker" not in status.stale_stage_names
1532
+
1533
+
1534
+ def test_stale_stage_detects_always_changed_marker():
1535
+ stale_stage = calkit.pipeline.StaleStage.from_status_data(
1536
+ status_data=["always changed"],
1537
+ configured_outputs=["ticker.out"],
1538
+ )
1539
+ assert stale_stage.always_run
1540
+ assert not stale_stage.modified_command
1541
+ assert stale_stage.stale_outputs == []
1542
+ assert stale_stage.modified_outputs == []
1543
+ assert stale_stage.modified_inputs == []
1544
+
1545
+
1546
+ def test_stale_stage_always_changed_with_missing_output():
1547
+ # Mirrors DVC's actual output for an always_changed stage whose output
1548
+ # is missing from cache: ["always changed"] coexists with a change block.
1549
+ stale_stage = calkit.pipeline.StaleStage.from_status_data(
1550
+ status_data=[
1551
+ {"changed outs": {"ticker.out": "deleted"}},
1552
+ "always changed",
1553
+ ],
1554
+ configured_outputs=["ticker.out"],
1555
+ )
1556
+ assert stale_stage.always_run
1557
+ assert stale_stage.stale_outputs == ["ticker.out"]
1558
+
1559
+
1468
1560
  def test_stale_stage_detects_changed_command():
1469
1561
  stale_stage = calkit.pipeline.StaleStage.from_status_data(
1470
1562
  status_data=[{"changed command": "python src/new-script.py"}],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.41.2
3
+ Version: 0.41.3
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -10,7 +10,7 @@ calkit/datasets.py,sha256=9lOlOreey2aJGMw6MiV9MDzSHp-fYkCf1k1AZbWTdQE,2235
10
10
  calkit/dependencies.py,sha256=y5N_4ctwp9rFZVg82Si9_2x8Yh64Ex-WqP0iiN5NNBE,13276
11
11
  calkit/detect.py,sha256=Q2y51N5i9dcY8fdCd7Jeiz43XLX4P_ciQDP1ybYNh2w,62194
12
12
  calkit/docker.py,sha256=EEcgbXjs-1ttixAZ-G-hmDlvnqSxYbIZmbzeAza37TI,15706
13
- calkit/environments.py,sha256=b9ZcTvEj5iLb_xE1odTgxesmGUe-WwpzBtjcWmNcC2c,74312
13
+ calkit/environments.py,sha256=GQhdH6WI3tXBSGg7ovVNieS0xSqD2kEsP97dXzLJd_s,74529
14
14
  calkit/fs.py,sha256=tVyiSBfWNZJv5qudR-KzzxajPYR2rf-uTNEJSQjMHfI,43818
15
15
  calkit/git.py,sha256=Ydpcq_W4TZe0Sic3JlbnrzotfK8AzCG76S1zViP4HlA,13312
16
16
  calkit/github.py,sha256=8_yZ0ej8TKM4qk8iPeGq47R1I4MHdSoJ_Zpr1gRwNEM,1701
@@ -26,9 +26,9 @@ calkit/notebooks.py,sha256=obKRJaVf-rrQslcu76yx8lP18QKWGJOD_Irw7yHw9ek,8913
26
26
  calkit/office.py,sha256=mZqCxkIsmWthBAeJZ5SiGe21fn5zD2zXsy1E6nQIpWE,1291
27
27
  calkit/ops.py,sha256=lLlZAFeplU8uLeMavr99d__Iof-YYwKijus74WfLipk,854
28
28
  calkit/overleaf.py,sha256=0LcVvMmYu8sfwEGG1GTPedNuk7hOIQTr6Hwv4u9S5Xs,23540
29
- calkit/pipeline.py,sha256=Nb6YnSopzmBAXjixWUfpglocrHypD356Wz_fr2_5sns,56211
29
+ calkit/pipeline.py,sha256=0AqvSqpfAdzcNetIysYRzYOl8O8F6btqTdXe8QF0TBs,58495
30
30
  calkit/releases.py,sha256=G3kV17f0EXu0pHoaD40U9HEie1NW5hxTII0J5TIxUDs,9815
31
- calkit/server.py,sha256=TdhHsjmIvmit_XgeWcS115Ki41jLhWVKe4K879DopKU,22076
31
+ calkit/server.py,sha256=GQm1bI5Q1NLFNwQL-iP6j4iWKUpKpP3cLEyfj5IMwe4,22009
32
32
  calkit/cli/__init__.py,sha256=pP_1wFuewdUUvEWTtaEcdKblPaSYuTvthQb2Ef08UMo,1921
33
33
  calkit/cli/check.py,sha256=D0s9Rglc62W12QyglnrZ1o4MfHi8yrhgaFWmFig-qek,49976
34
34
  calkit/cli/cloud.py,sha256=3FVvJUqG16I2PWIpcbvnVryK9tpqSK06BwPQBN_kvQs,4040
@@ -46,13 +46,13 @@ calkit/cli/overleaf.py,sha256=eh_9SiL9zIn5z4ErEvx0c4w4-2fSAIBbF3kStsUO-ZM,24899
46
46
  calkit/cli/scheduler.py,sha256=ANQqP0PwQaWtiIfNFBg2uwEpOSpVVcQkginKI8FJFV4,28615
47
47
  calkit/cli/update.py,sha256=GX8vcO46JDCDRrDK4YmZJ2ODLgGz6_s5pTjao7S_trI,25543
48
48
  calkit/cli/main/__init__.py,sha256=qu1POZPyqs33ZKfasOxv_Wc-EzVcEgK3Dt_vwFL8Bi8,65
49
- calkit/cli/main/core.py,sha256=xycGTgHbFMo39Ba-YL4b9zWNePduiVln2yklEmvtLwE,119831
49
+ calkit/cli/main/core.py,sha256=Y86I40gv6RLIoZLROWrlki2zfaqNnWvt1Xrw-J5UVYI,120603
50
50
  calkit/cli/main/xr.py,sha256=Bug5qJuiIq0tusmdu30t-ZdyaT_Sy5RwPSTiHvCtIqE,25600
51
51
  calkit/dvc/__init__.py,sha256=-B6tilCb_jw7QFmJ2srILez24wDhNKUzIBhZUMt01dE,381
52
52
  calkit/dvc/core.py,sha256=ycpEXx2IgNgqB-agl5rhpyxuK4GWLQOFV7RTPnwKV_I,21142
53
53
  calkit/dvc/zip.py,sha256=otlcfvQYI4hl7qRfLivggybRQiRKXIRu9LCVioFgXH4,21003
54
54
  calkit/jupyterlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- calkit/jupyterlab/routes.py,sha256=790XMOd5_C-1383mAbSjwQhLYzOuvDA0H3yQ3Kn9sUA,51286
55
+ calkit/jupyterlab/routes.py,sha256=jnNd289CpNQFY6am5p3qaiL_Tcnkd6J_vDVdjIXlb1A,51465
56
56
  calkit/models/__init__.py,sha256=vGYF4MZBGzDk1BPBzpp1bRb59RCJ2Bj2kzHuVVRacug,120
57
57
  calkit/models/core.py,sha256=LXYEf_RfYB95I9opWC0bVTHterVAk4gTLoQf7GFDMMo,9772
58
58
  calkit/models/io.py,sha256=FgItZDxrSyph91YRMOEfJo9xWmy3drMin_632vy9WhA,1087
@@ -77,7 +77,7 @@ calkit/tests/test_core.py,sha256=MDSQCsUI4RkZ6VZGWhDELdebZujLuLn8o6BLBSaBb5w,460
77
77
  calkit/tests/test_dependencies.py,sha256=HskXLzbvvRJz2TXnKPpPAo9lPhoZGYQFy4BmEF31nvk,11139
78
78
  calkit/tests/test_detect.py,sha256=1QLei1SjLdxy3IWwFZi3PIz8WHpKmG50Hjygc_HxYxg,32907
79
79
  calkit/tests/test_docker.py,sha256=27d0K1GOWiCNv_5TkZSob6EaC0EtQ9DSBIDfkEAEzGk,2077
80
- calkit/tests/test_environments.py,sha256=dQ5XwgVRMaUipu75T29Coq9b0sK4bRQc8LVl4f7gBdE,36606
80
+ calkit/tests/test_environments.py,sha256=KlIQ7tZhhPSFlJ3cM4nLCpCxShB18MCHUBW0y2Kw3UU,37659
81
81
  calkit/tests/test_fs.py,sha256=8XFlC6bsw_aGodhmDq87OnI60L2licz459yUlDcAieY,9448
82
82
  calkit/tests/test_git.py,sha256=2Jp47397tcdnMb2u3jUi1H9W7hZbbpqBxRsQuLxQbFo,14556
83
83
  calkit/tests/test_install.py,sha256=54zDtURcYExjZNZmI--Eh_hnidcwYxX-7RfcAJLT2jg,8679
@@ -87,7 +87,7 @@ calkit/tests/test_jupyter.py,sha256=YTL6zI740UM2KUjskSipzvSKxsyQ8rVPFQIX5cb2tMQ,
87
87
  calkit/tests/test_magics.py,sha256=1O_hYPLMBVQJj8rLCFV3sU2DejlLLnk6Tv4_Tr-FZHQ,2101
88
88
  calkit/tests/test_matlab.py,sha256=7yFKOYDCtxYIkRyFOwTIxlvzx03Pac0FKGIUT4Zx7zA,7589
89
89
  calkit/tests/test_notebooks.py,sha256=6xUpHjCbCv08oJkArj-NkTUMlz9zzm8pf1qam3s27M0,2692
90
- calkit/tests/test_pipeline.py,sha256=9sy_N6o13kHZwTOsPxXAcqku5FTAq-oYtFw0RBZX8e4,73998
90
+ calkit/tests/test_pipeline.py,sha256=GjONQFUVUty_mHcsArxsmhrNMA38iw3WLW9Sb_j3tvI,77822
91
91
  calkit/tests/test_releases.py,sha256=-eig7SpntQKjHLBRbHv0J-pX4ecr3HO5dukuvfZQPIo,6081
92
92
  calkit/tests/test_templates.py,sha256=RN4RIgmuFlmFYQFgfJUYS-KaLmie8fnWzmfg5Ay1egk,430
93
93
  calkit/tests/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -117,23 +117,23 @@ calkit/tests/models/test_pipeline.py,sha256=AJsMAX4928U3_Omt0_8K96U5LU_KoNErgpHe
117
117
  calkit/agent_skills/add-pipeline-stage/SKILL.md,sha256=kpYYb-rJsS4B9p1Ub3gL21irCG5ZZ4DvWZpdJeEwPZk,3772
118
118
  calkit/agent_skills/conventions/SKILL.md,sha256=nAM2FjSMk6ED56Dr5zh2bL_dhfzDra-h2ZgzHU7ruS4,9524
119
119
  calkit/agent_skills/create-pipeline/SKILL.md,sha256=2FGw1iDQVRk5FUq79GP3lPdgvgof2Wi1O9O-abGGtgs,5422
120
- calkit_python-0.41.2.data/data/etc/jupyter/jupyter_server_config.d/calkit.json,sha256=CWrZP--JDGz8fsvbAlKr_duTL1vDriGT48SL1YCtkY0,81
121
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/package.json,sha256=INxx8QhvSl7KP6yL7A7-m2-FIsnB__mYEWG_Eq5RP-c,6651
122
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig,sha256=6w0b5y8LztEv2tErajJ5d39yBryld02crX0CG3zZFNs,6509
123
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json,sha256=YSpIrwpwB-lQBk9Mwv-npedWTOOZreO6nlWZhqlWyGI,840
124
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js,sha256=mixXcqFUZukj7_jQVxHTavsYl7cdZv83sEe4phJgkh0,59893
125
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js,sha256=LEEAOkUtQ9KzWEHn-QFv5yGi70B-sBOnrvztzHr0Shw,223
126
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js,sha256=pCoEaqUQj1T4-zAc9SUd__9ZGm7uL2wHQve2xwL89NI,8156
127
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/909.e3f9cc3408834a7fdcc3.js,sha256=4_nMNAiDSn_cw7UjIHEwone4fizrvqrqSgbwUWvjmoU,114571
128
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js,sha256=n_a0yu_gE7l6fauyoXXv9BZ7ZEYt2387mTfs7CbLcs4,51939
129
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt,sha256=eNJ8gc9n9IF8nW1d9sI9niuHstYzjNz5vqXx9UgWSPc,249
130
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png,sha256=svHD7-cMtTnRITFwugwsVaB9nZ-h8A61ose8z32HiRE,24850
131
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/remoteEntry.65469af996e7a96aa983.js,sha256=ZUaa-ZbnqWqpgxIpcQcRX7auvhOBkPH2YHNID5udGQs,8737
132
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/style.js,sha256=r89Jlk5v1drcwhCpv9FnC3Jig_JH4k24kZNIvxh6Htk,149
133
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json,sha256=2BGdItLJwO3fAopLl4eJvp26TEwuscYC0-yFaF-LaLU,13683
134
- calkit_python-0.41.2.data/data/share/jupyter/labextensions/calkit/install.json,sha256=DK9d8G-q-rMVlcT3rAeGIyo3REWKw6FySBZceLU9yaw,187
135
- calkit_python-0.41.2.dist-info/METADATA,sha256=oRbgEXKBGAdSQVOFsBEjg5jCPXL-QCOBzt33O7WCTX0,12070
136
- calkit_python-0.41.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
137
- calkit_python-0.41.2.dist-info/entry_points.txt,sha256=2iQBBzjTAOdk66CwS-f3ecXXW5DjUqkG1KBRj8mHI1I,133
138
- calkit_python-0.41.2.dist-info/licenses/LICENSE,sha256=9ZamCaSUTZk9rcrnf-sWFKLOHr3ws-S_dgKMegW4nw8,1056
139
- calkit_python-0.41.2.dist-info/RECORD,,
120
+ calkit_python-0.41.3.data/data/etc/jupyter/jupyter_server_config.d/calkit.json,sha256=CWrZP--JDGz8fsvbAlKr_duTL1vDriGT48SL1YCtkY0,81
121
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/package.json,sha256=INxx8QhvSl7KP6yL7A7-m2-FIsnB__mYEWG_Eq5RP-c,6651
122
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig,sha256=6w0b5y8LztEv2tErajJ5d39yBryld02crX0CG3zZFNs,6509
123
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json,sha256=YSpIrwpwB-lQBk9Mwv-npedWTOOZreO6nlWZhqlWyGI,840
124
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js,sha256=mixXcqFUZukj7_jQVxHTavsYl7cdZv83sEe4phJgkh0,59893
125
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js,sha256=LEEAOkUtQ9KzWEHn-QFv5yGi70B-sBOnrvztzHr0Shw,223
126
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js,sha256=pCoEaqUQj1T4-zAc9SUd__9ZGm7uL2wHQve2xwL89NI,8156
127
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/909.e3f9cc3408834a7fdcc3.js,sha256=4_nMNAiDSn_cw7UjIHEwone4fizrvqrqSgbwUWvjmoU,114571
128
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js,sha256=n_a0yu_gE7l6fauyoXXv9BZ7ZEYt2387mTfs7CbLcs4,51939
129
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt,sha256=eNJ8gc9n9IF8nW1d9sI9niuHstYzjNz5vqXx9UgWSPc,249
130
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png,sha256=svHD7-cMtTnRITFwugwsVaB9nZ-h8A61ose8z32HiRE,24850
131
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/remoteEntry.65469af996e7a96aa983.js,sha256=ZUaa-ZbnqWqpgxIpcQcRX7auvhOBkPH2YHNID5udGQs,8737
132
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/style.js,sha256=r89Jlk5v1drcwhCpv9FnC3Jig_JH4k24kZNIvxh6Htk,149
133
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json,sha256=2BGdItLJwO3fAopLl4eJvp26TEwuscYC0-yFaF-LaLU,13683
134
+ calkit_python-0.41.3.data/data/share/jupyter/labextensions/calkit/install.json,sha256=DK9d8G-q-rMVlcT3rAeGIyo3REWKw6FySBZceLU9yaw,187
135
+ calkit_python-0.41.3.dist-info/METADATA,sha256=HXScoQzLK-wBoXGUB8R9_eeYWP6GtacnWfwSntLCL7s,12070
136
+ calkit_python-0.41.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
137
+ calkit_python-0.41.3.dist-info/entry_points.txt,sha256=2iQBBzjTAOdk66CwS-f3ecXXW5DjUqkG1KBRj8mHI1I,133
138
+ calkit_python-0.41.3.dist-info/licenses/LICENSE,sha256=9ZamCaSUTZk9rcrnf-sWFKLOHr3ws-S_dgKMegW4nw8,1056
139
+ calkit_python-0.41.3.dist-info/RECORD,,