calkit-python 0.48.2__py3-none-any.whl → 0.49.0__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 (76) hide show
  1. calkit/calc.py +15 -9
  2. calkit/cli/__init__.py +65 -1
  3. calkit/cli/check.py +425 -32
  4. calkit/cli/delete.py +25 -0
  5. calkit/cli/describe.py +13 -2
  6. calkit/cli/latex.py +115 -147
  7. calkit/cli/list.py +52 -0
  8. calkit/cli/main/core.py +636 -131
  9. calkit/cli/main/xr.py +55 -29
  10. calkit/cli/new.py +67 -0
  11. calkit/cli/overleaf.py +139 -109
  12. calkit/cli/scheduler.py +217 -2
  13. calkit/cli/update.py +394 -6
  14. calkit/conda.py +455 -41
  15. calkit/conditions.py +251 -0
  16. calkit/config.py +2 -25
  17. calkit/core.py +226 -54
  18. calkit/dependencies.py +6 -16
  19. calkit/docker.py +112 -0
  20. calkit/docx.py +7 -1
  21. calkit/dvc/core.py +434 -4
  22. calkit/environments.py +1229 -72
  23. calkit/git.py +79 -6
  24. calkit/latex.py +191 -24
  25. calkit/models/base.py +13 -0
  26. calkit/models/core.py +386 -260
  27. calkit/models/io.py +33 -7
  28. calkit/models/iteration.py +6 -4
  29. calkit/models/pipeline.py +213 -33
  30. calkit/models/requirements.py +216 -0
  31. calkit/overleaf.py +89 -8
  32. calkit/pipeline.py +548 -18
  33. calkit/questions.py +91 -194
  34. calkit/tests/cli/main/test_core.py +645 -8
  35. calkit/tests/cli/main/test_xr.py +8 -0
  36. calkit/tests/cli/test_check.py +411 -0
  37. calkit/tests/cli/test_delete.py +53 -0
  38. calkit/tests/cli/test_describe.py +17 -0
  39. calkit/tests/cli/test_latex.py +83 -1
  40. calkit/tests/cli/test_list.py +61 -0
  41. calkit/tests/cli/test_new.py +60 -0
  42. calkit/tests/cli/test_overleaf.py +60 -1
  43. calkit/tests/cli/test_scheduler.py +146 -0
  44. calkit/tests/cli/test_update.py +332 -0
  45. calkit/tests/dvc/test_core.py +93 -0
  46. calkit/tests/models/test_core.py +136 -1
  47. calkit/tests/test_conda.py +325 -0
  48. calkit/tests/test_conditions.py +114 -0
  49. calkit/tests/test_core.py +125 -3
  50. calkit/tests/test_docker.py +81 -0
  51. calkit/tests/test_docx.py +69 -6
  52. calkit/tests/test_environments.py +840 -1
  53. calkit/tests/test_overleaf.py +76 -0
  54. calkit/tests/test_pipeline.py +466 -3
  55. calkit/tests/test_questions.py +132 -120
  56. calkit/tests/test_workspace.py +40 -2
  57. calkit/upgrade.py +1 -1
  58. calkit/workspace.py +88 -26
  59. {calkit_python-0.48.2.dist-info → calkit_python-0.49.0.dist-info}/METADATA +5 -3
  60. {calkit_python-0.48.2.dist-info → calkit_python-0.49.0.dist-info}/RECORD +76 -72
  61. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/etc/jupyter/jupyter_server_config.d/calkit.json +0 -0
  62. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/package.json +0 -0
  63. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/package.json.orig +0 -0
  64. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/schemas/calkit/plugin.json +0 -0
  65. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/506.c34b070098184e33.js +0 -0
  66. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/57d13a7399cec3c5.png +0 -0
  67. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/616.528427eb54a4a0c0.js +0 -0
  68. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/740.6bf87276e9e5788a.js +0 -0
  69. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/899.f9b9f9bf705a6493.js +0 -0
  70. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/935.46ecc6bf99aa593a.js +0 -0
  71. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/remoteEntry.109a3a8379365e59.js +0 -0
  72. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/style.js +0 -0
  73. {calkit_python-0.48.2.data → calkit_python-0.49.0.data}/data/share/jupyter/labextensions/calkit/static/third-party-licenses.json +0 -0
  74. {calkit_python-0.48.2.dist-info → calkit_python-0.49.0.dist-info}/WHEEL +0 -0
  75. {calkit_python-0.48.2.dist-info → calkit_python-0.49.0.dist-info}/entry_points.txt +0 -0
  76. {calkit_python-0.48.2.dist-info → calkit_python-0.49.0.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 Input(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
219
+ class XGBoostModelParams(_Model):
214
220
  path: str
215
221
  type: Literal["classifier", "regressor"]
216
222
 
calkit/cli/__init__.py CHANGED
@@ -1,7 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
1
5
  from .core import * # noqa: F403
2
6
 
7
+ if TYPE_CHECKING:
8
+ import typer
9
+
3
10
 
4
11
  def run() -> None:
12
+ import os
13
+ import sys
14
+
5
15
  # Intercept ``--use-version`` before Typer/Click parses argv: Click
6
16
  # processes eager options like ``--help`` and ``--version`` (and the
7
17
  # group's ``no_args_is_help``) ahead of the callback body, so an
@@ -11,7 +21,61 @@ def run() -> None:
11
21
  _maybe_exec_with_version()
12
22
  from .main import app
13
23
 
14
- app()
24
+ # Completion lists commands, so needs them all
25
+ if any(k.startswith("_") and k.endswith("_COMPLETE") for k in os.environ):
26
+ app()
27
+ else:
28
+ _only_invoked(app, sys.argv[1:])()
29
+
30
+
31
+ def _only_invoked(
32
+ app: typer.Typer, args: list[str], top: bool = True
33
+ ) -> typer.Typer:
34
+ """Narrow ``app`` to the command or group ``args`` invoke.
35
+
36
+ Typer builds every command's options before running one, which for all
37
+ of Calkit's takes a few tenths of a second, so this leaves out the ones
38
+ that won't run. Anything else, e.g., ``--help`` or a misspelled command,
39
+ keeps them all, so help and suggestions list everything.
40
+ decisions/calkit.yaml has a question on how much that saves.
41
+ """
42
+ import copy
43
+ import re
44
+
45
+ from typer.main import get_command_name, solve_typer_info_defaults
46
+
47
+ from .main.core import APP_OPTIONS_WITH_VALUES
48
+
49
+ # Find the command name, skipping the top level's options
50
+ takes_value = {
51
+ flag for flags in APP_OPTIONS_WITH_VALUES.values() for flag in flags
52
+ }
53
+ i = 0
54
+ while i < len(args) and args[i].startswith("-") and top:
55
+ i += 2 if args[i] in takes_value else 1
56
+ if i >= len(args) or args[i].startswith("-"):
57
+ return app
58
+ name, rest = args[i], args[i + 1 :]
59
+
60
+ def names(spec: str | None) -> list[str]:
61
+ return re.split(r" ?[,|] ?", spec) if spec else []
62
+
63
+ narrowed = copy.copy(app)
64
+ for cmd in app.registered_commands:
65
+ default = cmd.callback and get_command_name(cmd.callback.__name__)
66
+ if name in names(cmd.name or default):
67
+ narrowed.registered_commands = [cmd]
68
+ narrowed.registered_groups = []
69
+ return narrowed
70
+ for group in app.registered_groups:
71
+ sub_app = group.typer_instance
72
+ if sub_app and name in names(solve_typer_info_defaults(group).name):
73
+ group = copy.copy(group)
74
+ group.typer_instance = _only_invoked(sub_app, rest, top=False)
75
+ narrowed.registered_commands = []
76
+ narrowed.registered_groups = [group]
77
+ return narrowed
78
+ return app
15
79
 
16
80
 
17
81
  def _maybe_exec_with_version() -> None: