calkit-python 0.48.1__py3-none-any.whl → 0.48.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/calc.py +15 -9
- calkit/cli/check.py +92 -2
- calkit/cli/describe.py +4 -1
- calkit/cli/latex.py +477 -1
- calkit/cli/main/core.py +59 -17
- calkit/cli/update.py +233 -0
- calkit/docx.py +7 -1
- calkit/dvc/core.py +210 -0
- calkit/environments.py +6 -0
- calkit/git.py +83 -8
- calkit/latex.py +260 -10
- calkit/markdown.py +156 -5
- calkit/models/base.py +13 -0
- calkit/models/core.py +44 -44
- calkit/models/io.py +8 -6
- calkit/models/iteration.py +6 -4
- calkit/models/pipeline.py +114 -10
- calkit/pipeline.py +53 -4
- calkit/questions.py +61 -34
- calkit/resources/devcontainer/devcontainer.json +1 -1
- calkit/resources/vscode/settings.json +1 -1
- calkit/tests/cli/main/test_core.py +112 -1
- calkit/tests/cli/test_check.py +85 -0
- calkit/tests/cli/test_latex.py +241 -0
- calkit/tests/cli/test_update.py +170 -0
- calkit/tests/dvc/test_core.py +93 -0
- calkit/tests/test_docx.py +106 -9
- calkit/tests/test_environments.py +10 -0
- calkit/tests/test_git.py +29 -0
- calkit/tests/test_markdown.py +88 -0
- calkit/tests/test_pipeline.py +10 -0
- calkit/tests/test_questions.py +60 -0
- {calkit_python-0.48.1.dist-info → calkit_python-0.48.3.dist-info}/METADATA +3 -1
- {calkit_python-0.48.1.dist-info → calkit_python-0.48.3.dist-info}/RECORD +50 -49
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/506.c34b070098184e33.js +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/57d13a7399cec3c5.png +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/616.528427eb54a4a0c0.js +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/740.6bf87276e9e5788a.js +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/899.f9b9f9bf705a6493.js +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/935.46ecc6bf99aa593a.js +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.109a3a8379365e59.js +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
- {calkit_python-0.48.1.data → calkit_python-0.48.3.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
- {calkit_python-0.48.1.dist-info → calkit_python-0.48.3.dist-info}/WHEEL +0 -0
- {calkit_python-0.48.1.dist-info → calkit_python-0.48.3.dist-info}/entry_points.txt +0 -0
- {calkit_python-0.48.1.dist-info → calkit_python-0.48.3.dist-info}/licenses/LICENSE +0 -0
calkit/calc.py
CHANGED
|
@@ -4,14 +4,20 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Annotated, Literal
|
|
6
6
|
|
|
7
|
-
from pydantic import BaseModel, Discriminator, model_validator
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Discriminator, model_validator
|
|
8
8
|
|
|
9
9
|
DTYPES = {"int": int, "float": float, "str": str}
|
|
10
10
|
DEFAULT_IN_TYPE = "float"
|
|
11
11
|
DEFAULT_OUT_TYPE = "float"
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class _Model(BaseModel):
|
|
15
|
+
# As calkit.models.base.Model, which importing here would import
|
|
16
|
+
# calkit.models, which imports this
|
|
17
|
+
model_config = ConfigDict(defer_build=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Input(_Model):
|
|
15
21
|
name: str
|
|
16
22
|
description: str | None = None
|
|
17
23
|
dtype: Literal["int", "float", "str"] = DEFAULT_IN_TYPE
|
|
@@ -19,14 +25,14 @@ class Input(BaseModel):
|
|
|
19
25
|
max: int | float | None = None
|
|
20
26
|
|
|
21
27
|
|
|
22
|
-
class Output(
|
|
28
|
+
class Output(_Model):
|
|
23
29
|
name: str
|
|
24
30
|
description: str | None = None
|
|
25
31
|
dtype: Literal["int", "float", "str"] = DEFAULT_OUT_TYPE
|
|
26
32
|
template: str | None = None
|
|
27
33
|
|
|
28
34
|
|
|
29
|
-
class Calculation(
|
|
35
|
+
class Calculation(_Model):
|
|
30
36
|
kind: str
|
|
31
37
|
params: dict = {}
|
|
32
38
|
name: str | None = None
|
|
@@ -124,7 +130,7 @@ class Calculation(BaseModel):
|
|
|
124
130
|
return template.format(**(inputs | {out_name: res}))
|
|
125
131
|
|
|
126
132
|
|
|
127
|
-
class FormulaParams(
|
|
133
|
+
class FormulaParams(_Model):
|
|
128
134
|
formula: str
|
|
129
135
|
|
|
130
136
|
|
|
@@ -139,7 +145,7 @@ class Formula(Calculation):
|
|
|
139
145
|
return arithmetic_eval.evaluate(self.params.formula, inputs)
|
|
140
146
|
|
|
141
147
|
|
|
142
|
-
class LinearParams(
|
|
148
|
+
class LinearParams(_Model):
|
|
143
149
|
coeffs: dict[str, float]
|
|
144
150
|
offset: float = 0.0
|
|
145
151
|
|
|
@@ -163,7 +169,7 @@ class Linear(Calculation):
|
|
|
163
169
|
return val
|
|
164
170
|
|
|
165
171
|
|
|
166
|
-
class LookupTableParams(
|
|
172
|
+
class LookupTableParams(_Model):
|
|
167
173
|
x_values: list[float]
|
|
168
174
|
y_values: list[float]
|
|
169
175
|
method: Literal["floor", "ceil", "round", "interpolate"] = "interpolate"
|
|
@@ -176,7 +182,7 @@ class LookupTable(Calculation):
|
|
|
176
182
|
params: LookupTableParams
|
|
177
183
|
|
|
178
184
|
|
|
179
|
-
class HttpRequestParams(
|
|
185
|
+
class HttpRequestParams(_Model):
|
|
180
186
|
url: str
|
|
181
187
|
inputs_as_params: bool = True # Otherwise, use body
|
|
182
188
|
method: Literal["get", "post", "put"] = "get"
|
|
@@ -210,7 +216,7 @@ class HttpRequest(Calculation):
|
|
|
210
216
|
return resp.text
|
|
211
217
|
|
|
212
218
|
|
|
213
|
-
class XGBoostModelParams(
|
|
219
|
+
class XGBoostModelParams(_Model):
|
|
214
220
|
path: str
|
|
215
221
|
type: Literal["classifier", "regressor"]
|
|
216
222
|
|
calkit/cli/check.py
CHANGED
|
@@ -2026,6 +2026,95 @@ def check_pipeline(
|
|
|
2026
2026
|
)
|
|
2027
2027
|
|
|
2028
2028
|
|
|
2029
|
+
@check_app.command(name="storage")
|
|
2030
|
+
def check_storage(
|
|
2031
|
+
fix: Annotated[
|
|
2032
|
+
bool,
|
|
2033
|
+
typer.Option(
|
|
2034
|
+
"--fix", help="Move each path to where it's declared to be stored."
|
|
2035
|
+
),
|
|
2036
|
+
] = False,
|
|
2037
|
+
as_json: Annotated[
|
|
2038
|
+
bool, typer.Option("--json", help="Output the problems as JSON.")
|
|
2039
|
+
] = False,
|
|
2040
|
+
) -> None:
|
|
2041
|
+
"""Check that paths are tracked where they're declared to be stored.
|
|
2042
|
+
|
|
2043
|
+
That is, nothing is tracked by both Git and DVC, no pipeline output
|
|
2044
|
+
stored in Git is ignored by it, and nothing in Git is too large for it.
|
|
2045
|
+
With --fix, the pipeline's declarations, or a file's .dvc file, decide
|
|
2046
|
+
where each path belongs, and the changes are staged.
|
|
2047
|
+
"""
|
|
2048
|
+
import calkit.dvc
|
|
2049
|
+
import calkit.git
|
|
2050
|
+
import calkit.pipeline
|
|
2051
|
+
|
|
2052
|
+
if fix and as_json:
|
|
2053
|
+
raise_error("--json can't be used with --fix")
|
|
2054
|
+
problems = calkit.dvc.get_storage_problems()
|
|
2055
|
+
if as_json:
|
|
2056
|
+
typer.echo(json.dumps(problems, indent=2))
|
|
2057
|
+
if not fix:
|
|
2058
|
+
if not as_json:
|
|
2059
|
+
for problem in problems:
|
|
2060
|
+
warn(problem["message"])
|
|
2061
|
+
if any(p["kind"] != "large" for p in problems):
|
|
2062
|
+
if not as_json:
|
|
2063
|
+
typer.echo("Run 'calkit check storage --fix' to fix these")
|
|
2064
|
+
raise typer.Exit(1)
|
|
2065
|
+
if not problems and not as_json:
|
|
2066
|
+
calkit.echo("✅ Every path is stored where it's declared to be")
|
|
2067
|
+
return
|
|
2068
|
+
repo = calkit.git.get_repo()
|
|
2069
|
+
gitignores = calkit.git.get_dirty_gitignores(repo)
|
|
2070
|
+
fixed = []
|
|
2071
|
+
unfixed = []
|
|
2072
|
+
# Pipeline outputs are recorded in DVC, which records their stages'
|
|
2073
|
+
# inputs too, so only for stages that are up to date
|
|
2074
|
+
to_record = []
|
|
2075
|
+
for problem in problems:
|
|
2076
|
+
path = os.path.abspath(problem["path"])
|
|
2077
|
+
if problem["store_in"] == "dvc":
|
|
2078
|
+
repo.git.rm("-r", "-q", "--cached", "--", path)
|
|
2079
|
+
calkit.git.ensure_path_is_ignored(repo, path)
|
|
2080
|
+
if problem["kind"] == "both":
|
|
2081
|
+
to_record.append(problem)
|
|
2082
|
+
elif problem["store_in"] == "git":
|
|
2083
|
+
calkit.git.ensure_path_is_not_ignored(repo, path)
|
|
2084
|
+
repo.git.add("--", path)
|
|
2085
|
+
else:
|
|
2086
|
+
unfixed.append(problem)
|
|
2087
|
+
continue
|
|
2088
|
+
fixed.append(problem)
|
|
2089
|
+
typer.echo(f"Fixed: {problem['message']}")
|
|
2090
|
+
# None when it can't be told which are stale, so none are recorded
|
|
2091
|
+
stale: set[str] | None = set()
|
|
2092
|
+
if any(p["stage"] for p in to_record):
|
|
2093
|
+
status = calkit.pipeline.get_status(
|
|
2094
|
+
check_environments=False, clean_notebooks=False
|
|
2095
|
+
)
|
|
2096
|
+
if not status.errors:
|
|
2097
|
+
stale = {n.split("@")[0] for n in status.stale_stage_names}
|
|
2098
|
+
else:
|
|
2099
|
+
stale = None
|
|
2100
|
+
for problem in to_record:
|
|
2101
|
+
stage = problem["stage"]
|
|
2102
|
+
if stage and (stale is None or stage.split("@")[0] in stale):
|
|
2103
|
+
typer.echo(f"Run '{stage}' to record {problem['path']} in DVC")
|
|
2104
|
+
continue
|
|
2105
|
+
target = problem["dvc_file"] or stage
|
|
2106
|
+
if calkit.dvc.run_dvc_command(["commit", "-f", "-q", target]) != 0:
|
|
2107
|
+
warn(f"Couldn't record {problem['path']} in DVC")
|
|
2108
|
+
for message in calkit.git.stage_gitignores(repo, gitignores):
|
|
2109
|
+
warn(message)
|
|
2110
|
+
for problem in unfixed:
|
|
2111
|
+
warn(problem["message"])
|
|
2112
|
+
if fixed:
|
|
2113
|
+
typer.echo("Commit the staged changes")
|
|
2114
|
+
if any(p["kind"] != "large" for p in unfixed):
|
|
2115
|
+
raise typer.Exit(1)
|
|
2116
|
+
|
|
2117
|
+
|
|
2029
2118
|
@check_app.command(name="call")
|
|
2030
2119
|
def check_call(
|
|
2031
2120
|
cmd: Annotated[str, typer.Argument(help="Command to check.")],
|
|
@@ -2082,8 +2171,9 @@ def check_questions(
|
|
|
2082
2171
|
pushed, or pinned to a Git ref that doesn't exist); broken references
|
|
2083
2172
|
(a key that doesn't resolve, a placeholder that names no evidence, a
|
|
2084
2173
|
label missing from the LaTeX); evidence the pipeline would rebuild;
|
|
2085
|
-
and evidence from a frozen stage, or downstream of one,
|
|
2086
|
-
|
|
2174
|
+
and evidence from a frozen stage, or downstream of one, that no longer
|
|
2175
|
+
matches dvc.lock, which won't be rerun, unless the citation pins a
|
|
2176
|
+
git_ref. Subprojects' questions are checked too.
|
|
2087
2177
|
|
|
2088
2178
|
Evidence pinned with a git_ref is checked at that ref rather than in
|
|
2089
2179
|
the working tree. Exits with an error if any answered question is
|
calkit/cli/describe.py
CHANGED
|
@@ -9,7 +9,6 @@ import typer
|
|
|
9
9
|
|
|
10
10
|
import calkit
|
|
11
11
|
from calkit.cli import AliasGroup, echo_json, raise_error
|
|
12
|
-
from calkit.environments import get_env_lock_fpath
|
|
13
12
|
|
|
14
13
|
describe_app = typer.Typer(cls=AliasGroup, no_args_is_help=True)
|
|
15
14
|
|
|
@@ -20,6 +19,10 @@ def _describe_env(env: dict, env_name: str) -> dict:
|
|
|
20
19
|
The keys here are a stable contract for tools consuming ``--json``, so
|
|
21
20
|
they are always present, even if their value is null.
|
|
22
21
|
"""
|
|
22
|
+
# Here rather than at the top, since every command imports this module;
|
|
23
|
+
# what that saves is a question in decisions/calkit.yaml
|
|
24
|
+
from calkit.environments import get_env_lock_fpath
|
|
25
|
+
|
|
23
26
|
return {
|
|
24
27
|
"kind": env.get("kind"),
|
|
25
28
|
"spec_path": env.get("path"),
|