calkit-python 0.11.2__tar.gz → 0.11.3__tar.gz

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 (66) hide show
  1. {calkit_python-0.11.2 → calkit_python-0.11.3}/PKG-INFO +1 -1
  2. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/__init__.py +1 -1
  3. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/main.py +7 -3
  4. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/new.py +64 -0
  5. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/core.py +1 -1
  6. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/models.py +18 -3
  7. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/cli/test_main.py +17 -0
  8. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/cli/test_new.py +43 -0
  9. {calkit_python-0.11.2 → calkit_python-0.11.3}/.github/FUNDING.yml +0 -0
  10. {calkit_python-0.11.2 → calkit_python-0.11.3}/.github/workflows/publish-test.yml +0 -0
  11. {calkit_python-0.11.2 → calkit_python-0.11.3}/.github/workflows/publish.yml +0 -0
  12. {calkit_python-0.11.2 → calkit_python-0.11.3}/.gitignore +0 -0
  13. {calkit_python-0.11.2 → calkit_python-0.11.3}/LICENSE +0 -0
  14. {calkit_python-0.11.2 → calkit_python-0.11.3}/README.md +0 -0
  15. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/calc.py +0 -0
  16. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/check.py +0 -0
  17. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/__init__.py +0 -0
  18. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/check.py +0 -0
  19. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/config.py +0 -0
  20. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/core.py +0 -0
  21. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/import_.py +0 -0
  22. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/list.py +0 -0
  23. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/notebooks.py +0 -0
  24. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/office.py +0 -0
  25. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cli/update.py +0 -0
  26. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/cloud.py +0 -0
  27. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/conda.py +0 -0
  28. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/config.py +0 -0
  29. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/data.py +0 -0
  30. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/docker.py +0 -0
  31. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/dvc.py +0 -0
  32. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/git.py +0 -0
  33. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/gui.py +0 -0
  34. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/jupyter.py +0 -0
  35. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/magics.py +0 -0
  36. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/office.py +0 -0
  37. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/server.py +0 -0
  38. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/__init__.py +0 -0
  39. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/core.py +0 -0
  40. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/__init__.py +0 -0
  41. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/article/paper.tex +0 -0
  42. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/core.py +0 -0
  43. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/jfm/jfm.bst +0 -0
  44. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/jfm/jfm.cls +0 -0
  45. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  46. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/jfm/paper.tex +0 -0
  47. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/templates/latex/jfm/upmath.sty +0 -0
  48. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/__init__.py +0 -0
  49. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/cli/__init__.py +0 -0
  50. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/cli/test_list.py +0 -0
  51. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_calc.py +0 -0
  52. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_check.py +0 -0
  53. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_conda.py +0 -0
  54. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_core.py +0 -0
  55. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_dvc.py +0 -0
  56. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_jupyter.py +0 -0
  57. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_magics.py +0 -0
  58. {calkit_python-0.11.2 → calkit_python-0.11.3}/calkit/tests/test_templates.py +0 -0
  59. {calkit_python-0.11.2 → calkit_python-0.11.3}/docs/img/calkit-no-bg.png +0 -0
  60. {calkit_python-0.11.2 → calkit_python-0.11.3}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  61. {calkit_python-0.11.2 → calkit_python-0.11.3}/docs/tutorials/conda-envs.md +0 -0
  62. {calkit_python-0.11.2 → calkit_python-0.11.3}/docs/tutorials/img/run-proc.png +0 -0
  63. {calkit_python-0.11.2 → calkit_python-0.11.3}/docs/tutorials/notebook-pipeline.md +0 -0
  64. {calkit_python-0.11.2 → calkit_python-0.11.3}/docs/tutorials/procedures.md +0 -0
  65. {calkit_python-0.11.2 → calkit_python-0.11.3}/pyproject.toml +0 -0
  66. {calkit_python-0.11.2 → calkit_python-0.11.3}/test/pipeline.ipynb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.11.2
3
+ Version: 0.11.3
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://github.com/calkit/calkit
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -1,4 +1,4 @@
1
- __version__ = "0.11.2"
1
+ __version__ = "0.11.3"
2
2
 
3
3
  from .core import *
4
4
  from . import git
@@ -7,6 +7,7 @@ import functools
7
7
  import hashlib
8
8
  import json
9
9
  import os
10
+ import platform as _platform
10
11
  import subprocess
11
12
  import sys
12
13
  import time
@@ -657,7 +658,6 @@ def run_in_env(
657
658
  except subprocess.CalledProcessError:
658
659
  raise_error(f"Failed to run in {env['kind']} environment")
659
660
  elif env["kind"] == "uv-venv":
660
- # TODO: This doesn't work on Windows
661
661
  if "prefix" not in env:
662
662
  raise_error("uv-venv environments require a prefix")
663
663
  if "path" not in env:
@@ -676,8 +676,12 @@ def run_in_env(
676
676
  raise_error(f"Failed to create uv-venv at {prefix}")
677
677
  fname, ext = os.path.splitext(path)
678
678
  lock_fpath = fname + "-lock" + ext
679
+ if _platform.system() == "Windows":
680
+ activate_cmd = f"{prefix}\\Scripts\\activate"
681
+ else:
682
+ activate_cmd = f". {prefix}/bin/activate"
679
683
  check_cmd = (
680
- f". {prefix}/bin/activate "
684
+ f"{activate_cmd} "
681
685
  f"&& uv pip install -q -r {path} "
682
686
  f"&& uv pip freeze > {lock_fpath} "
683
687
  "&& deactivate"
@@ -689,7 +693,7 @@ def run_in_env(
689
693
  except subprocess.CalledProcessError:
690
694
  raise_error("Failed to check uv-venv")
691
695
  # Now run the command
692
- cmd = f". {prefix}/bin/activate && {shell_cmd} && deactivate"
696
+ cmd = f"{activate_cmd} && {shell_cmd} && deactivate"
693
697
  if verbose:
694
698
  typer.echo(f"Running command: {cmd}")
695
699
  try:
@@ -1035,3 +1035,67 @@ def new_conda_env(
1035
1035
  repo.git.add("dvc.yaml")
1036
1036
  if not no_commit and repo.git.diff("--staged"):
1037
1037
  repo.git.commit(["-m", f"Add Conda environment {name}"])
1038
+
1039
+
1040
+ @new_app.command("uv-venv")
1041
+ def new_uv_venv(
1042
+ packages: Annotated[
1043
+ list[str],
1044
+ typer.Argument(help="Packages to include in the environment."),
1045
+ ],
1046
+ name: Annotated[
1047
+ str, typer.Option("--name", "-n", help="Environment name.")
1048
+ ],
1049
+ path: Annotated[
1050
+ str, typer.Option("--path", help="Path for requirements file.")
1051
+ ] = "requirements.txt",
1052
+ prefix: Annotated[
1053
+ str, typer.Option("--prefix", help="Prefix for environment location.")
1054
+ ] = ".venv",
1055
+ description: Annotated[
1056
+ str, typer.Option("--description", help="Description.")
1057
+ ] = None,
1058
+ overwrite: Annotated[
1059
+ bool,
1060
+ typer.Option(
1061
+ "--overwrite",
1062
+ "-f",
1063
+ help="Overwrite any existing environment with this name.",
1064
+ ),
1065
+ ] = False,
1066
+ no_commit: Annotated[
1067
+ bool, typer.Option("--no-commit", help="Do not commit changes.")
1068
+ ] = False,
1069
+ ):
1070
+ """Create a new uv virtual environment."""
1071
+ if os.path.isfile(path) and not overwrite:
1072
+ raise_error("Output path already exists (use -f to overwrite)")
1073
+ repo = git.Repo()
1074
+ # Add environment to Calkit info
1075
+ ck_info = calkit.load_calkit_info()
1076
+ # If environments is a list instead of a dict, reformulate it
1077
+ envs = ck_info.get("environments", {})
1078
+ if isinstance(envs, list):
1079
+ typer.echo("Converting environments from list to dict")
1080
+ envs = {env.pop("name"): env for env in envs}
1081
+ if name in envs and not overwrite:
1082
+ raise_error(
1083
+ f"Environment with name {name} already exists "
1084
+ "(use -f to overwrite)"
1085
+ )
1086
+ packages_txt = "\n".join(packages)
1087
+ # Write environment to path
1088
+ with open(path, "w") as f:
1089
+ f.write(packages_txt)
1090
+ repo.git.add(path)
1091
+ typer.echo("Adding environment to calkit.yaml")
1092
+ env = dict(path=path, kind="uv-venv", prefix=prefix)
1093
+ if description is not None:
1094
+ env["description"] = description
1095
+ envs[name] = env
1096
+ ck_info["environments"] = envs
1097
+ with open("calkit.yaml", "w") as f:
1098
+ ryaml.dump(ck_info, f)
1099
+ repo.git.add("calkit.yaml")
1100
+ if not no_commit and repo.git.diff("--staged"):
1101
+ repo.git.commit(["-m", f"Add uv venv {name}"])
@@ -64,7 +64,7 @@ def load_calkit_info(
64
64
  wdir=None,
65
65
  process_includes: bool | str | list[str] = False,
66
66
  as_pydantic: bool = False,
67
- ) -> dict:
67
+ ) -> dict | ProjectInfo:
68
68
  """Load Calkit project information.
69
69
 
70
70
  Parameters
@@ -63,7 +63,15 @@ class ReferenceCollection(BaseModel):
63
63
 
64
64
  class Environment(BaseModel):
65
65
  kind: Literal[
66
- "conda", "docker", "pip", "poetry", "npm", "yarn", "remote-ssh"
66
+ "conda",
67
+ "docker",
68
+ "poetry",
69
+ "npm",
70
+ "yarn",
71
+ "remote-ssh",
72
+ "uv",
73
+ "pixi",
74
+ "uv-venv",
67
75
  ]
68
76
  path: str | None = None
69
77
  description: str | None = None
@@ -71,8 +79,13 @@ class Environment(BaseModel):
71
79
  default: bool | None = None
72
80
 
73
81
 
82
+ class UvVenvEnvironment(Environment):
83
+ kind: Literal["uv-venv"]
84
+ prefix: str
85
+
86
+
74
87
  class DockerEnvironment(Environment):
75
- kind: str = "docker"
88
+ kind: Literal["docker"]
76
89
  image: str
77
90
  layers: list[str] | None = None
78
91
  shell: Literal["bash", "sh"] = "sh"
@@ -202,7 +215,9 @@ class ProjectInfo(BaseModel):
202
215
  figures: list[Figure] = []
203
216
  publications: list[Publication] = []
204
217
  references: list[ReferenceCollection] = []
205
- environments: dict[str, Environment | DockerEnvironment] = {}
218
+ environments: dict[
219
+ str, Environment | DockerEnvironment | UvVenvEnvironment
220
+ ] = {}
206
221
  software: list[Software] = []
207
222
  notebooks: list[Notebook] = []
208
223
  procedures: dict[str, Procedure] = {}
@@ -89,6 +89,23 @@ def test_run_in_env(tmp_dir):
89
89
  ck_info = calkit.load_calkit_info()
90
90
  env = ck_info["environments"]["py3.10"]
91
91
  assert env.get("path") is None
92
+ # Test uv venv
93
+ subprocess.check_call(
94
+ ["calkit", "new", "uv-venv", "-n", "uv1", "polars==1.18.0"]
95
+ )
96
+ out = subprocess.check_output(
97
+ [
98
+ "calkit",
99
+ "runenv",
100
+ "-n",
101
+ "uv1",
102
+ "--",
103
+ "python",
104
+ "-c",
105
+ "'import polars; print(polars.__version__)'",
106
+ ]
107
+ ).decode().strip()
108
+ assert out == "1.18.0"
92
109
 
93
110
 
94
111
  def test_check_call():
@@ -169,3 +169,46 @@ def test_new_publication(tmp_dir):
169
169
  "calkit runenv -n my-latex-env "
170
170
  '"cd my-paper && latexmk -interaction=nonstopmode -pdf paper.tex"'
171
171
  )
172
+
173
+
174
+ def test_new_uv_venv(tmp_dir):
175
+ subprocess.check_call(["git", "init"])
176
+ subprocess.check_call(["dvc", "init"])
177
+ subprocess.check_call(
178
+ [
179
+ "calkit",
180
+ "new",
181
+ "uv-venv",
182
+ "-n",
183
+ "my-uv-venv",
184
+ "pandas>=2.0",
185
+ "matplotlib",
186
+ ]
187
+ )
188
+ ck_info = calkit.load_calkit_info(as_pydantic=True)
189
+ envs = ck_info.environments
190
+ env = envs["my-uv-venv"]
191
+ assert env.path == "requirements.txt"
192
+ assert env.prefix == ".venv"
193
+ assert env.kind == "uv-venv"
194
+ subprocess.check_call(
195
+ [
196
+ "calkit",
197
+ "new",
198
+ "uv-venv",
199
+ "-n",
200
+ "my-uv-venv2",
201
+ "--path",
202
+ "requirements-2.txt",
203
+ "--prefix",
204
+ ".venv2",
205
+ "pandas>=2.0",
206
+ "matplotlib",
207
+ ]
208
+ )
209
+ ck_info = calkit.load_calkit_info(as_pydantic=True)
210
+ envs = ck_info.environments
211
+ env = envs["my-uv-venv2"]
212
+ assert env.path == "requirements-2.txt"
213
+ assert env.prefix == ".venv2"
214
+ assert env.kind == "uv-venv"
File without changes
File without changes