calkit-python 0.37.0__py3-none-any.whl → 0.37.2__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 (31) hide show
  1. calkit/cli/config.py +8 -1
  2. calkit/cli/main/core.py +4 -4
  3. calkit/cli/new.py +1 -3
  4. calkit/cli/update.py +1 -3
  5. calkit/dvc/core.py +47 -11
  6. calkit/dvc/zip.py +73 -9
  7. calkit/pipeline.py +31 -19
  8. calkit/releases.py +15 -10
  9. calkit/tests/dvc/test_core.py +69 -0
  10. calkit/tests/dvc/test_zip.py +8 -1
  11. calkit/tests/test_releases.py +6 -6
  12. {calkit_python-0.37.0.dist-info → calkit_python-0.37.2.dist-info}/METADATA +1 -1
  13. {calkit_python-0.37.0.dist-info → calkit_python-0.37.2.dist-info}/RECORD +31 -31
  14. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
  15. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/install.json +0 -0
  16. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
  17. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
  18. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
  19. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js +0 -0
  20. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js +0 -0
  21. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js +0 -0
  22. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/909.651be47ca47390b78a92.js +0 -0
  23. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js +0 -0
  24. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  25. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png +0 -0
  26. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.c091821b3d7f2d287a67.js +0 -0
  27. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
  28. {calkit_python-0.37.0.data → calkit_python-0.37.2.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
  29. {calkit_python-0.37.0.dist-info → calkit_python-0.37.2.dist-info}/WHEEL +0 -0
  30. {calkit_python-0.37.0.dist-info → calkit_python-0.37.2.dist-info}/entry_points.txt +0 -0
  31. {calkit_python-0.37.0.dist-info → calkit_python-0.37.2.dist-info}/licenses/LICENSE +0 -0
calkit/cli/config.py CHANGED
@@ -70,6 +70,13 @@ def unset_config_value(key: str):
70
70
  @config_app.command(name="setup-remote", help="Alias for 'remote'.")
71
71
  @config_app.command(name="remote")
72
72
  def setup_remote(
73
+ ck: Annotated[
74
+ bool,
75
+ typer.Option(
76
+ "--ck",
77
+ help="Use a ck:// URL for the 'calkit' DVC remote.",
78
+ ),
79
+ ] = False,
73
80
  no_commit: Annotated[
74
81
  bool,
75
82
  typer.Option(
@@ -81,7 +88,7 @@ def setup_remote(
81
88
  the local config.
82
89
  """
83
90
  try:
84
- configure_remote()
91
+ configure_remote(use_ck=ck)
85
92
  set_remote_auth()
86
93
  except subprocess.CalledProcessError:
87
94
  if not os.path.isfile(".dvc/config"):
calkit/cli/main/core.py CHANGED
@@ -347,8 +347,6 @@ def get_status(
347
347
  clean_notebooks=True,
348
348
  compile_to_dvc=True,
349
349
  )
350
- for error in pipeline_status.errors:
351
- warn(error)
352
350
  if pipeline_status.failed_environment_checks:
353
351
  warn(
354
352
  "Failed pipeline environment checks for: "
@@ -469,7 +467,9 @@ def get_status(
469
467
  print_sep("Pipeline")
470
468
  # Nicely format the results from pipeline status
471
469
  if pipeline_status and pipeline_status.errors:
472
- warn("Pipeline status unavailable due to errors.")
470
+ warn("Pipeline status unavailable due to errors:")
471
+ for error in pipeline_status.errors:
472
+ warn(error)
473
473
  return
474
474
  if pipeline_status and not pipeline_status.has_pipeline:
475
475
  typer.echo("This project has no pipeline.")
@@ -482,7 +482,7 @@ def get_status(
482
482
  continue
483
483
  typer.echo(f" {typer.style(stage_name, fg='yellow')}:")
484
484
  if stale_stage.modified_command:
485
- typer.echo(" modified command")
485
+ typer.echo(" modified command")
486
486
  # Show stale outputs for this stage
487
487
  if stale_stage.stale_outputs:
488
488
  typer.echo(" stale outputs:")
calkit/cli/new.py CHANGED
@@ -7,7 +7,6 @@ import os
7
7
  import pathlib
8
8
  import shutil
9
9
  import subprocess
10
- import zipfile
11
10
  from enum import Enum
12
11
 
13
12
  import dotenv
@@ -2686,8 +2685,7 @@ def new_release(
2686
2685
  zip_path = release_files_dir + "/archive.zip"
2687
2686
  all_paths = calkit.releases.ls_files()
2688
2687
  typer.echo(f"Adding files to {zip_path}")
2689
- with zipfile.ZipFile(zip_path, "w") as zipf:
2690
- calkit.releases.add_paths_to_zip(zipf, all_paths)
2688
+ calkit.releases.zip_paths(zip_path, all_paths)
2691
2689
  typer.echo("Checking extracted project release archive")
2692
2690
  try:
2693
2691
  calkit.releases.check_project_release_archive(
calkit/cli/update.py CHANGED
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import os
7
- import zipfile
8
7
  from datetime import datetime
9
8
 
10
9
  import git
@@ -266,8 +265,7 @@ def update_release(
266
265
  zip_path = release_files_dir + "/archive.zip"
267
266
  all_paths = calkit.releases.ls_files()
268
267
  typer.echo(f"Adding files to {zip_path}")
269
- with zipfile.ZipFile(zip_path, "w") as zipf:
270
- calkit.releases.add_paths_to_zip(zipf, all_paths)
268
+ calkit.releases.zip_paths(zip_path, all_paths)
271
269
  typer.echo("Checking project release archive")
272
270
  try:
273
271
  calkit.releases.check_project_release_archive(zip_path)
calkit/dvc/core.py CHANGED
@@ -11,6 +11,7 @@ from typing import Any
11
11
 
12
12
  import dvc.repo
13
13
  import git
14
+ from configobj import ConfigObj
14
15
  from dvc.utils.objects import cached_property
15
16
  from dvc_objects.fs.base import ObjectFileSystem
16
17
  from fsspec import Callback
@@ -251,10 +252,10 @@ def run_dvc_command(argv: list[str], cwd: str | None = None) -> int:
251
252
  return run_dvc_cli(argv)
252
253
 
253
254
 
254
- def configure_remote(wdir: str | None = None) -> str:
255
+ def configure_remote(wdir: str | None = None, use_ck: bool = False) -> str:
255
256
  """Configure a DVC remote for the current project.
256
257
 
257
- TODO: Use the ck:// scheme.
258
+ TODO: Use the ck:// scheme by default once it's deemed stable.
258
259
  """
259
260
  try:
260
261
  project_name = calkit.detect_project_name(wdir=wdir)
@@ -275,8 +276,12 @@ def configure_remote(wdir: str | None = None) -> str:
275
276
  if not url.endswith(".git"):
276
277
  url += ".git"
277
278
  repo.git.remote(["add", "origin", url])
278
- base_url = calkit.cloud.get_base_url()
279
- remote_url = f"{base_url}/projects/{project_name}/dvc"
279
+ if use_ck:
280
+ clear_remote_local_http_auth(wdir=wdir)
281
+ remote_url = f"ck://{project_name}"
282
+ else:
283
+ base_url = calkit.cloud.get_base_url()
284
+ remote_url = f"{base_url}/projects/{project_name}/dvc"
280
285
  remote_name = get_app_name()
281
286
  result = run_dvc_command(
282
287
  ["remote", "add", "-d", "-f", remote_name, remote_url],
@@ -284,17 +289,47 @@ def configure_remote(wdir: str | None = None) -> str:
284
289
  )
285
290
  if result != 0:
286
291
  raise RuntimeError(f"Failed to add DVC remote {remote_name}")
287
- result = run_dvc_command(
288
- ["remote", "modify", remote_name, "auth", "custom"],
289
- cwd=wdir,
290
- )
291
- if result != 0:
292
- raise RuntimeError(
293
- f"Failed to configure auth for DVC remote {remote_name}"
292
+ if not use_ck:
293
+ result = run_dvc_command(
294
+ ["remote", "modify", remote_name, "auth", "custom"],
295
+ cwd=wdir,
294
296
  )
297
+ if result != 0:
298
+ raise RuntimeError(
299
+ f"Failed to configure auth for DVC remote {remote_name}"
300
+ )
295
301
  return remote_name
296
302
 
297
303
 
304
+ def clear_remote_local_http_auth(
305
+ remote_name: str | None = None, wdir: str | None = None
306
+ ) -> None:
307
+ """Remove HTTP-specific local auth settings for a DVC remote.
308
+
309
+ This clears values written to ``.dvc/config.local`` by HTTP auth setup.
310
+ """
311
+ if remote_name is None:
312
+ remote_name = get_app_name()
313
+ config_local = Path(wdir or ".") / ".dvc" / "config.local"
314
+ if not config_local.is_file():
315
+ return
316
+ cfg = ConfigObj(str(config_local), encoding="utf-8")
317
+ section_name = f'remote "{remote_name}"'
318
+ remote = cfg.get(section_name)
319
+ if not isinstance(remote, dict):
320
+ return
321
+ changed = False
322
+ for option in ("custom_auth_header", "password", "auth"):
323
+ if option in remote:
324
+ remote.pop(option)
325
+ changed = True
326
+ if not changed:
327
+ return
328
+ if not remote:
329
+ cfg.pop(section_name, None)
330
+ cfg.write()
331
+
332
+
298
333
  def set_remote_auth(
299
334
  remote_name: str | None = None,
300
335
  always_auth: bool = False,
@@ -312,6 +347,7 @@ def set_remote_auth(
312
347
  remotes = get_remotes(wdir=wdir)
313
348
  remote_url = remotes.get(remote_name, "")
314
349
  if remote_url.startswith("ck://"):
350
+ clear_remote_local_http_auth(remote_name=remote_name, wdir=wdir)
315
351
  logger.info(
316
352
  f"Remote {remote_name} uses ck:// scheme; skipping HTTP auth setup"
317
353
  )
calkit/dvc/zip.py CHANGED
@@ -25,6 +25,7 @@ from tqdm import tqdm
25
25
  import calkit
26
26
  import calkit.git
27
27
  from calkit.core import DVC_SIZE_THRESH_BYTES
28
+ from calkit.dvc.core import run_dvc_command
28
29
 
29
30
  LOCAL_DIR = ".calkit/local"
30
31
  ZIPS_DIR = ".calkit/zip"
@@ -38,6 +39,12 @@ ZIP_CANDIDATE_AVG_FILE_SIZE_BYTES = 10_000_000 # 10 MB
38
39
  # Minimum number of files a directory must contain to be a zip candidate;
39
40
  # a single large file is better tracked directly by DVC
40
41
  ZIP_CANDIDATE_MIN_FILE_COUNT = 10
42
+ # Favor speed for dvc-zip by default; users can tune 0..9 via env var
43
+ # CALKIT_DVC_ZIP_COMPRESS_LEVEL
44
+ ZIP_COMPRESS_LEVEL = 1
45
+ # Use Python zipfile by default; set CALKIT_DVC_ZIP_USE_SYSTEM=1 to try
46
+ # the system zip/unzip tools instead (may be faster for very large files)
47
+ ZIP_USE_SYSTEM_CLI = False
41
48
 
42
49
 
43
50
  def _check_local_dir() -> Path:
@@ -285,16 +292,59 @@ def get_zip_path(workspace_path: str) -> str:
285
292
  raise ValueError(f"No zip path defined for {workspace_path}")
286
293
 
287
294
 
295
+ def _get_zip_compress_level() -> int:
296
+ raw = os.getenv("CALKIT_DVC_ZIP_COMPRESS_LEVEL", str(ZIP_COMPRESS_LEVEL))
297
+ try:
298
+ level = int(raw)
299
+ except ValueError:
300
+ typer.echo(
301
+ "Invalid CALKIT_DVC_ZIP_COMPRESS_LEVEL value; "
302
+ f"using default {ZIP_COMPRESS_LEVEL}.",
303
+ err=True,
304
+ )
305
+ return ZIP_COMPRESS_LEVEL
306
+ return min(max(level, 0), 9)
307
+
308
+
309
+ def _should_use_system_zip_cli() -> bool:
310
+ raw = os.getenv("CALKIT_DVC_ZIP_USE_SYSTEM")
311
+ if raw is None:
312
+ return ZIP_USE_SYSTEM_CLI
313
+ return raw.strip().lower() not in {"0", "false", "no", "off"}
314
+
315
+
316
+ def _iter_files(path: str):
317
+ for foldername, _, filenames in os.walk(path):
318
+ for filename in filenames:
319
+ yield os.path.join(foldername, filename)
320
+
321
+
288
322
  def zip_(workspace_path: str, zip_path: str):
289
323
  """Zip a path."""
324
+ zip_path = os.path.abspath(zip_path)
290
325
  output_dir = os.path.dirname(zip_path)
291
326
  os.makedirs(output_dir, exist_ok=True)
292
- all_files = [
293
- os.path.join(foldername, filename)
294
- for foldername, _, filenames in os.walk(workspace_path)
295
- for filename in filenames
296
- ]
297
- with ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zip_file:
327
+ compress_level = _get_zip_compress_level()
328
+ if _should_use_system_zip_cli() and shutil.which("zip"):
329
+ try:
330
+ subprocess.run(
331
+ ["zip", "-q", f"-{compress_level}", "-r", zip_path, "."],
332
+ check=True,
333
+ cwd=workspace_path,
334
+ )
335
+ return
336
+ except Exception:
337
+ typer.echo(
338
+ "System zip failed; falling back to Python zipfile.",
339
+ err=True,
340
+ )
341
+ all_files = list(_iter_files(workspace_path))
342
+ with ZipFile(
343
+ zip_path,
344
+ "w",
345
+ compression=zipfile.ZIP_DEFLATED,
346
+ compresslevel=compress_level,
347
+ ) as zip_file:
298
348
  for file_path in tqdm(all_files, desc="Zipping", unit="file"):
299
349
  zip_file.write(
300
350
  file_path, os.path.relpath(file_path, workspace_path)
@@ -303,11 +353,25 @@ def zip_(workspace_path: str, zip_path: str):
303
353
 
304
354
  def unzip(workspace_path: str, zip_path: str):
305
355
  """Unzip from zip to workspace."""
356
+ zip_path = os.path.abspath(zip_path)
306
357
  input_dir = os.path.dirname(workspace_path)
307
358
  if input_dir:
308
359
  os.makedirs(input_dir, exist_ok=True)
360
+ if _should_use_system_zip_cli() and shutil.which("unzip"):
361
+ try:
362
+ os.makedirs(workspace_path, exist_ok=True)
363
+ subprocess.run(
364
+ ["unzip", "-oq", zip_path, "-d", workspace_path],
365
+ check=True,
366
+ )
367
+ return
368
+ except Exception:
369
+ typer.echo(
370
+ "System unzip failed; falling back to Python zipfile.",
371
+ err=True,
372
+ )
309
373
  with ZipFile(zip_path, "r") as zip_file:
310
- members = zip_file.namelist()
374
+ members = zip_file.infolist()
311
375
  for member in tqdm(members, desc="Unzipping", unit="file"):
312
376
  zip_file.extract(member, workspace_path)
313
377
 
@@ -407,7 +471,7 @@ def sync_one(
407
471
  if zip_deleted and direction == "to-zip":
408
472
  typer.echo(f"Rezipping '{workspace_path}' (zip was deleted)")
409
473
  zip_(workspace_path=workspace_path, zip_path=zip_path)
410
- subprocess.run(["dvc", "add", zip_path], check=True)
474
+ run_dvc_command(["add", zip_path])
411
475
  zip_hash = get_hash(zip_path)
412
476
  # Workspace was deleted but zip exists and direction is to-workspace:
413
477
  # restore workspace
@@ -429,7 +493,7 @@ def sync_one(
429
493
  if workspace_changed and (direction in ["to-zip", "both"]):
430
494
  typer.echo(f"Zipping '{workspace_path}' (workspace has changed)")
431
495
  zip_(workspace_path=workspace_path, zip_path=zip_path)
432
- subprocess.run(["dvc", "add", zip_path], check=True)
496
+ run_dvc_command(["add", zip_path])
433
497
  zip_hash = get_hash(zip_path)
434
498
  # If we unzip, we need to update the hash
435
499
  if zip_changed and (direction in ["to-workspace", "both"]):
calkit/pipeline.py CHANGED
@@ -383,10 +383,31 @@ def get_status(
383
383
  if v != ["always changed"] and not k.endswith(".dvc")
384
384
  }
385
385
  stages_config = ck_info.get("pipeline", {}).get("stages", {})
386
+ # Build an ordered list of stage names from dvc.yaml to preserve
387
+ # pipeline order, since dvc_repo.status() returns stages alphabetically
388
+ dvc_yaml_stages: list[str] = []
389
+ if os.path.isfile("dvc.yaml"):
390
+ try:
391
+ with open("dvc.yaml") as f:
392
+ dvc_yaml = calkit.ryaml.load(f)
393
+ dvc_yaml_stages = list(
394
+ (dvc_yaml or {}).get("stages", {}).keys()
395
+ )
396
+ except Exception:
397
+ pass
386
398
  ordered_stale_stages = {}
387
- for stage_name in stages_config.keys():
388
- if stage_name not in raw_stale_stages:
389
- continue
399
+ # First, add stages in dvc.yaml order, matching expanded stage names
400
+ # (e.g. benchmark-boom@1-3-1) against their base template name
401
+ # (benchmark-boom) using the position in dvc.yaml
402
+ dvc_yaml_stage_order = {
403
+ name: i for i, name in enumerate(dvc_yaml_stages)
404
+ }
405
+
406
+ def _stage_sort_key(stage_name: str) -> int:
407
+ base = stage_name.split("@")[0]
408
+ return dvc_yaml_stage_order.get(base, len(dvc_yaml_stages))
409
+
410
+ for stage_name in sorted(raw_stale_stages.keys(), key=_stage_sort_key):
390
411
  status_data = raw_stale_stages[stage_name]
391
412
  ordered_stale_stages[stage_name] = StaleStage.from_status_data(
392
413
  status_data=status_data,
@@ -399,23 +420,14 @@ def get_status(
399
420
  )
400
421
  ],
401
422
  )
402
- # Keep any stale stages not present in ck_info at the end
403
- for stage_name, status_data in raw_stale_stages.items():
404
- if stage_name in ordered_stale_stages:
405
- continue
406
- ordered_stale_stages[stage_name] = StaleStage.from_status_data(
407
- status_data=status_data,
408
- configured_outputs=[
409
- output.get("path", str(output))
410
- if isinstance(output, dict)
411
- else str(output)
412
- for output in stages_config.get(stage_name, {}).get(
413
- "outputs", []
414
- )
415
- ],
416
- )
417
423
  result["stale_stages"] = ordered_stale_stages
418
- return PipelineStatus.model_validate(result)
424
+ return PipelineStatus(
425
+ has_pipeline=result["has_pipeline"],
426
+ environment_checks=result["environment_checks"],
427
+ cleaned_notebooks=result["cleaned_notebooks"],
428
+ stale_stages=result["stale_stages"],
429
+ errors=result["errors"],
430
+ )
419
431
  finally:
420
432
  if wdir is not None:
421
433
  os.chdir(prev_cwd)
calkit/releases.py CHANGED
@@ -184,16 +184,21 @@ def make_dvc_md5s(
184
184
  return resp
185
185
 
186
186
 
187
- def add_paths_to_zip(zipf: zipfile.ZipFile, paths: list[str]) -> None:
188
- """Add files to a zip archive, expanding directories recursively."""
189
- for path in paths:
190
- if os.path.isdir(path):
191
- for root, _, files in os.walk(path):
192
- for filename in files:
193
- fpath = os.path.join(root, filename)
194
- zipf.write(fpath)
195
- elif os.path.isfile(path):
196
- zipf.write(path)
187
+ def zip_paths(zip_path: str, paths: list[str]) -> None:
188
+ """Create a compressed ZIP from a list of file or directory paths."""
189
+ with zipfile.ZipFile(
190
+ zip_path,
191
+ "w",
192
+ compression=zipfile.ZIP_DEFLATED,
193
+ ) as zipf:
194
+ for path in paths:
195
+ if os.path.isdir(path):
196
+ for root, _, files in os.walk(path):
197
+ for filename in files:
198
+ fpath = os.path.join(root, filename)
199
+ zipf.write(fpath)
200
+ elif os.path.isfile(path):
201
+ zipf.write(path)
197
202
 
198
203
 
199
204
  def populate_dvc_cache():
@@ -4,6 +4,8 @@ import os
4
4
  import subprocess
5
5
 
6
6
  import dvc.repo
7
+ import git
8
+ from configobj import ConfigObj
7
9
  from dvc.config_schema import SCHEMA, Invalid
8
10
  from dvc_objects.fs import known_implementations
9
11
 
@@ -89,3 +91,70 @@ def test_list_files_paths(tmp_dir):
89
91
  repo = dvc.repo.Repo()
90
92
  repo.add("file1.txt") # type: ignore
91
93
  assert "file1.txt" in calkit.dvc.list_paths()
94
+
95
+
96
+ def test_configure_remote_ck_uses_ck_scheme_and_skips_http_auth(monkeypatch):
97
+ monkeypatch.setattr(calkit, "detect_project_name", lambda wdir=None: "o/p")
98
+
99
+ class DummyRepo:
100
+ def remote(self):
101
+ return "origin"
102
+
103
+ monkeypatch.setattr(git, "Repo", lambda wdir=None: DummyRepo())
104
+ calls = []
105
+ events = []
106
+
107
+ def fake_run(argv, cwd=None):
108
+ events.append("run")
109
+ calls.append((argv, cwd))
110
+ return 0
111
+
112
+ monkeypatch.setattr(calkit.dvc.core, "run_dvc_command", fake_run)
113
+ monkeypatch.setattr(
114
+ calkit.dvc.core,
115
+ "clear_remote_local_http_auth",
116
+ lambda remote_name=None, wdir=None: events.append("clear"),
117
+ )
118
+ out = calkit.dvc.configure_remote(use_ck=True)
119
+ assert out == calkit.config.get_app_name()
120
+ assert events and events[0] == "clear"
121
+ assert calls == [
122
+ (
123
+ [
124
+ "remote",
125
+ "add",
126
+ "-d",
127
+ "-f",
128
+ calkit.config.get_app_name(),
129
+ "ck://o/p",
130
+ ],
131
+ None,
132
+ )
133
+ ]
134
+
135
+
136
+ def test_set_remote_auth_ck_remote_clears_local_http_auth(
137
+ monkeypatch, tmp_path
138
+ ):
139
+ remote_name = calkit.config.get_app_name()
140
+ monkeypatch.setattr(
141
+ calkit.dvc.core,
142
+ "get_remotes",
143
+ lambda wdir=None: {remote_name: "ck://owner/proj"},
144
+ )
145
+ dvc_dir = tmp_path / ".dvc"
146
+ dvc_dir.mkdir()
147
+ fpath = dvc_dir / "config.local"
148
+ with open(fpath, "w") as f:
149
+ f.write(
150
+ f'[remote "{remote_name}"]\n'
151
+ " custom_auth_header = Authorization\n"
152
+ " password = Bearer token\n"
153
+ " url = https://example.com\n"
154
+ )
155
+ calkit.dvc.set_remote_auth(wdir=str(tmp_path))
156
+ cfg = ConfigObj(str(fpath), encoding="utf-8")
157
+ section = cfg[f'remote "{remote_name}"']
158
+ assert "custom_auth_header" not in section
159
+ assert "password" not in section
160
+ assert section["url"] == "https://example.com" # type: ignore
@@ -200,7 +200,14 @@ def test_get_sync_status(tmp_dir):
200
200
 
201
201
 
202
202
  def test_sync_one(tmp_dir, monkeypatch):
203
- monkeypatch.setattr("calkit.dvc.zip.subprocess.run", lambda *_, **__: None)
203
+ real_run = calkit.dvc.zip.subprocess.run
204
+
205
+ def _run(cmd, *args, **kwargs):
206
+ if cmd and cmd[0] == "dvc":
207
+ return None
208
+ return real_run(cmd, *args, **kwargs)
209
+
210
+ monkeypatch.setattr("calkit.dvc.zip.subprocess.run", _run)
204
211
  # to-zip: zips input and writes sync record
205
212
  src = tmp_dir / "src"
206
213
  src.mkdir()
@@ -10,10 +10,10 @@ import git
10
10
  import pytest
11
11
 
12
12
  from calkit.releases import (
13
- add_paths_to_zip,
14
13
  check_project_release_archive,
15
14
  create_bibtex,
16
15
  ls_files,
16
+ zip_paths,
17
17
  )
18
18
 
19
19
 
@@ -135,16 +135,16 @@ def test_create_bibtex():
135
135
  assert len(entries) == 1
136
136
 
137
137
 
138
- def test_add_paths_to_zip_expands_directory_contents(tmp_dir):
138
+ def test_zip_paths(tmp_dir):
139
139
  os.makedirs("data/sub", exist_ok=True)
140
140
  with open("data/sub/file.txt", "w") as f:
141
141
  f.write("hello")
142
142
  with open("root.txt", "w") as f:
143
143
  f.write("root")
144
144
  zip_path = "archive.zip"
145
- with zipfile.ZipFile(zip_path, "w") as zipf:
146
- add_paths_to_zip(zipf, ["data", "root.txt"])
145
+ zip_paths(zip_path, ["data", "root.txt"])
147
146
  with zipfile.ZipFile(zip_path) as zipf:
148
147
  names = set(zipf.namelist())
149
- assert "data/sub/file.txt" in names
150
- assert "root.txt" in names
148
+ assert "data/sub/file.txt" in names
149
+ assert "root.txt" in names
150
+ assert zipf.getinfo("root.txt").compress_type == zipfile.ZIP_DEFLATED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.37.0
3
+ Version: 0.37.2
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -24,30 +24,30 @@ calkit/notebooks.py,sha256=7OISqRi1sOtPO5xFoi41zKxOu0kNIvp8U5WDI2PZcYw,8800
24
24
  calkit/office.py,sha256=mZqCxkIsmWthBAeJZ5SiGe21fn5zD2zXsy1E6nQIpWE,1291
25
25
  calkit/ops.py,sha256=lLlZAFeplU8uLeMavr99d__Iof-YYwKijus74WfLipk,854
26
26
  calkit/overleaf.py,sha256=0LcVvMmYu8sfwEGG1GTPedNuk7hOIQTr6Hwv4u9S5Xs,23540
27
- calkit/pipeline.py,sha256=QcCn3Z1Gs8l-yJp3oIZrt9nhocJdJoTLbiSNmKLozWs,25521
28
- calkit/releases.py,sha256=DfMqfM-f_g5Bdyl-jkpA03Pm_bVlOO7UBnXtbFAe6sM,8112
27
+ calkit/pipeline.py,sha256=RoQ_bjCJTQte_eaRnpiaxEzoV0U6Xv9rvV5Akej5lsI,26054
28
+ calkit/releases.py,sha256=GmvH1Qm7UYTKeDZPnNBoNQDLZ4Dxei1PfUQdNNUk09A,8244
29
29
  calkit/server.py,sha256=xXbah6MJUyjZNWA_cjiwBkZXKs_xNvQDILCoNHTSahw,21712
30
30
  calkit/cli/__init__.py,sha256=WE2C1rXrwby5mEs8NGbGLwt4K-4uO7PHUoYS4GkxBKo,92
31
31
  calkit/cli/check.py,sha256=dfvgYd7BSlccOHFt4_EfdtEVms5kQy6ZTxEg-f1DDOU,42885
32
32
  calkit/cli/cloud.py,sha256=-0sasNFp8Ir6dut8unFfRA6XhU1jKM0s9MjgI5sGZ1c,579
33
- calkit/cli/config.py,sha256=T4KzeCJy_O_GKae2rEN1-4rPdvklqbIUZdEwl-gQxV4,9862
33
+ calkit/cli/config.py,sha256=ozMABJ0ngGps5LJvfnXfDpYYjac_zsaJLLwH09ix2Yw,10037
34
34
  calkit/cli/core.py,sha256=I3GCmE9VfBwzVgVu4HUuOFtjpM1a9TtbKOtXHkrD4s0,770
35
35
  calkit/cli/describe.py,sha256=bCq-YPiWSM4nDyi6KJXBPqiGjD2eW3xh9xY2oaAK3LQ,347
36
36
  calkit/cli/import_.py,sha256=McH6Q85L47CFTHRGkWfL7t2SEBlw7PL9ChK-hOyfCjQ,17872
37
37
  calkit/cli/latex.py,sha256=dhcbVNcR3YjVSD5mo-OBtZN1FhH4mv4mlGnuP19EbjU,6220
38
38
  calkit/cli/list.py,sha256=6kiaqR71WCQk2tJAshD29dNFbW8IvpJHmkomMhdpc9A,3987
39
- calkit/cli/new.py,sha256=uYaCteidHLRc-y8PZQil-yNK7QAM9-JTHY2JmwOESrQ,110773
39
+ calkit/cli/new.py,sha256=21L26nbEvSjoD77H7nyY2LuxPXRDIX7YIB9U-67Zqxk,110698
40
40
  calkit/cli/notebooks.py,sha256=ttDZgVZvif5w-BCU4Lf2xDH36upwzAErJhSvy_3XkJY,20333
41
41
  calkit/cli/office.py,sha256=Cg0xXChncCpESsoQlVGp4M5Ouu5aH1ELlquiu489isk,1749
42
42
  calkit/cli/overleaf.py,sha256=Nuf0BU_DktHtl4J_kziMLqDd7M1dMkAYVI3OsG7KlSQ,24807
43
43
  calkit/cli/slurm.py,sha256=mg_8xcMVDmXjmJuHvPcKYb0oEWy_tN0f2QLgxCrO5f4,12466
44
- calkit/cli/update.py,sha256=nX-UVTgrOOnDmqvQIAhzRf6lttq42Cd-2UnB_ghb4zk,19901
44
+ calkit/cli/update.py,sha256=3vlGXDoAc63z8KHNhaKmzWKvm3C1oiGK-c2Du28m1e4,19826
45
45
  calkit/cli/main/__init__.py,sha256=qu1POZPyqs33ZKfasOxv_Wc-EzVcEgK3Dt_vwFL8Bi8,65
46
- calkit/cli/main/core.py,sha256=0-OBvZQ_g6OY5KyZmrmqgSOdYI8f0NbRFs-60Ezlrro,92121
46
+ calkit/cli/main/core.py,sha256=Ds9bhOww5p6vB85zKy_KvS_sS2g9fTyF_03KJH4Fb0o,92127
47
47
  calkit/cli/main/xr.py,sha256=-hiIS6Xr3RiVAFGgH19yqvyzbcAwnEyaVBUH-qDehxY,25575
48
48
  calkit/dvc/__init__.py,sha256=g_K9o7zVXS0d_6in8UqhPpkPfxXAjGGmjLVLl7k6Nd0,34
49
- calkit/dvc/core.py,sha256=DkV7ECbIrZfud5SONpU_Att7HOPAcHQWbU2DBjoBKas,17036
50
- calkit/dvc/zip.py,sha256=lt7kITGfeBs1Bw086V-GywTLgRtrHGXdp1krbU6J3D4,16514
49
+ calkit/dvc/core.py,sha256=relc7smVSQLVxpWjt_aCCbHZiWF8ZFD2nKckOjk96eE,18274
50
+ calkit/dvc/zip.py,sha256=Rcf7oEbkuazGsPcvKVhdiRrqNMqRIIp9rmnOPidEhKU,18572
51
51
  calkit/jupyterlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  calkit/jupyterlab/routes.py,sha256=vipSlBXVHIzdnZryNhZBpeTWnqcw2wGiOJYwO7Lxd_o,51010
53
53
  calkit/models/__init__.py,sha256=vGYF4MZBGzDk1BPBzpp1bRb59RCJ2Bj2kzHuVVRacug,120
@@ -82,7 +82,7 @@ calkit/tests/test_magics.py,sha256=1O_hYPLMBVQJj8rLCFV3sU2DejlLLnk6Tv4_Tr-FZHQ,2
82
82
  calkit/tests/test_matlab.py,sha256=7yFKOYDCtxYIkRyFOwTIxlvzx03Pac0FKGIUT4Zx7zA,7589
83
83
  calkit/tests/test_notebooks.py,sha256=6xUpHjCbCv08oJkArj-NkTUMlz9zzm8pf1qam3s27M0,2692
84
84
  calkit/tests/test_pipeline.py,sha256=NrI6kcMtH7QvlVYDIvESPb2FXJWNQB6s0UHSLT4LtNg,29014
85
- calkit/tests/test_releases.py,sha256=Ib4pHsibA447rrxHrGwOsdHrC6qAjbjwIgQ1fwxJO_g,4925
85
+ calkit/tests/test_releases.py,sha256=1em4Bj6PPzCt_PGPrrN82CQpmlYYy4t6GlY4U8jcQBA,4914
86
86
  calkit/tests/test_templates.py,sha256=RN4RIgmuFlmFYQFgfJUYS-KaLmie8fnWzmfg5Ay1egk,430
87
87
  calkit/tests/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
88
  calkit/tests/cli/test_check.py,sha256=y0gR1bCuhZnx0Oatcr2nyaGBjCFe9I45SqNq2P_6DzI,2703
@@ -98,30 +98,30 @@ calkit/tests/cli/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
98
98
  calkit/tests/cli/main/test_core.py,sha256=ZRWujJmI2Dtr5FfQxUSXnNfpa-SagkV90y1dHUbGGAs,28728
99
99
  calkit/tests/cli/main/test_xr.py,sha256=UhtzJEjRiHgE_p_tdjGyYS7I9yy_Ubs2W2lSmLPpBLU,22386
100
100
  calkit/tests/dvc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
- calkit/tests/dvc/test_core.py,sha256=QD8JTvSdUcDQhfpoVnAdlq3SikTaAzEhRo-X2fDBi-0,2576
102
- calkit/tests/dvc/test_zip.py,sha256=HClb6kGg0MFnBrTHeMQsUje_syR9CgYT7YCtYK-Rw9w,11040
101
+ calkit/tests/dvc/test_core.py,sha256=urN4FIPMVgYfVJxqzH1d7x0hpMI3h2KeArnslfFRDYI,4616
102
+ calkit/tests/dvc/test_zip.py,sha256=H6L3uJdUpsaNQcOPKnmRxMb5Imr_MaDQFoAnMrZMErU,11212
103
103
  calkit/tests/jupyterlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
104
  calkit/tests/jupyterlab/test_routes.py,sha256=TEEyoHiKnzzKOkQsJcMPKdqpKFQ25yBCvVSquqe9nsE,408
105
105
  calkit/tests/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  calkit/tests/models/test_iteration.py,sha256=vjuDKwyYQAV4w1qF9VJWQEHksq3KRd6gtT39_a-RhnI,644
107
107
  calkit/tests/models/test_pipeline.py,sha256=yefZIG0oHO3xBSwEc3BLx6JOuqKr8GwZvlx1eMcr6SI,9429
108
- calkit_python-0.37.0.data/data/etc/jupyter/jupyter_server_config.d/calkit.json,sha256=CWrZP--JDGz8fsvbAlKr_duTL1vDriGT48SL1YCtkY0,81
109
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/package.json,sha256=HWflZoLFLgne6BqHLI_cZTBzro050PoPTabnCp5NRQc,6651
110
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig,sha256=6w0b5y8LztEv2tErajJ5d39yBryld02crX0CG3zZFNs,6509
111
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json,sha256=YSpIrwpwB-lQBk9Mwv-npedWTOOZreO6nlWZhqlWyGI,840
112
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js,sha256=mixXcqFUZukj7_jQVxHTavsYl7cdZv83sEe4phJgkh0,59893
113
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js,sha256=LEEAOkUtQ9KzWEHn-QFv5yGi70B-sBOnrvztzHr0Shw,223
114
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js,sha256=pCoEaqUQj1T4-zAc9SUd__9ZGm7uL2wHQve2xwL89NI,8156
115
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/909.651be47ca47390b78a92.js,sha256=ZRvkfKRzkLeKkhSUg3qX-df9A80XTd0F0CPcXMfmb-Q,114572
116
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js,sha256=n_a0yu_gE7l6fauyoXXv9BZ7ZEYt2387mTfs7CbLcs4,51939
117
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt,sha256=eNJ8gc9n9IF8nW1d9sI9niuHstYzjNz5vqXx9UgWSPc,249
118
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png,sha256=svHD7-cMtTnRITFwugwsVaB9nZ-h8A61ose8z32HiRE,24850
119
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/remoteEntry.c091821b3d7f2d287a67.js,sha256=wJGCGz1_LSh6Z9lt5VFlfXs92iKQNlLUJshrAQl4ziw,8737
120
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/style.js,sha256=r89Jlk5v1drcwhCpv9FnC3Jig_JH4k24kZNIvxh6Htk,149
121
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json,sha256=2BGdItLJwO3fAopLl4eJvp26TEwuscYC0-yFaF-LaLU,13683
122
- calkit_python-0.37.0.data/data/share/jupyter/labextensions/calkit/install.json,sha256=DK9d8G-q-rMVlcT3rAeGIyo3REWKw6FySBZceLU9yaw,187
123
- calkit_python-0.37.0.dist-info/METADATA,sha256=6yQrDoWTAqxYKghlLhbbNpjz5WN19BAE1_X65hcV0-E,8777
124
- calkit_python-0.37.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
125
- calkit_python-0.37.0.dist-info/entry_points.txt,sha256=59JWYjwz2N3yBOk_9vYUzsMsSs-It1LW8gYE8NsTUJ4,113
126
- calkit_python-0.37.0.dist-info/licenses/LICENSE,sha256=9ZamCaSUTZk9rcrnf-sWFKLOHr3ws-S_dgKMegW4nw8,1056
127
- calkit_python-0.37.0.dist-info/RECORD,,
108
+ calkit_python-0.37.2.data/data/etc/jupyter/jupyter_server_config.d/calkit.json,sha256=CWrZP--JDGz8fsvbAlKr_duTL1vDriGT48SL1YCtkY0,81
109
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/package.json,sha256=HWflZoLFLgne6BqHLI_cZTBzro050PoPTabnCp5NRQc,6651
110
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig,sha256=6w0b5y8LztEv2tErajJ5d39yBryld02crX0CG3zZFNs,6509
111
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json,sha256=YSpIrwpwB-lQBk9Mwv-npedWTOOZreO6nlWZhqlWyGI,840
112
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/502.9a2c5772a15466e923ef.js,sha256=mixXcqFUZukj7_jQVxHTavsYl7cdZv83sEe4phJgkh0,59893
113
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/695.2c41003a452d43d2b358.js,sha256=LEEAOkUtQ9KzWEHn-QFv5yGi70B-sBOnrvztzHr0Shw,223
114
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/867.a42a046aa5108f54f8fb.js,sha256=pCoEaqUQj1T4-zAc9SUd__9ZGm7uL2wHQve2xwL89NI,8156
115
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/909.651be47ca47390b78a92.js,sha256=ZRvkfKRzkLeKkhSUg3qX-df9A80XTd0F0CPcXMfmb-Q,114572
116
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js,sha256=n_a0yu_gE7l6fauyoXXv9BZ7ZEYt2387mTfs7CbLcs4,51939
117
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt,sha256=eNJ8gc9n9IF8nW1d9sI9niuHstYzjNz5vqXx9UgWSPc,249
118
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/b2f1c3efe70cb539d121.png,sha256=svHD7-cMtTnRITFwugwsVaB9nZ-h8A61ose8z32HiRE,24850
119
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/remoteEntry.c091821b3d7f2d287a67.js,sha256=wJGCGz1_LSh6Z9lt5VFlfXs92iKQNlLUJshrAQl4ziw,8737
120
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/style.js,sha256=r89Jlk5v1drcwhCpv9FnC3Jig_JH4k24kZNIvxh6Htk,149
121
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json,sha256=2BGdItLJwO3fAopLl4eJvp26TEwuscYC0-yFaF-LaLU,13683
122
+ calkit_python-0.37.2.data/data/share/jupyter/labextensions/calkit/install.json,sha256=DK9d8G-q-rMVlcT3rAeGIyo3REWKw6FySBZceLU9yaw,187
123
+ calkit_python-0.37.2.dist-info/METADATA,sha256=1w6BYdTyAKHWTh-mOZ6_3UFXRHlg9cE_f2Iof4qWQng,8777
124
+ calkit_python-0.37.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
125
+ calkit_python-0.37.2.dist-info/entry_points.txt,sha256=59JWYjwz2N3yBOk_9vYUzsMsSs-It1LW8gYE8NsTUJ4,113
126
+ calkit_python-0.37.2.dist-info/licenses/LICENSE,sha256=9ZamCaSUTZk9rcrnf-sWFKLOHr3ws-S_dgKMegW4nw8,1056
127
+ calkit_python-0.37.2.dist-info/RECORD,,