calkit-python 0.11.0__tar.gz → 0.11.2__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.
- {calkit_python-0.11.0 → calkit_python-0.11.2}/PKG-INFO +1 -1
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/__init__.py +1 -1
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/main.py +7 -4
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/new.py +1 -1
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cloud.py +5 -2
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/conda.py +37 -30
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/config.py +1 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/core.py +7 -2
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/models.py +32 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_conda.py +38 -1
- {calkit_python-0.11.0 → calkit_python-0.11.2}/.github/FUNDING.yml +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/.github/workflows/publish-test.yml +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/.github/workflows/publish.yml +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/.gitignore +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/LICENSE +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/README.md +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/calc.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/check.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/check.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/config.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/core.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/import_.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/list.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/office.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/cli/update.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/data.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/docker.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/dvc.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/git.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/gui.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/jupyter.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/magics.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/office.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/server.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/__init__.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/core.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/__init__.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/article/paper.tex +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/core.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/jfm/jfm.bst +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/jfm/jfm.cls +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/jfm/paper.tex +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/templates/latex/jfm/upmath.sty +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/cli/test_main.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/cli/test_new.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_calc.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_check.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_dvc.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_magics.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/calkit/tests/test_templates.py +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/docs/img/calkit-no-bg.png +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/docs/tutorials/adding-latex-pub-docker.md +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/docs/tutorials/conda-envs.md +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/docs/tutorials/img/run-proc.png +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/docs/tutorials/notebook-pipeline.md +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/docs/tutorials/procedures.md +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/pyproject.toml +0 -0
- {calkit_python-0.11.0 → calkit_python-0.11.2}/test/pipeline.ipynb +0 -0
|
@@ -93,9 +93,9 @@ def clone(
|
|
|
93
93
|
if location is not None:
|
|
94
94
|
cmd.append(location)
|
|
95
95
|
try:
|
|
96
|
-
subprocess.
|
|
97
|
-
except
|
|
98
|
-
raise_error(
|
|
96
|
+
subprocess.check_call(cmd)
|
|
97
|
+
except subprocess.CalledProcessError:
|
|
98
|
+
raise_error("Failed to clone with Git")
|
|
99
99
|
if location is None:
|
|
100
100
|
location = url.split("/")[-1].removesuffix(".git")
|
|
101
101
|
typer.echo(f"Moving into repo dir: {location}")
|
|
@@ -109,7 +109,10 @@ def clone(
|
|
|
109
109
|
calkit.dvc.set_remote_auth(remote_name=name)
|
|
110
110
|
# DVC pull
|
|
111
111
|
if not no_dvc_pull:
|
|
112
|
-
|
|
112
|
+
try:
|
|
113
|
+
subprocess.check_call(["dvc", "pull"])
|
|
114
|
+
except subprocess.CalledProcessError:
|
|
115
|
+
raise_error("Failed to pull from DVC remote(s)")
|
|
113
116
|
|
|
114
117
|
|
|
115
118
|
@app.command(name="status")
|
|
@@ -827,7 +827,7 @@ def new_publication(
|
|
|
827
827
|
else:
|
|
828
828
|
template_type = None
|
|
829
829
|
# Check all of our inputs
|
|
830
|
-
if template_type not in ["latex"]:
|
|
830
|
+
if template_type is not None and template_type not in ["latex"]:
|
|
831
831
|
raise_error(f"Unknown template type '{template_type}'")
|
|
832
832
|
if env_name is not None and template_type != "latex":
|
|
833
833
|
raise_error("Environments can only be created for latex templates")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import warnings
|
|
6
6
|
from functools import partial
|
|
7
7
|
from typing import Literal
|
|
8
8
|
|
|
@@ -35,6 +35,7 @@ def get_token() -> str:
|
|
|
35
35
|
_tokens[get_base_url()] = token
|
|
36
36
|
# TODO: Check for expiration
|
|
37
37
|
if token is None:
|
|
38
|
+
warnings.warn("No token found; attempting email+password auth")
|
|
38
39
|
return auth()
|
|
39
40
|
return token
|
|
40
41
|
|
|
@@ -50,10 +51,12 @@ def get_headers(headers: dict | None = None) -> dict:
|
|
|
50
51
|
def auth() -> str:
|
|
51
52
|
"""Authenticate with the server and save a token."""
|
|
52
53
|
cfg = config.read()
|
|
54
|
+
if cfg.email is None or cfg.password is None:
|
|
55
|
+
raise ValueError("Config is missing email or password")
|
|
53
56
|
base_url = get_base_url()
|
|
54
57
|
resp = requests.post(
|
|
55
58
|
base_url + "/login/access-token",
|
|
56
|
-
data=dict(username=cfg.
|
|
59
|
+
data=dict(username=cfg.email, password=cfg.password),
|
|
57
60
|
)
|
|
58
61
|
token = resp.json()["access_token"]
|
|
59
62
|
_tokens[base_url] = token
|
|
@@ -2,14 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
import subprocess
|
|
6
7
|
|
|
8
|
+
from packaging.specifiers import SpecifierSet
|
|
9
|
+
from packaging.version import Version
|
|
7
10
|
from pydantic import BaseModel
|
|
8
11
|
|
|
9
12
|
import calkit
|
|
10
13
|
from calkit import ryaml
|
|
11
14
|
|
|
12
15
|
|
|
16
|
+
def _check_single(req: str, actual: str, conda: bool = False) -> bool:
|
|
17
|
+
"""Helper function for checking actual versions against requirements."""
|
|
18
|
+
req_name = re.split("[=<>]", req)[0]
|
|
19
|
+
req_spec = req.removeprefix(req_name)
|
|
20
|
+
if conda and req_spec.startswith("="):
|
|
21
|
+
req_spec = "=" + req_spec
|
|
22
|
+
if not req_spec.endswith(".*"):
|
|
23
|
+
req_spec += ".*"
|
|
24
|
+
actual_name, actual_vers = re.split("[=<>]+", actual, maxsplit=1)
|
|
25
|
+
if actual_name != req_name:
|
|
26
|
+
return False
|
|
27
|
+
actual_spec = actual.removeprefix(actual_name)
|
|
28
|
+
if conda and actual_spec.startswith("="):
|
|
29
|
+
actual_spec = "=" + actual_spec
|
|
30
|
+
version = Version(actual_vers)
|
|
31
|
+
spec = SpecifierSet(req_spec)
|
|
32
|
+
return spec.contains(version)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _check_list(req: str, actual: list[str], conda: bool = False) -> bool:
|
|
36
|
+
for installed in actual:
|
|
37
|
+
if _check_single(req, installed, conda=conda):
|
|
38
|
+
return True
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
|
|
13
42
|
class EnvCheckResult(BaseModel):
|
|
14
43
|
env_exists: bool | None = None
|
|
15
44
|
env_needs_export: bool | None = None
|
|
@@ -127,45 +156,23 @@ def check_env(
|
|
|
127
156
|
required_conda_deps.append(dep.replace("==", "="))
|
|
128
157
|
log_func("Checking conda dependencies")
|
|
129
158
|
for dep in required_conda_deps:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
else:
|
|
135
|
-
version = None
|
|
136
|
-
if version is not None and dep not in existing_conda_deps:
|
|
159
|
+
is_okay = _check_list(
|
|
160
|
+
req=dep, actual=existing_conda_deps, conda=True
|
|
161
|
+
)
|
|
162
|
+
if not is_okay:
|
|
137
163
|
log_func(f"Found missing dependency: {dep}")
|
|
138
164
|
env_needs_rebuild = True
|
|
139
165
|
break
|
|
140
|
-
elif version is None:
|
|
141
|
-
# TODO: This does not handle specification of only major or
|
|
142
|
-
# major+minor version
|
|
143
|
-
if package not in [
|
|
144
|
-
d.split("=")[0] for d in existing_conda_deps
|
|
145
|
-
]:
|
|
146
|
-
log_func(f"Found missing dependency: {dep}")
|
|
147
|
-
env_needs_rebuild = True
|
|
148
|
-
break
|
|
149
166
|
if not env_needs_rebuild and not relaxed:
|
|
150
167
|
log_func("Checking pip dependencies")
|
|
151
168
|
for dep in required_pip_deps:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
else:
|
|
157
|
-
version = None
|
|
158
|
-
if version is not None and dep not in existing_pip_deps:
|
|
169
|
+
is_okay = _check_list(
|
|
170
|
+
req=dep, actual=existing_pip_deps, conda=False
|
|
171
|
+
)
|
|
172
|
+
if not is_okay:
|
|
159
173
|
env_needs_rebuild = True
|
|
160
174
|
log_func(f"Found missing dependency: {dep}")
|
|
161
175
|
break
|
|
162
|
-
elif version is None:
|
|
163
|
-
if package not in [
|
|
164
|
-
d.split("==")[0] for d in existing_pip_deps
|
|
165
|
-
]:
|
|
166
|
-
log_func(f"Found missing dependency: {dep}")
|
|
167
|
-
env_needs_rebuild = True
|
|
168
|
-
break
|
|
169
176
|
if env_needs_rebuild:
|
|
170
177
|
res.env_needs_rebuild = True
|
|
171
178
|
log_func(f"Rebuilding {env_name} since it does not match spec")
|
|
@@ -48,6 +48,7 @@ class Settings(BaseSettings):
|
|
|
48
48
|
env_prefix="CALKIT" + get_env_suffix(sep="_") + "_",
|
|
49
49
|
)
|
|
50
50
|
username: str | None = None
|
|
51
|
+
email: str | None = None
|
|
51
52
|
token: str | None = None
|
|
52
53
|
dvc_token: str | None = None
|
|
53
54
|
dataframe_engine: Literal["pandas", "polars"] = "pandas"
|
|
@@ -16,13 +16,14 @@ except ImportError:
|
|
|
16
16
|
UTC = _timezone.utc
|
|
17
17
|
|
|
18
18
|
from datetime import datetime
|
|
19
|
-
|
|
20
19
|
from typing import Literal
|
|
21
20
|
|
|
22
21
|
import ruamel.yaml
|
|
23
22
|
from git import Repo
|
|
24
23
|
from git.exc import InvalidGitRepositoryError
|
|
25
24
|
|
|
25
|
+
from calkit.models import ProjectInfo
|
|
26
|
+
|
|
26
27
|
logging.basicConfig(level=logging.INFO)
|
|
27
28
|
logger = logging.getLogger(__package__)
|
|
28
29
|
|
|
@@ -60,7 +61,9 @@ def find_project_dirs(relative=False, max_depth=3) -> list[str]:
|
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
def load_calkit_info(
|
|
63
|
-
wdir=None,
|
|
64
|
+
wdir=None,
|
|
65
|
+
process_includes: bool | str | list[str] = False,
|
|
66
|
+
as_pydantic: bool = False,
|
|
64
67
|
) -> dict:
|
|
65
68
|
"""Load Calkit project information.
|
|
66
69
|
|
|
@@ -99,6 +102,8 @@ def load_calkit_info(
|
|
|
99
102
|
with open(include_fpath) as f:
|
|
100
103
|
include_data = ryaml.load(f)
|
|
101
104
|
info[kind][obj_name] |= include_data
|
|
105
|
+
if as_pydantic:
|
|
106
|
+
return ProjectInfo.model_validate(info)
|
|
102
107
|
return info
|
|
103
108
|
|
|
104
109
|
|
|
@@ -42,6 +42,7 @@ class Publication(_CalkitObject):
|
|
|
42
42
|
"journal-article",
|
|
43
43
|
"conference-paper",
|
|
44
44
|
"presentation",
|
|
45
|
+
"proposal",
|
|
45
46
|
"poster",
|
|
46
47
|
"report",
|
|
47
48
|
"blog",
|
|
@@ -106,6 +107,7 @@ class ProcedureInput(BaseModel):
|
|
|
106
107
|
description : str
|
|
107
108
|
Optional longer description of the input.
|
|
108
109
|
"""
|
|
110
|
+
|
|
109
111
|
name: str | None = None
|
|
110
112
|
dtype: Literal["int", "bool", "str", "float"] = None
|
|
111
113
|
units: str | None = None
|
|
@@ -143,6 +145,32 @@ class Procedure(BaseModel):
|
|
|
143
145
|
imported_from: str | None = None
|
|
144
146
|
|
|
145
147
|
|
|
148
|
+
class Release(BaseModel):
|
|
149
|
+
kind: Literal["project", "publication", "dataset", "model", "figure"]
|
|
150
|
+
url: str | None = None
|
|
151
|
+
doi: str | None = None
|
|
152
|
+
has_suffix: bool = False
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class ProjectRelease(Release):
|
|
156
|
+
kind: Literal["project"]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class PublicationRelease(Release):
|
|
160
|
+
kind: Literal["publication"]
|
|
161
|
+
path: str
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class DatasetRelease(Release):
|
|
165
|
+
kind: Literal["dataset"]
|
|
166
|
+
path: str
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class ModelRelease(Release):
|
|
170
|
+
kind: Literal["model"]
|
|
171
|
+
path: str
|
|
172
|
+
|
|
173
|
+
|
|
146
174
|
class ProjectInfo(BaseModel):
|
|
147
175
|
"""All of the project's information or metadata, written to the
|
|
148
176
|
``calkit.yaml`` file.
|
|
@@ -178,3 +206,7 @@ class ProjectInfo(BaseModel):
|
|
|
178
206
|
software: list[Software] = []
|
|
179
207
|
notebooks: list[Notebook] = []
|
|
180
208
|
procedures: dict[str, Procedure] = {}
|
|
209
|
+
releases: dict[
|
|
210
|
+
str,
|
|
211
|
+
ProjectRelease | PublicationRelease | DatasetRelease | ModelRelease,
|
|
212
|
+
] = {}
|
|
@@ -5,7 +5,25 @@ import uuid
|
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
|
-
from calkit.conda import check_env
|
|
8
|
+
from calkit.conda import _check_list, _check_single, check_env
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_check_single():
|
|
12
|
+
assert _check_single("python=3.12", "python=3.12.18", conda=True)
|
|
13
|
+
assert _check_single("python=3", "python=3.12.18", conda=True)
|
|
14
|
+
assert _check_single("python=3.12.18", "python=3.12.18", conda=True)
|
|
15
|
+
assert _check_single("python>=3.12,<3.13", "python==3.12.18", conda=False)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_check_list():
|
|
19
|
+
installed = ["python=3.12.1", "numpy=1.0.11"]
|
|
20
|
+
assert _check_list("python=3", installed, conda=True)
|
|
21
|
+
assert _check_list("numpy", installed, conda=True)
|
|
22
|
+
assert not _check_list("pandas", installed, conda=True)
|
|
23
|
+
installed = ["python==3.12.1", "numpy==1.0.11"]
|
|
24
|
+
assert _check_list("python>=3", installed, conda=False)
|
|
25
|
+
assert _check_list("numpy", installed, conda=False)
|
|
26
|
+
assert not _check_list("pandas", installed, conda=False)
|
|
9
27
|
|
|
10
28
|
|
|
11
29
|
def delete_env(name: str):
|
|
@@ -124,3 +142,22 @@ def test_check_env(tmp_dir, env_name):
|
|
|
124
142
|
)
|
|
125
143
|
res = check_env(relaxed=True)
|
|
126
144
|
assert not res.env_needs_rebuild
|
|
145
|
+
# Make sure we can handle other ways of specifying versions
|
|
146
|
+
subprocess.check_call(
|
|
147
|
+
[
|
|
148
|
+
"calkit",
|
|
149
|
+
"new",
|
|
150
|
+
"conda-env",
|
|
151
|
+
"--overwrite",
|
|
152
|
+
"-n",
|
|
153
|
+
env_name,
|
|
154
|
+
"python=3.12",
|
|
155
|
+
"--pip",
|
|
156
|
+
"numpy>=1",
|
|
157
|
+
]
|
|
158
|
+
)
|
|
159
|
+
res = check_env()
|
|
160
|
+
assert res.env_needs_rebuild
|
|
161
|
+
res = check_env()
|
|
162
|
+
assert not res.env_needs_export
|
|
163
|
+
assert not res.env_needs_rebuild
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|