continuo-python-runtime 0.4.0__py3-none-any.whl → 0.5.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.
- continuo_python_runtime/validation/runner.py +21 -7
- {continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/METADATA +63 -30
- {continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/RECORD +7 -7
- {continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/WHEEL +0 -0
- {continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/entry_points.txt +0 -0
- {continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -18,6 +18,11 @@ Dispatches on ``VALIDATION_OP`` env var (default ``build_from_sql``):
|
|
|
18
18
|
that dropped a column the script reads fails the release gate, then the output
|
|
19
19
|
table is materialized empty from the declared typed columns and the declared
|
|
20
20
|
physical layout.
|
|
21
|
+
- ``check_binds``: for a dbt test's compiled SQL (``CANDIDATE_SQL_URI``, fetched
|
|
22
|
+
the same way as ``build_from_sql``). EXPLAINs it against the candidate schema
|
|
23
|
+
via the engine adapter and creates nothing — a test whose compiled SQL names
|
|
24
|
+
a column a candidate change dropped or renamed fails the release at its
|
|
25
|
+
source, before any table is built.
|
|
21
26
|
|
|
22
27
|
The engine adapter is discovered from the single installed
|
|
23
28
|
``continuo_engine.adapters`` entry point — each runner image installs exactly one.
|
|
@@ -108,7 +113,7 @@ def load_candidate_spec() -> dict:
|
|
|
108
113
|
return spec
|
|
109
114
|
|
|
110
115
|
|
|
111
|
-
_NODE_OPS = ("build_from_sql", "clone_from_prod", "build_from_columns")
|
|
116
|
+
_NODE_OPS = ("build_from_sql", "clone_from_prod", "build_from_columns", "check_binds")
|
|
112
117
|
_SCHEMA_OPS = ("ensure_schema", "drop_schema")
|
|
113
118
|
|
|
114
119
|
|
|
@@ -116,10 +121,13 @@ def main() -> None:
|
|
|
116
121
|
"""Run one validation op end to end; exits non-zero on failure.
|
|
117
122
|
|
|
118
123
|
Node ops (``build_from_sql``/``clone_from_prod``/``build_from_columns``)
|
|
119
|
-
materialize one empty node table and require ``TABLE_NAME``.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
materialize one empty node table and require ``TABLE_NAME``. ``check_binds``
|
|
125
|
+
is also a node op and requires ``TABLE_NAME`` (for identity only, in the
|
|
126
|
+
result block's ``unique_id``) but creates nothing: it bind-checks a dbt
|
|
127
|
+
test's compiled SQL against the candidate schema with the engine's EXPLAIN.
|
|
128
|
+
Schema ops (``ensure_schema``/``drop_schema``) act on the whole candidate
|
|
129
|
+
schema — the executor schedules them as one-shot engine-image Jobs to own
|
|
130
|
+
the candidate-schema lifecycle without connecting to the warehouse itself —
|
|
123
131
|
and take no table.
|
|
124
132
|
"""
|
|
125
133
|
logging.basicConfig(
|
|
@@ -139,7 +147,7 @@ def main() -> None:
|
|
|
139
147
|
if op in _NODE_OPS:
|
|
140
148
|
table = _require("TABLE_NAME")
|
|
141
149
|
unique_id = _node_id() or f"model.{table}"
|
|
142
|
-
if op
|
|
150
|
+
if op in ("build_from_sql", "check_binds"):
|
|
143
151
|
try:
|
|
144
152
|
raw_sql = load_candidate_sql()
|
|
145
153
|
except Exception as exc:
|
|
@@ -150,7 +158,7 @@ def main() -> None:
|
|
|
150
158
|
if not raw_sql:
|
|
151
159
|
logger.error(
|
|
152
160
|
"CANDIDATE_SQL_URI is unset or the object is empty for a "
|
|
153
|
-
"
|
|
161
|
+
"%s node; cannot validate", op
|
|
154
162
|
)
|
|
155
163
|
print(result.result_block("error", "CANDIDATE_SQL_URI is unset or empty",
|
|
156
164
|
unique_id=unique_id), flush=True)
|
|
@@ -238,6 +246,12 @@ def main() -> None:
|
|
|
238
246
|
if op == "build_from_sql":
|
|
239
247
|
assert candidate_sql is not None, "candidate_sql must be set for build_from_sql"
|
|
240
248
|
adapter.build_empty_from_sql(schema, table, candidate_sql)
|
|
249
|
+
elif op == "check_binds":
|
|
250
|
+
# A dbt test: EXPLAIN its compiled SQL against the candidate
|
|
251
|
+
# schema. A dropped or renamed column fails here; no table is
|
|
252
|
+
# created and no row is read.
|
|
253
|
+
assert candidate_sql is not None, "candidate_sql must be set for check_binds"
|
|
254
|
+
adapter.check_binds(candidate_sql)
|
|
241
255
|
elif op == "build_from_columns":
|
|
242
256
|
assert spec is not None, "spec must be set for build_from_columns"
|
|
243
257
|
if csv_source:
|
{continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: continuo-python-runtime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Runtime harness, contract tooling, and CI lint for Continuo python nodes.
|
|
5
5
|
Author: Simone Carolini
|
|
6
6
|
Maintainer: Simone Carolini
|
|
@@ -11,11 +11,11 @@ Classifier: Development Status :: 4 - Beta
|
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
13
13
|
Requires-Python: >=3.14
|
|
14
|
-
Requires-Dist: boto3==1.43.
|
|
15
|
-
Requires-Dist: continuo-engine-contract==0.7.
|
|
16
|
-
Requires-Dist: pyarrow==25.0.
|
|
14
|
+
Requires-Dist: boto3==1.43.85
|
|
15
|
+
Requires-Dist: continuo-engine-contract==0.7.3
|
|
16
|
+
Requires-Dist: pyarrow==25.0.1
|
|
17
17
|
Requires-Dist: pyyaml==6.0.3
|
|
18
|
-
Requires-Dist: sqlglot==30.
|
|
18
|
+
Requires-Dist: sqlglot==30.17.0
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
|
|
21
21
|
# Continuo Python Runtime
|
|
@@ -32,7 +32,7 @@ and register the release with Continuo.
|
|
|
32
32
|
|
|
33
33
|
## What this repo is
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Five artifacts come out of this repository:
|
|
36
36
|
|
|
37
37
|
- **The `continuo-python-runtime` PyPI package** — the `continuo-runtime` CLI
|
|
38
38
|
(`validate` / `merge` / `hash` / `lint` / `run` / `validation-op`) and the
|
|
@@ -41,6 +41,13 @@ Four artifacts come out of this repository:
|
|
|
41
41
|
- **The `continuo-engine-contract` PyPI package** — the `WarehouseAdapter`
|
|
42
42
|
port, the contract schema, the shared SQL/type/config guards, and the
|
|
43
43
|
sentinel result-block format. Adapter authors outside this repo pin it.
|
|
44
|
+
- **The two engine-adapter PyPI packages** (`continuo-postgres-adapter`,
|
|
45
|
+
`continuo-trino-adapter`) — one `WarehouseAdapter` implementation per
|
|
46
|
+
warehouse engine, each published independently under the same tag. A
|
|
47
|
+
domain repo normally never installs these directly (the engine image
|
|
48
|
+
already has the matching one baked in); they exist as standalone PyPI
|
|
49
|
+
packages for the "build your own container" shape (see below) and for
|
|
50
|
+
third-party adapter authors to reference.
|
|
44
51
|
- **Per-engine base images**, one per warehouse engine
|
|
45
52
|
(`continuo-python-runtime-postgres`, `continuo-python-runtime-trino`), that
|
|
46
53
|
domain repos build `FROM`. Each image bakes in the runtime and a single
|
|
@@ -50,10 +57,11 @@ Four artifacts come out of this repository:
|
|
|
50
57
|
- **`template/`** — a copy-ready domain repo: `Dockerfile`, `contracts/`,
|
|
51
58
|
`scripts/`, and the `release.yml` CI/CD workflow.
|
|
52
59
|
|
|
53
|
-
One `vX.Y.Z` git tag releases all of it: `publish-pypi.yml` builds
|
|
54
|
-
distributions into a single `dist/` and publishes them together, and
|
|
55
|
-
`images.yml` builds and pushes both engine images
|
|
56
|
-
|
|
60
|
+
One `vX.Y.Z` git tag releases all of it: `publish-pypi.yml` builds all four
|
|
61
|
+
PyPI distributions into a single `dist/` and publishes them together, and
|
|
62
|
+
`images.yml` builds and pushes both engine images — each installing its
|
|
63
|
+
matching pinned adapter version from that same release — multi-arch under
|
|
64
|
+
the same tag.
|
|
57
65
|
|
|
58
66
|
### What this repo owns
|
|
59
67
|
|
|
@@ -70,22 +78,27 @@ validation-side port, adapter class, entry-point group, or image. One
|
|
|
70
78
|
| --- | --- | --- | --- |
|
|
71
79
|
| `continuo-python-runtime` | `continuo_python_runtime` | this repo (root) | Harness (CLI, `conform()`, `RunContext`, error taxonomy) **and** the validation runner (`continuo-runtime validation-op`). Published to PyPI. |
|
|
72
80
|
| `continuo-engine-contract` | `continuo_engine_contract` | this repo, `contract/` | The `WarehouseAdapter` port, contract schema, the SQL/type/config guards adapters must run, and the result-block format. Published to PyPI. |
|
|
73
|
-
| `continuo-
|
|
74
|
-
| `continuo-
|
|
81
|
+
| `continuo-postgres-adapter` | `continuo_postgres_adapter` | this repo, `adapters/postgres/` | `PostgresAdapter` — one class, both roles. Published to PyPI. |
|
|
82
|
+
| `continuo-trino-adapter` | `continuo_trino_adapter` | this repo, `adapters/trino/` | `TrinoAdapter` — one class, both roles, for Trino/Iceberg. Published to PyPI. |
|
|
75
83
|
|
|
76
84
|
All four are uv workspace members (`[tool.uv.workspace]` in the root
|
|
77
85
|
`pyproject.toml`), so `uv sync --all-packages --all-groups` at the repo root
|
|
78
86
|
installs everything for local development.
|
|
79
87
|
|
|
80
|
-
**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
**All four packages in the table above are published to PyPI**, under the
|
|
89
|
+
same `vX.Y.Z` tag. The two engine images then **install the matching pinned
|
|
90
|
+
adapter version from PyPI** — `Dockerfile.postgres` installs
|
|
91
|
+
`continuo-postgres-adapter==X.Y.Z`, `Dockerfile.trino` installs
|
|
92
|
+
`continuo-trino-adapter==X.Y.Z` — rather than building it from this repo's
|
|
93
|
+
source tree, so each image still ships exactly one adapter and the runtime
|
|
94
|
+
still discovers it through the `continuo_engine.adapters` entry-point group
|
|
95
|
+
at run time. The image **name** (`continuo-python-runtime-<engine>`) and the
|
|
96
|
+
adapter's pip **distribution** name (`continuo-<engine>-adapter`) are two
|
|
97
|
+
different artifacts of the same adapter — same engine, same version, same
|
|
98
|
+
runtime behavior, different packaging; see "Build your own container" below
|
|
99
|
+
for a build shape that installs the pip package directly instead of `FROM`
|
|
100
|
+
the image. All four packages are still built, type-checked, and tested by CI
|
|
101
|
+
on every change.
|
|
89
102
|
|
|
90
103
|
### The result block is a frozen wire contract
|
|
91
104
|
|
|
@@ -262,26 +275,46 @@ A domain repo picks its warehouse engine by which base image it builds
|
|
|
262
275
|
`FROM`:
|
|
263
276
|
|
|
264
277
|
```dockerfile
|
|
265
|
-
FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.
|
|
278
|
+
FROM ghcr.io/carolsimone/continuo-python-runtime-postgres:v0.5.0
|
|
266
279
|
# or
|
|
267
|
-
FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.
|
|
280
|
+
FROM ghcr.io/carolsimone/continuo-python-runtime-trino:v0.5.0
|
|
268
281
|
```
|
|
269
282
|
|
|
270
283
|
The engine is part of the image **name**; the tag is the bare version, so
|
|
271
284
|
Continuo's Helm chart can pin an image as `<name>:vX.Y.Z@sha256:<digest>`.
|
|
272
285
|
|
|
273
|
-
Each image bakes in exactly one `WarehouseAdapter` for that engine —
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
`
|
|
278
|
-
|
|
279
|
-
|
|
286
|
+
Each image bakes in exactly one `WarehouseAdapter` for that engine — the
|
|
287
|
+
pinned PyPI version of the `continuo-postgres-adapter` or
|
|
288
|
+
`continuo-trino-adapter` package built from this repo's `adapters/postgres/`
|
|
289
|
+
or `adapters/trino/` source (see the table above) — registered under the
|
|
290
|
+
`continuo_engine.adapters` entry-point group (entry names `postgres` /
|
|
291
|
+
`trino`). The runtime discovers it via `discover_adapter()` at run time, so a
|
|
292
|
+
single image serves every node in the service and the release-time
|
|
293
|
+
validation Job for it. The executor injects the warehouse connection as
|
|
294
|
+
environment variables (engine-native, e.g.
|
|
280
295
|
`POSTGRES_HOST`/`POSTGRES_DB`/`POSTGRES_USER`) plus the node-selection
|
|
281
296
|
environment (`NODE_ID`, `TABLE_NAME`, `TARGET_SCHEMA`, and optionally
|
|
282
297
|
`CONTRACT_DIR`/`APP_ROOT`) that `continuo-runtime run` reads to dispatch the
|
|
283
298
|
right node's script.
|
|
284
299
|
|
|
300
|
+
### Build your own container
|
|
301
|
+
|
|
302
|
+
A domain repo does not have to build `FROM` the published engine image.
|
|
303
|
+
`template/` ships two Dockerfiles for the two build shapes (see
|
|
304
|
+
`template/README.md` § "Choosing a base" for the full comparison):
|
|
305
|
+
|
|
306
|
+
- **Shape 1 — `template/Dockerfile`** — `FROM` the published engine image
|
|
307
|
+
(`continuo-python-runtime-<engine>`), as shown above. Simplest; the image
|
|
308
|
+
already has the runtime and adapter installed and pinned.
|
|
309
|
+
- **Shape 2 — `template/Dockerfile.pip`** — your own base image, installing
|
|
310
|
+
`continuo-python-runtime` and one `continuo-<engine>-adapter` from PyPI
|
|
311
|
+
via a hash-locked `requirements.lock` (`template/requirements.lock`). Use
|
|
312
|
+
this when you must control the base image yourself.
|
|
313
|
+
|
|
314
|
+
Both shapes end up running the same runtime and the same adapter version;
|
|
315
|
+
which one you pick only changes who controls the base OS layer underneath
|
|
316
|
+
them.
|
|
317
|
+
|
|
285
318
|
## Further reading
|
|
286
319
|
|
|
287
320
|
- `docs/superpowers/specs/2026-07-31-python-runtime-design.md` — this
|
|
@@ -19,11 +19,11 @@ continuo_python_runtime/csv_readers/__init__.py,sha256=9TKvjC37C77uRaJQZuO0--zVj
|
|
|
19
19
|
continuo_python_runtime/csv_readers/https.py,sha256=tMJ9k5F0b9SAydpon0ZlOznfjvjhF2BBLltyVmvRneE,4998
|
|
20
20
|
continuo_python_runtime/csv_readers/s3.py,sha256=zmvHs3TxlN2alPKrdaHzPyREmkve2nBiAsHRXXe1mE0,2246
|
|
21
21
|
continuo_python_runtime/validation/__init__.py,sha256=hz5oGXsoaUoygR0_pBby9PS6cEfrre518EE0HGB58eE,79
|
|
22
|
-
continuo_python_runtime/validation/runner.py,sha256=
|
|
22
|
+
continuo_python_runtime/validation/runner.py,sha256=7pbksf7MXfTNcdklCOkOXg9qfRiJtyI_dbgbL3a9LWk,14072
|
|
23
23
|
continuo_python_runtime/validation/s3.py,sha256=q8nRrT9R7L1-M4dSlDnkL7CUJMPmq86MHY_UKYFlEf8,1785
|
|
24
|
-
continuo_python_runtime-0.
|
|
25
|
-
continuo_python_runtime-0.
|
|
26
|
-
continuo_python_runtime-0.
|
|
27
|
-
continuo_python_runtime-0.
|
|
28
|
-
continuo_python_runtime-0.
|
|
29
|
-
continuo_python_runtime-0.
|
|
24
|
+
continuo_python_runtime-0.5.0.dist-info/METADATA,sha256=vQisnCASgreJNhw4ryPWuyQvo4dmw6mWEMJH3zZxHx4,18811
|
|
25
|
+
continuo_python_runtime-0.5.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
26
|
+
continuo_python_runtime-0.5.0.dist-info/entry_points.txt,sha256=ZvIwSm9f9B9ZmZMf6CyF5Zt4QONn8rqaIk-u-B1CF2Y,70
|
|
27
|
+
continuo_python_runtime-0.5.0.dist-info/licenses/LICENSE,sha256=hfXfFCk-8gnps4YvHA6bjCNaSYqOrq2gAdqkXYycAQc,11345
|
|
28
|
+
continuo_python_runtime-0.5.0.dist-info/licenses/NOTICE,sha256=ykgYyQkAMx3uas90FZHzt0aTca9IgJQCLpZ4d74Pl3U,465
|
|
29
|
+
continuo_python_runtime-0.5.0.dist-info/RECORD,,
|
|
File without changes
|
{continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{continuo_python_runtime-0.4.0.dist-info → continuo_python_runtime-0.5.0.dist-info}/licenses/NOTICE
RENAMED
|
File without changes
|