recotem 2.0.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 (57) hide show
  1. recotem/__init__.py +5 -0
  2. recotem/__main__.py +14 -0
  3. recotem/_exit_codes.py +157 -0
  4. recotem/_http_fetch.py +609 -0
  5. recotem/_idmap.py +124 -0
  6. recotem/_ipython_stub.py +29 -0
  7. recotem/_log_safe.py +52 -0
  8. recotem/_metrics_bigquery.py +72 -0
  9. recotem/_metrics_watcher.py +74 -0
  10. recotem/_size_cap.py +164 -0
  11. recotem/artifact/__init__.py +11 -0
  12. recotem/artifact/format.py +233 -0
  13. recotem/artifact/io.py +406 -0
  14. recotem/artifact/signing.py +497 -0
  15. recotem/cli.py +807 -0
  16. recotem/config.py +575 -0
  17. recotem/datasource/__init__.py +10 -0
  18. recotem/datasource/base.py +164 -0
  19. recotem/datasource/bigquery.py +360 -0
  20. recotem/datasource/csv.py +585 -0
  21. recotem/datasource/registry.py +221 -0
  22. recotem/datasource/sql.py +546 -0
  23. recotem/log_redaction.py +282 -0
  24. recotem/logging.py +92 -0
  25. recotem/metadata/__init__.py +3 -0
  26. recotem/metadata/loader.py +470 -0
  27. recotem/recipe/__init__.py +26 -0
  28. recotem/recipe/envvars.py +150 -0
  29. recotem/recipe/errors.py +59 -0
  30. recotem/recipe/loader.py +803 -0
  31. recotem/recipe/models.py +338 -0
  32. recotem/serving/__init__.py +4 -0
  33. recotem/serving/_header_utils.py +36 -0
  34. recotem/serving/_naming.py +26 -0
  35. recotem/serving/app.py +912 -0
  36. recotem/serving/auth.py +298 -0
  37. recotem/serving/metrics.py +465 -0
  38. recotem/serving/registry.py +360 -0
  39. recotem/serving/routes.py +962 -0
  40. recotem/serving/schemas.py +279 -0
  41. recotem/serving/watcher.py +1480 -0
  42. recotem/training/__init__.py +8 -0
  43. recotem/training/_compat.py +30 -0
  44. recotem/training/algorithms.py +113 -0
  45. recotem/training/errors.py +79 -0
  46. recotem/training/evaluate.py +73 -0
  47. recotem/training/lock.py +363 -0
  48. recotem/training/pipeline.py +825 -0
  49. recotem/training/progress.py +244 -0
  50. recotem/training/search.py +704 -0
  51. recotem/training/split.py +145 -0
  52. recotem/version.py +1 -0
  53. recotem-2.0.0.dist-info/METADATA +430 -0
  54. recotem-2.0.0.dist-info/RECORD +57 -0
  55. recotem-2.0.0.dist-info/WHEEL +4 -0
  56. recotem-2.0.0.dist-info/entry_points.txt +8 -0
  57. recotem-2.0.0.dist-info/licenses/LICENSE +201 -0
recotem/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Recotem: recipe-driven recommender training and serving."""
2
+
3
+ from recotem.version import __version__
4
+
5
+ __all__ = ["__version__"]
recotem/__main__.py ADDED
@@ -0,0 +1,14 @@
1
+ """Enable ``python -m recotem`` invocation.
2
+
3
+ The console-script entry point ``recotem`` is the recommended way to run the
4
+ CLI, but it is unavailable when the package is installed into a venv that is
5
+ not on ``PATH``. Falling back to ``python -m recotem`` keeps the troubleshooting
6
+ advice in ``docs/getting-started.md`` honest.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from recotem.cli import app
12
+
13
+ if __name__ == "__main__":
14
+ app()
recotem/_exit_codes.py ADDED
@@ -0,0 +1,157 @@
1
+ """Canonical exit-code constants and exception-to-exit-code mapping.
2
+
3
+ Single source of truth for both ``recotem.cli`` and
4
+ ``recotem.training.pipeline`` so the two paths cannot diverge silently.
5
+
6
+ Exit code table
7
+ ---------------
8
+ | Code | Constant | Meaning |
9
+ |------|---------------------|--------------------------------------------|
10
+ | 0 | _EXIT_SUCCESS | success |
11
+ | 1 | _EXIT_UNKNOWN | unhandled / unmapped exception |
12
+ | 2 | _EXIT_RECIPE | RecipeError |
13
+ | 3 | _EXIT_DATASOURCE | DataSourceError |
14
+ | 4 | _EXIT_TRAINING | TrainingError |
15
+ | 5 | _EXIT_ARTIFACT | ArtifactError |
16
+ | 6 | _EXIT_LOCK_CONTESTED| LockContestedError |
17
+ | 7 | _EXIT_HTTP_FETCH | HttpFetchError |
18
+ | 8 | _EXIT_CONFIG | ConfigError / missing signing keys |
19
+
20
+ Design note on imports
21
+ ----------------------
22
+ All exception-class imports inside ``_map_exception_to_exit`` are deferred to
23
+ function-call time to prevent circular imports. The ``recotem.training``
24
+ package ``__init__.py`` imports ``pipeline``, which imports this module, so
25
+ any top-level import of ``recotem.training.*`` here would cause a circular
26
+ import. Deferring the imports to inside the function is safe because the
27
+ function is only called after all modules are fully initialised.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # Exit-code constants (no imports needed — just ints)
34
+ # ---------------------------------------------------------------------------
35
+
36
+ _EXIT_SUCCESS = 0
37
+ _EXIT_UNKNOWN = 1
38
+ _EXIT_RECIPE = 2
39
+ _EXIT_DATASOURCE = 3
40
+ _EXIT_TRAINING = 4
41
+ _EXIT_ARTIFACT = 5
42
+ _EXIT_LOCK_CONTESTED = 6
43
+ _EXIT_HTTP_FETCH = 7
44
+ _EXIT_CONFIG = 8
45
+
46
+
47
+ def _map_exception_to_exit(exc: BaseException) -> int: # noqa: C901
48
+ """Map a known exception type to its canonical exit code.
49
+
50
+ Checked in priority order so that the most-specific mapping wins when
51
+ exception hierarchies overlap (e.g. a subclass of TrainingError that
52
+ signals a configuration problem maps to _EXIT_CONFIG, not _EXIT_TRAINING).
53
+
54
+ All exception-class imports are deferred (inside this function) to avoid
55
+ circular imports at module level — see module docstring for details.
56
+
57
+ The ``__cause__`` chain is walked for HttpFetchError so that a
58
+ DataSourceError wrapping an HttpFetchError maps to exit 7 (transient
59
+ network) rather than exit 3 (structural datasource failure). CronJob
60
+ retry logic distinguishes the two.
61
+ """
62
+ # --- configuration errors (signing_key_missing TrainingError subclass) ---
63
+ try:
64
+ from recotem.training.errors import (
65
+ TrainingError as _TrainingError, # noqa: PLC0415
66
+ )
67
+
68
+ if isinstance(exc, _TrainingError) and getattr(exc, "code", "") in (
69
+ "signing_key_missing",
70
+ ):
71
+ return _EXIT_CONFIG
72
+ except (ImportError, AttributeError):
73
+ pass
74
+
75
+ # --- recipe errors ---
76
+ try:
77
+ from recotem.recipe.errors import RecipeError as _RecipeError # noqa: PLC0415
78
+
79
+ if isinstance(exc, _RecipeError):
80
+ return _EXIT_RECIPE
81
+ except ImportError:
82
+ pass
83
+
84
+ # --- HTTP fetch errors (checked BEFORE DataSourceError so that a
85
+ # DataSourceError wrapping an HttpFetchError still maps to exit 7).
86
+ # CronJob retry semantics distinguish transient HTTP/SSRF failures (7)
87
+ # from structural data-source failures (3).
88
+ try:
89
+ from recotem._http_fetch import (
90
+ HttpFetchError as _HttpFetchError, # noqa: PLC0415
91
+ )
92
+
93
+ # Walk the __cause__ chain — datasource layers wrap HttpFetchError
94
+ # into DataSourceError via ``raise DataSourceError(...) from exc``.
95
+ cur: BaseException | None = exc
96
+ while cur is not None:
97
+ if isinstance(cur, _HttpFetchError):
98
+ return _EXIT_HTTP_FETCH
99
+ cur = cur.__cause__
100
+ except (ImportError, AttributeError):
101
+ pass
102
+
103
+ # --- datasource errors ---
104
+ try:
105
+ from recotem.datasource.base import (
106
+ DataSourceError as _DataSourceError, # noqa: PLC0415
107
+ )
108
+
109
+ if isinstance(exc, _DataSourceError):
110
+ return _EXIT_DATASOURCE
111
+ except ImportError:
112
+ pass
113
+
114
+ # --- config errors (must come before ArtifactError so that a signing-key
115
+ # misconfiguration on the serve path exits 8, not 5) ---
116
+ try:
117
+ from recotem.config import ConfigError as _ConfigError # noqa: PLC0415
118
+
119
+ if isinstance(exc, _ConfigError):
120
+ return _EXIT_CONFIG
121
+ except ImportError:
122
+ pass
123
+
124
+ # --- artifact errors ---
125
+ try:
126
+ from recotem.artifact.format import (
127
+ ArtifactError as _ArtifactError, # noqa: PLC0415
128
+ )
129
+
130
+ if isinstance(exc, _ArtifactError):
131
+ return _EXIT_ARTIFACT
132
+ except ImportError:
133
+ pass
134
+
135
+ # --- lock contested ---
136
+ try:
137
+ from recotem.training.lock import (
138
+ LockContestedError as _LockContestedError, # noqa: PLC0415
139
+ )
140
+
141
+ if isinstance(exc, _LockContestedError):
142
+ return _EXIT_LOCK_CONTESTED
143
+ except (ImportError, AttributeError):
144
+ pass
145
+
146
+ # --- general training errors ---
147
+ try:
148
+ from recotem.training.errors import (
149
+ TrainingError as _TrainingError2, # noqa: PLC0415
150
+ )
151
+
152
+ if isinstance(exc, _TrainingError2):
153
+ return _EXIT_TRAINING
154
+ except (ImportError, AttributeError):
155
+ pass
156
+
157
+ return _EXIT_UNKNOWN