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.
- calkit/cli/main/core.py +12 -3
- calkit/cli/new.py +4 -1
- calkit/dvc/core.py +54 -0
- calkit/tests/cli/main/test_core.py +19 -0
- calkit/tests/cli/main/test_subprojects.py +101 -0
- calkit/tests/cli/test_new.py +50 -6
- {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/METADATA +2 -2
- {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/RECORD +25 -25
- {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/entry_points.txt +1 -1
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/909.6d8285ce7c45878ac508.js +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js +0 -0
- {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
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.d7a43c7948f690d37d19.js +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
- {calkit_python-0.45.1.data → calkit_python-0.45.3.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
- {calkit_python-0.45.1.dist-info → calkit_python-0.45.3.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
):
|
calkit/tests/cli/test_new.py
CHANGED
|
@@ -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/{
|
|
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/{
|
|
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/{
|
|
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/{
|
|
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/{
|
|
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.
|
|
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
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
163
|
-
calkit_python-0.45.
|
|
164
|
-
calkit_python-0.45.
|
|
165
|
-
calkit_python-0.45.
|
|
166
|
-
calkit_python-0.45.
|
|
167
|
-
calkit_python-0.45.
|
|
168
|
-
calkit_python-0.45.
|
|
169
|
-
calkit_python-0.45.
|
|
170
|
-
calkit_python-0.45.
|
|
171
|
-
calkit_python-0.45.
|
|
172
|
-
calkit_python-0.45.
|
|
173
|
-
calkit_python-0.45.
|
|
174
|
-
calkit_python-0.45.
|
|
175
|
-
calkit_python-0.45.
|
|
176
|
-
calkit_python-0.45.
|
|
177
|
-
calkit_python-0.45.
|
|
178
|
-
calkit_python-0.45.
|
|
179
|
-
calkit_python-0.45.
|
|
180
|
-
calkit_python-0.45.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|