calkit-python 0.45.1__py3-none-any.whl → 0.45.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 (25) hide show
  1. calkit/cli/main/core.py +12 -3
  2. calkit/cli/new.py +4 -1
  3. calkit/dvc/core.py +54 -0
  4. calkit/tests/cli/main/test_core.py +19 -0
  5. calkit/tests/cli/main/test_subprojects.py +101 -0
  6. calkit/tests/cli/test_new.py +50 -6
  7. {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/METADATA +2 -2
  8. {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/RECORD +25 -25
  9. {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/entry_points.txt +1 -1
  10. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
  11. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
  12. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
  13. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
  14. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js +0 -0
  15. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js +0 -0
  16. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js +0 -0
  17. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/909.6d8285ce7c45878ac508.js +0 -0
  18. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js +0 -0
  19. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  20. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png +0 -0
  21. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.d7a43c7948f690d37d19.js +0 -0
  22. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
  23. {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
  24. {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/WHEEL +0 -0
  25. {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/licenses/LICENSE +0 -0
calkit/cli/main/core.py CHANGED
@@ -286,9 +286,18 @@ def init(
286
286
  needs_own_repo = True
287
287
  if needs_own_repo:
288
288
  subprocess.run(["git", "init"])
289
- result = calkit.dvc.init(force=force)
290
- if result != 0:
291
- raise_error("Failed to initialize DVC")
289
+ # A DVC repo can already exist here without a calkit.yaml, e.g., on
290
+ # platforms that set one up for us, so leave it alone rather than
291
+ # failing on DVC's refusal to initialize over an existing .dvc. The
292
+ # marker is .dvc/config, as elsewhere: a .dvc directory without one is
293
+ # an interrupted initialization, which has nothing worth keeping and
294
+ # which DVC will only initialize over with --force.
295
+ if os.path.isfile(os.path.join(".dvc", "config")) and not force:
296
+ typer.echo("DVC is already initialized")
297
+ else:
298
+ result = calkit.dvc.init(force=force or os.path.isdir(".dvc"))
299
+ if result != 0:
300
+ raise_error("Failed to initialize DVC")
292
301
  # Ensure autostage is enabled for DVC
293
302
  result = calkit.dvc.run_dvc_command(["config", "core.autostage", "true"])
294
303
  if result != 0:
calkit/cli/new.py CHANGED
@@ -3710,6 +3710,9 @@ def new_release(
3710
3710
  f"'{project_name}'."
3711
3711
  )
3712
3712
  record_id = None
3713
+ # SPDX IDs are case-insensitive, but the InvenioRDM license vocabulary
3714
+ # used by Zenodo and CaltechDATA only accepts them lowercased
3715
+ license_ids = [lid.strip().lower() for lid in license_ids]
3713
3716
  # Detect project license IDs if necessary
3714
3717
  if not license_ids:
3715
3718
  license_file = calkit.licenses.find_license_file()
@@ -3756,7 +3759,7 @@ def new_release(
3756
3759
  publication_date=release_date,
3757
3760
  version=name,
3758
3761
  publisher=publisher_name,
3759
- rights=[{"id": lid for lid in license_ids}],
3762
+ rights=[{"id": lid} for lid in license_ids],
3760
3763
  )
3761
3764
  # Add related identifiers
3762
3765
  github_url = calkit.detect_project_github_url()
calkit/dvc/core.py CHANGED
@@ -98,6 +98,60 @@ if sys.platform == "win32":
98
98
  _tolerate_lock_release_failures()
99
99
 
100
100
 
101
+ def _hash_dirs_inside_nested_repos() -> None:
102
+ """Make DVC hash directories that live inside nested DVC repos.
103
+
104
+ DVC's ``DvcIgnoreFilter`` prunes nested DVC repos (e.g. isolated
105
+ subprojects) when walking, so the parent never indexes a subproject's
106
+ ``dvc.yaml`` and ``.dvc`` files as its own. The same walk hashes directory
107
+ deps and outs, though, and the subrepo check is order-dependent:
108
+ ``_get_trie_pattern`` hands the starting directory's ``dnames`` to every
109
+ ancestor it visits, so a walk that starts inside the nested repo never
110
+ registers it, while the root-level index walk does. Whichever touches the
111
+ ignore trie first wins for the rest of the process. In practice a parent
112
+ stage depending on a directory inside an isolated subproject hashes it as
113
+ *empty* on ``status`` but not always on ``repro``, so ``dvc.lock`` and
114
+ ``dvc status`` disagree and the stage is forever stale (or, worse, changes
115
+ in the subproject go unnoticed).
116
+
117
+ A walk that starts at or below a nested repo can only be a caller hashing
118
+ that path (the index walk always starts at the repo root), and the caller
119
+ named the path deliberately, so its contents must be hashed. Such walks
120
+ are routed through DVC's ``ignore_subrepos=False`` trie, which is built
121
+ separately, is deterministic, and still honors every ``.dvcignore`` on
122
+ the way down, including the nested repo's own.
123
+ """
124
+ from dvc.ignore import DvcIgnoreFilter
125
+ from dvc.repo import Repo
126
+
127
+ original_walk = DvcIgnoreFilter.walk
128
+
129
+ def in_nested_repo(dvcignore: Any, path: str) -> bool:
130
+ fs = dvcignore.fs
131
+ root_dir = dvcignore.root_dir
132
+ current = fs.abspath(path)
133
+ if not fs.isin(current, root_dir):
134
+ return False
135
+ while current != root_dir:
136
+ if fs.exists(fs.join(current, Repo.DVC_DIR)):
137
+ return True
138
+ parent = fs.parent(current)
139
+ if parent == current:
140
+ return False
141
+ current = parent
142
+ return False
143
+
144
+ def walk(self: Any, fs: Any, path: str, **kwargs: Any) -> Any:
145
+ if "ignore_subrepos" not in kwargs and in_nested_repo(self, path):
146
+ kwargs["ignore_subrepos"] = False
147
+ return original_walk(self, fs, path, **kwargs)
148
+
149
+ DvcIgnoreFilter.walk = walk
150
+
151
+
152
+ _hash_dirs_inside_nested_repos()
153
+
154
+
101
155
  # Default seconds to wait for DVC's repo-level lock during a pipeline run.
102
156
  #
103
157
  # DVC's own default is only 3 seconds (``dvc.lock.DEFAULT_TIMEOUT``), after
@@ -149,6 +149,25 @@ def test_init(tmp_dir):
149
149
  # --force allows re-initialization without clobbering calkit.yaml
150
150
  subprocess.check_call(["calkit", "init", "--force"], cwd="sub")
151
151
  assert calkit.load_calkit_info(wdir="sub") == ck_info
152
+ # An existing DVC repo with no calkit.yaml is left alone rather than
153
+ # failing on DVC's refusal to initialize over an existing .dvc (#1013)
154
+ os.remove(os.path.join("sub", "calkit.yaml"))
155
+ assert os.path.isdir(os.path.join("sub", ".dvc"))
156
+ result = subprocess.run(
157
+ ["calkit", "init"],
158
+ cwd="sub",
159
+ capture_output=True,
160
+ text=True,
161
+ )
162
+ assert result.returncode == 0
163
+ assert "already initialized" in result.stdout.lower()
164
+ assert os.path.isfile(os.path.join("sub", "calkit.yaml"))
165
+ assert calkit.load_calkit_info(wdir="sub") == {}
166
+ # A .dvc directory left behind by an interrupted init has no config in
167
+ # it, so it is re-initialized rather than taken for a working repo
168
+ os.remove(os.path.join("sub", ".dvc", "config"))
169
+ subprocess.check_call(["calkit", "init"], cwd="sub")
170
+ assert os.path.isfile(os.path.join("sub", ".dvc", "config"))
152
171
  # --no-commit stages the initial files without committing, so a
153
172
  # bootstrap (as run by xr) can be rolled back by restoring files
154
173
  os.makedirs("nocommit")
@@ -4,6 +4,7 @@ import os
4
4
  import subprocess
5
5
 
6
6
  import calkit
7
+ import calkit.dvc
7
8
  import calkit.pipeline
8
9
 
9
10
 
@@ -375,6 +376,106 @@ def test_isolated_subproject_external_dep(tmp_dir):
375
376
  assert not status.is_stale
376
377
 
377
378
 
379
+ def test_isolated_subproject_dir_dep(tmp_dir):
380
+ # A parent stage depending on a *directory* inside an isolated
381
+ # subproject: DVC prunes nested repos when walking, so without calkit's
382
+ # workaround the directory hashes as empty on status (but not always on
383
+ # repro), leaving the stage forever stale and blind to subproject changes.
384
+ def write_sub(b_content):
385
+ # Create the output directory here, since mkdir -p doesn't work in
386
+ # cmd.exe on Windows
387
+ os.makedirs("sub/figs", exist_ok=True)
388
+ write_ck_info(
389
+ "sub/calkit.yaml",
390
+ {
391
+ "pipeline": {
392
+ "stages": {
393
+ "make-figs": make_stage(
394
+ "echo a > figs/a.txt && "
395
+ f"echo {b_content} > figs/b.txt",
396
+ outputs=[
397
+ {"path": "figs/a.txt", "storage": "git"},
398
+ {"path": "figs/b.txt", "storage": "git"},
399
+ ],
400
+ )
401
+ }
402
+ }
403
+ },
404
+ )
405
+ calkit.pipeline.to_dvc(wdir="sub", write=True, manage_gitignore=False)
406
+
407
+ subprocess.check_call(["calkit", "init"])
408
+ init_isolated_subproject("sub", {})
409
+ write_sub("b")
410
+ write_ck_info(
411
+ "calkit.yaml",
412
+ {
413
+ "subprojects": [{"path": "sub"}],
414
+ "pipeline": {
415
+ "stages": {
416
+ "consume": make_stage(
417
+ "cat sub/figs/* > parent.txt",
418
+ inputs=["sub/figs/"],
419
+ outputs=[{"path": "parent.txt", "storage": "git"}],
420
+ )
421
+ }
422
+ },
423
+ },
424
+ )
425
+ subprocess.check_call(["calkit", "run"])
426
+ # Windows' echo leaves trailing spaces, so compare tokens
427
+ with open("parent.txt") as f:
428
+ assert f.read().split() == ["a", "b"]
429
+ # The lock must record the directory's real contents, not an empty dir
430
+ with open("dvc.lock") as f:
431
+ lock = calkit.ryaml.load(f)
432
+ figs_dep = [
433
+ d
434
+ for d in lock["stages"]["consume"]["deps"]
435
+ if d["path"] == "sub/figs/"
436
+ ][0]
437
+ assert figs_dep["nfiles"] == 2
438
+ # And status must agree with the lock
439
+ status = calkit.pipeline.get_status(
440
+ check_environments=False, compile_to_dvc=False
441
+ )
442
+ assert not status.is_stale
443
+ # Hashing must not depend on whether the index walk (which registers the
444
+ # subrepo in DVC's ignore trie) ran first
445
+ repo = calkit.dvc.get_dvc_repo()
446
+ list(repo.index.stages)
447
+ found = sorted(
448
+ os.path.relpath(p)
449
+ for p in repo.dvcignore.find(repo.fs, os.path.abspath("sub/figs"))
450
+ )
451
+ assert found == [
452
+ os.path.join("sub", "figs", "a.txt"),
453
+ os.path.join("sub", "figs", "b.txt"),
454
+ ]
455
+ # The index walk still keeps the subproject's own pipeline out of the
456
+ # parent's index
457
+ assert {s.addressing for s in repo.index.stages} == {"sub", "consume"}
458
+ # A change inside the subproject propagates to the parent stage
459
+ write_sub("c")
460
+ subprocess.check_call(["calkit", "run"])
461
+ with open("parent.txt") as f:
462
+ assert f.read().split() == ["a", "c"]
463
+ status = calkit.pipeline.get_status(
464
+ check_environments=False, compile_to_dvc=False
465
+ )
466
+ assert not status.is_stale
467
+ # Editing a file in the directory makes the parent stage stale
468
+ with open("sub/figs/a.txt", "w") as f:
469
+ f.write("z\n")
470
+ status = calkit.pipeline.get_status(
471
+ check_environments=False, compile_to_dvc=False
472
+ )
473
+ assert status.is_stale
474
+ assert [
475
+ p.rstrip("/") for p in status.stale_stages["consume"].modified_inputs
476
+ ] == ["sub/figs"]
477
+
478
+
378
479
  def test_targeted_status_uses_full_subproject_status_for_stage_targets(
379
480
  tmp_dir, monkeypatch
380
481
  ):
@@ -1100,6 +1100,10 @@ def test_new_nix_env_stages_flake(tmp_dir):
1100
1100
  def test_new_release(tmp_dir, monkeypatch, httpserver):
1101
1101
  # Set up a mock Zenodo API so the test doesn't depend on the real sandbox
1102
1102
  record_id = "test-record-abc123"
1103
+ # A new version of a record gets its own ID, which the client must switch
1104
+ # to; only the draft endpoints below are shared between the two
1105
+ version_record_id = "test-record-def456"
1106
+ any_record_id = f"(?:{record_id}|{version_record_id})"
1103
1107
  doi = "10.5072/zenodo.test123"
1104
1108
  # Point the Zenodo base URL at the local mock server and provide a dummy
1105
1109
  # token so no real credentials are needed. Both env vars are inherited
@@ -1115,26 +1119,27 @@ def test_new_release(tmp_dir, monkeypatch, httpserver):
1115
1119
  ).respond_with_json({"id": record_id, "pids": {}})
1116
1120
  # POST /records/{id}/draft/files – initiate a file upload slot
1117
1121
  httpserver.expect_request(
1118
- re.compile(rf"^/records/{record_id}/draft/files$"), method="POST"
1122
+ re.compile(rf"^/records/{any_record_id}/draft/files$"), method="POST"
1119
1123
  ).respond_with_json({"entries": []})
1120
1124
  # PUT /records/{id}/draft/files/{filename}/content – stream file bytes
1121
1125
  httpserver.expect_request(
1122
- re.compile(rf"^/records/{record_id}/draft/files/.+/content$"),
1126
+ re.compile(rf"^/records/{any_record_id}/draft/files/.+/content$"),
1123
1127
  method="PUT",
1124
1128
  ).respond_with_data("", status=200)
1125
1129
  # POST /records/{id}/draft/files/{filename}/commit – finalise upload
1126
1130
  httpserver.expect_request(
1127
- re.compile(rf"^/records/{record_id}/draft/files/.+/commit$"),
1131
+ re.compile(rf"^/records/{any_record_id}/draft/files/.+/commit$"),
1128
1132
  method="POST",
1129
1133
  ).respond_with_json({"key": "file", "status": "completed"})
1130
1134
  # POST /records/{id}/draft/pids/doi – reserve a DOI for a draft
1131
1135
  httpserver.expect_request(
1132
- re.compile(rf"^/records/{record_id}/draft/pids/doi$"), method="POST"
1136
+ re.compile(rf"^/records/{any_record_id}/draft/pids/doi$"),
1137
+ method="POST",
1133
1138
  ).respond_with_json({"pids": {"doi": {"identifier": doi}}})
1134
1139
  # GET /records/{id}/draft/files – list files already in the draft
1135
1140
  # (used by --reupload to decide which files to delete first)
1136
1141
  httpserver.expect_request(
1137
- re.compile(rf"^/records/{record_id}/draft/files$"), method="GET"
1142
+ re.compile(rf"^/records/{any_record_id}/draft/files$"), method="GET"
1138
1143
  ).respond_with_json({"entries": []})
1139
1144
  # POST /records/{id}/draft/actions/publish – publish the draft
1140
1145
  httpserver.expect_request(
@@ -1143,6 +1148,16 @@ def test_new_release(tmp_dir, monkeypatch, httpserver):
1143
1148
  ).respond_with_json(
1144
1149
  {"id": record_id, "pids": {"doi": {"identifier": doi}}}
1145
1150
  )
1151
+ # POST /records/{id}/versions – create a new version of a record, and
1152
+ # PUT /records/{new_id}/draft – set that new version's metadata; the draft
1153
+ # is only mocked under the new ID, so a client that failed to switch to it
1154
+ # would get no response here
1155
+ httpserver.expect_request(
1156
+ re.compile(rf"^/records/{record_id}/versions$"), method="POST"
1157
+ ).respond_with_json({"id": version_record_id, "pids": {}})
1158
+ httpserver.expect_request(
1159
+ re.compile(rf"^/records/{version_record_id}/draft$"), method="PUT"
1160
+ ).respond_with_json({"id": version_record_id, "pids": {}})
1146
1161
  # GET /records/{id} – fetch the published record for post-test assertions
1147
1162
  httpserver.expect_request(
1148
1163
  re.compile(rf"^/records/{record_id}$"), method="GET"
@@ -1267,7 +1282,6 @@ def test_new_release(tmp_dir, monkeypatch, httpserver):
1267
1282
  git_tags = git.Repo().tags
1268
1283
  assert "v0.1.0" in [tag.name for tag in git_tags]
1269
1284
  # Check the license is correct
1270
- # TODO: It seems like we can't use multiple license IDs with the API
1271
1285
  record_id = release["record_id"]
1272
1286
  record = calkit.invenio.get(f"/records/{record_id}")
1273
1287
  metadata = record["metadata"]
@@ -1275,6 +1289,36 @@ def test_new_release(tmp_dir, monkeypatch, httpserver):
1275
1289
  assert metadata["license"] == {"id": "cc-by-4.0"}
1276
1290
  related = metadata["related_identifiers"]
1277
1291
  assert related[0]["identifier"] == "https://github.com/calkit/test-project"
1292
+ # Issue #1582: SPDX IDs are case-insensitive, but the InvenioRDM license
1293
+ # vocabulary rejects anything but the lowercase form, so release again
1294
+ # with the licenses spelled in uppercase to check they are normalized
1295
+ subprocess.check_call(
1296
+ [
1297
+ "calkit",
1298
+ "new",
1299
+ "release",
1300
+ "--name",
1301
+ "v0.2.0",
1302
+ "--license",
1303
+ "MIT",
1304
+ "--license",
1305
+ "CC-BY-4.0",
1306
+ "--draft",
1307
+ "--no-github",
1308
+ ]
1309
+ )
1310
+ # Every set of metadata sent to the service should have carried both
1311
+ # licenses as separate lowercase rights entries, rather than collapsing
1312
+ # them into one: the first release detected them from the LICENSE file,
1313
+ # the second took them from the uppercase --license options
1314
+ rights_sent = [
1315
+ body["metadata"]["rights"]
1316
+ for request, _ in httpserver.log
1317
+ if (body := request.get_json(silent=True)) and "metadata" in body
1318
+ ]
1319
+ expected_rights = [{"id": "mit"}, {"id": "cc-by-4.0"}]
1320
+ assert len(rights_sent) >= 2
1321
+ assert rights_sent == [expected_rights] * len(rights_sent)
1278
1322
  # TODO: Test that we can delete the release
1279
1323
  # This will fail if it's not a draft
1280
1324
  # subprocess.check_call(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: calkit-python
3
- Version: 0.45.1
3
+ Version: 0.45.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
@@ -192,7 +192,7 @@ If you want to use Calkit without installing it,
192
192
  you can use uv's `uvx` command to run it directly:
193
193
 
194
194
  ```sh
195
- uvx calk9 --help
195
+ uvx ck9 --help
196
196
  ```
197
197
 
198
198
  ### Nix
@@ -46,7 +46,7 @@ calkit/cli/hub.py,sha256=ZBOlL7-mV57q_ynr4hPuhV7NlkKEkINSRksXD-jjiOg,2972
46
46
  calkit/cli/import_.py,sha256=ZAfPstDm_EoG9i8RMvVfctLOFMv0GbstkH0Wb7iq-9Q,18875
47
47
  calkit/cli/latex.py,sha256=mvNxzZkWlwJnYA_l6NFEflV0AGYHPIT1ALUxQQGHZJ0,19708
48
48
  calkit/cli/list.py,sha256=E40uJe_jRQcRin1RO4yFMUncKMpQe2syWhC54xzDhoQ,15090
49
- calkit/cli/new.py,sha256=SLhotKsToW0rNo7C2e5uLz9ESvBgnQwprkQqX51A50Y,149508
49
+ calkit/cli/new.py,sha256=t3CHyNFTnzKUAuph31HsWuUxK4iw7s28IVC5STSTxy0,149724
50
50
  calkit/cli/notebooks.py,sha256=aR4I0qatOtkrAxEG5PVLej20SBLKo8qr-lxDSzgljUQ,38836
51
51
  calkit/cli/office.py,sha256=jVSTvbl91TCzlglST5O2b8hdApZA_QHw_UiEQFJqxdc,1754
52
52
  calkit/cli/overleaf.py,sha256=PTL3MH3y3qlSCkZ39yaiWUWU6U7aiGyh6aKLA068-5U,38700
@@ -54,10 +54,10 @@ calkit/cli/scheduler.py,sha256=GQf_Hw2qVSrFH4SzSQCOdbZzZ-n2EdKgR2NoqNIqWlw,53064
54
54
  calkit/cli/sync.py,sha256=UGen0Yey6BARooI5K83eiArw4YN2ZQcRQjCocaGy6E4,2539
55
55
  calkit/cli/update.py,sha256=4CbMxL3jS2l2mF32174IYLjN0DpoqCOag814UekKaVk,48411
56
56
  calkit/cli/main/__init__.py,sha256=qu1POZPyqs33ZKfasOxv_Wc-EzVcEgK3Dt_vwFL8Bi8,65
57
- calkit/cli/main/core.py,sha256=zQoqy6uy1__2CjJpc4xm8wq5KExXvEN5rgi0vDmk9Dw,152154
57
+ calkit/cli/main/core.py,sha256=VtLEXS7xIYvnucXx7vJwMBuSi_WsfGg7y7GorpGfrO4,152737
58
58
  calkit/cli/main/xr.py,sha256=cYQBNVlRTzp6ba3EwEwZylOxVpk9ThWhy6IAD7_ahWw,37594
59
59
  calkit/dvc/__init__.py,sha256=-B6tilCb_jw7QFmJ2srILez24wDhNKUzIBhZUMt01dE,381
60
- calkit/dvc/core.py,sha256=_viZIkl5qO6Bnipru0w2zaEc2xwXuEOSYCDvlDoEaU0,36282
60
+ calkit/dvc/core.py,sha256=0yfq_p3CrejFxGEl19za9M2ZdywByZPS4MtndK_R2Mo,38637
61
61
  calkit/dvc/zip.py,sha256=nf4EzOyc5nI_OA3M6S7wwZpL7N4EQz9yCf8FbnPjlxM,21857
62
62
  calkit/jupyterlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
63
  calkit/jupyterlab/routes.py,sha256=sARrFjWnHVV-LunrgqO4DCt_x7XLhVPAqA1eaUFf7HQ,51443
@@ -135,16 +135,16 @@ calkit/tests/cli/test_hub.py,sha256=z3ne8Bnjv-V85OEh15FbAV4STrFwZKMgl_DgWwQ2wzY,
135
135
  calkit/tests/cli/test_import.py,sha256=e2BToVPebf4NalFQuIgyTNJHwbc9VMc6AYO-FAhYbQ0,663
136
136
  calkit/tests/cli/test_latex.py,sha256=92ZhSIYOiBb0TKZmODTVvkp0dU_X5ZuGvrdA4v5bzQo,10976
137
137
  calkit/tests/cli/test_list.py,sha256=5jc_OYI_rXNVv2UQc6IS8ItjQbBwEI5IDw5xKU_Uk98,9165
138
- calkit/tests/cli/test_new.py,sha256=tDat19K_ussPHlYYjqhKFKQUNNbHboiYpn79v0XGdqo,65017
138
+ calkit/tests/cli/test_new.py,sha256=q7kmZQ9IrMhDOcHuIXU0GJs0Z54SpX12vct8ntQaKLM,66977
139
139
  calkit/tests/cli/test_notebooks.py,sha256=2t1KiGhEz9H9LcIdWy4jGM05REUxJpWiIg6fSpX12ME,10678
140
140
  calkit/tests/cli/test_overleaf.py,sha256=w8tqRwOC6ilQX8wfkqppTBmcouxs61zEoq-R5G1emY8,49767
141
141
  calkit/tests/cli/test_scheduler.py,sha256=YfJi_fcRlQt1bdFyo3b8BFelA1PcH-wn5lAVXiH7Zj4,24109
142
142
  calkit/tests/cli/test_sync.py,sha256=jmhfgoMIjdihIuu9UVyHSizBDTF0hlBt2VWqEdVkAKk,4375
143
143
  calkit/tests/cli/test_update.py,sha256=i7dsfrDVD-0VA7knspzbb-6UZWHgGPVkbB6CsT0czMM,14878
144
144
  calkit/tests/cli/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
- calkit/tests/cli/main/test_core.py,sha256=a22eVhjPeK4cyFIm9uAf_jOzDStJ-GRi7cKlxdQOY4E,92505
145
+ calkit/tests/cli/main/test_core.py,sha256=LFS-3p01qVWlmG4RmB5CcRqMRVfllk0eLibWIqMb27E,93415
146
146
  calkit/tests/cli/main/test_lock.py,sha256=1iX8iSbGtr4O9OkaAi4d3gP3_QzNl6Rr6HLtbEpS6iU,9847
147
- calkit/tests/cli/main/test_subprojects.py,sha256=RKWHKoqWLgJnsBBbD5Q-DJHQ5agZdOS7ahzfPzkr878,14942
147
+ calkit/tests/cli/main/test_subprojects.py,sha256=Qs5NvwhQEFlZUvAtOyCWN243PONz8N79np5HwlgUl7E,18709
148
148
  calkit/tests/cli/main/test_xr.py,sha256=iauAWKXdzaaRFu5W91l4ZdH_ZU3h2NAn3v5p-EhAbco,28798
149
149
  calkit/tests/dvc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
150
  calkit/tests/dvc/test_core.py,sha256=LCmtZO_vLkW4GHf8nfeZvouLifVIc3VuuArkdKf6vD8,18215
@@ -159,22 +159,22 @@ calkit/tests/models/test_pipeline.py,sha256=hglkTBry1ONKbzN1HmlpefbrqAbc-668C0MQ
159
159
  calkit/agent_skills/add-pipeline-stage/SKILL.md,sha256=kpYYb-rJsS4B9p1Ub3gL21irCG5ZZ4DvWZpdJeEwPZk,3772
160
160
  calkit/agent_skills/conventions/SKILL.md,sha256=osbAtYU0AwROZHgmOoQYbqYr3JwG_v-cJAmandm7yN4,9738
161
161
  calkit/agent_skills/create-pipeline/SKILL.md,sha256=2FGw1iDQVRk5FUq79GP3lPdgvgof2Wi1O9O-abGGtgs,5422
162
- calkit_python-0.45.1.data/data/etc/jupyter/jupyter_server_config.d/calkit.json,sha256=CWrZP--JDGz8fsvbAlKr_duTL1vDriGT48SL1YCtkY0,81
163
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/package.json,sha256=tYRdVVqmPBoUJzNrMc7Da9Lk-NB54vhnplKRSlOTMIE,6224
164
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig,sha256=NBG3t7gFYb6_2J-yPU29qS7Ck9pYgm8-wC5Q1LrzQeI,6082
165
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json,sha256=YSpIrwpwB-lQBk9Mwv-npedWTOOZreO6nlWZhqlWyGI,840
166
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js,sha256=mixXcqFUZukj7_jQVxHTavsYl7cdZv83sEe4phJgkh0,59893
167
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js,sha256=LEEAOkUtQ9KzWEHn-QFv5yGi70B-sBOnrvztzHr0Shw,223
168
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js,sha256=pCoEaqUQj1T4-zAc9SUd__9ZGm7uL2wHQve2xwL89NI,8156
169
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/909.6d8285ce7c45878ac508.js,sha256=bYKFznxFh4rFCPs8tWnbZgaFGp72DuPZGdkzYCoCaSU,114583
170
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js,sha256=n_a0yu_gE7l6fauyoXXv9BZ7ZEYt2387mTfs7CbLcs4,51939
171
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt,sha256=eNJ8gc9n9IF8nW1d9sI9niuHstYzjNz5vqXx9UgWSPc,249
172
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png,sha256=svHD7-cMtTnRITFwugwsVaB9nZ-h8A61ose8z32HiRE,24850
173
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/remoteEntry.d7a43c7948f690d37d19.js,sha256=16Q8eUj2kNN9Gaeoe3hOSEUPMJ5IUHQnJF1h9DRUIJY,8737
174
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/style.js,sha256=r89Jlk5v1drcwhCpv9FnC3Jig_JH4k24kZNIvxh6Htk,149
175
- calkit_python-0.45.1.data/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json,sha256=2BGdItLJwO3fAopLl4eJvp26TEwuscYC0-yFaF-LaLU,13683
176
- calkit_python-0.45.1.dist-info/METADATA,sha256=RpRntx5V2HE6ajwpb2e1Lw4vbvOMokNZKs9cC7NZl_A,12818
177
- calkit_python-0.45.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
178
- calkit_python-0.45.1.dist-info/entry_points.txt,sha256=2iQBBzjTAOdk66CwS-f3ecXXW5DjUqkG1KBRj8mHI1I,133
179
- calkit_python-0.45.1.dist-info/licenses/LICENSE,sha256=9ZamCaSUTZk9rcrnf-sWFKLOHr3ws-S_dgKMegW4nw8,1056
180
- calkit_python-0.45.1.dist-info/RECORD,,
162
+ calkit_python-0.45.3.data/data/etc/jupyter/jupyter_server_config.d/calkit.json,sha256=CWrZP--JDGz8fsvbAlKr_duTL1vDriGT48SL1YCtkY0,81
163
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/package.json,sha256=tYRdVVqmPBoUJzNrMc7Da9Lk-NB54vhnplKRSlOTMIE,6224
164
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig,sha256=NBG3t7gFYb6_2J-yPU29qS7Ck9pYgm8-wC5Q1LrzQeI,6082
165
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json,sha256=YSpIrwpwB-lQBk9Mwv-npedWTOOZreO6nlWZhqlWyGI,840
166
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js,sha256=mixXcqFUZukj7_jQVxHTavsYl7cdZv83sEe4phJgkh0,59893
167
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js,sha256=LEEAOkUtQ9KzWEHn-QFv5yGi70B-sBOnrvztzHr0Shw,223
168
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js,sha256=pCoEaqUQj1T4-zAc9SUd__9ZGm7uL2wHQve2xwL89NI,8156
169
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/909.6d8285ce7c45878ac508.js,sha256=bYKFznxFh4rFCPs8tWnbZgaFGp72DuPZGdkzYCoCaSU,114583
170
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js,sha256=n_a0yu_gE7l6fauyoXXv9BZ7ZEYt2387mTfs7CbLcs4,51939
171
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt,sha256=eNJ8gc9n9IF8nW1d9sI9niuHstYzjNz5vqXx9UgWSPc,249
172
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png,sha256=svHD7-cMtTnRITFwugwsVaB9nZ-h8A61ose8z32HiRE,24850
173
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/remoteEntry.d7a43c7948f690d37d19.js,sha256=16Q8eUj2kNN9Gaeoe3hOSEUPMJ5IUHQnJF1h9DRUIJY,8737
174
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/style.js,sha256=r89Jlk5v1drcwhCpv9FnC3Jig_JH4k24kZNIvxh6Htk,149
175
+ calkit_python-0.45.3.data/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json,sha256=2BGdItLJwO3fAopLl4eJvp26TEwuscYC0-yFaF-LaLU,13683
176
+ calkit_python-0.45.3.dist-info/METADATA,sha256=Z809eOplrqKq9iYfrwUT3yBjzhSR1dfjXNPaHMaaG8w,12816
177
+ calkit_python-0.45.3.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
178
+ calkit_python-0.45.3.dist-info/entry_points.txt,sha256=TtpHpcv2vFVAVYxFVp0XrwCV_z7FWIOguEiypXRDoOw,131
179
+ calkit_python-0.45.3.dist-info/licenses/LICENSE,sha256=9ZamCaSUTZk9rcrnf-sWFKLOHr3ws-S_dgKMegW4nw8,1056
180
+ calkit_python-0.45.3.dist-info/RECORD,,
@@ -1,7 +1,7 @@
1
1
  [console_scripts]
2
- calk9 = calkit.cli:run
3
2
  calkit = calkit.cli:run
4
3
  ck = calkit.cli:run
4
+ ck9 = calkit.cli:run
5
5
 
6
6
  [fsspec.specs]
7
7
  ck = calkit.fs:CalkitFileSystem