equiframe 0.12.4__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.
- equiframe/__init__.py +832 -0
- equiframe/__main__.py +3 -0
- equiframe/_base.py +84 -0
- equiframe/_cli_support.py +125 -0
- equiframe/_comparison_io.py +161 -0
- equiframe/_dataset_provenance.py +146 -0
- equiframe/_decimal.py +22 -0
- equiframe/_diff_capture.py +433 -0
- equiframe/_doctor.py +123 -0
- equiframe/_document_io.py +61 -0
- equiframe/_documents.py +79 -0
- equiframe/_duckdb_transport.py +149 -0
- equiframe/_error_reporting.py +52 -0
- equiframe/_first_use.py +102 -0
- equiframe/_path_compat.py +26 -0
- equiframe/_physical.py +133 -0
- equiframe/_r_review.R +308 -0
- equiframe/_regular_io.py +167 -0
- equiframe/_report_format_v1.py +298 -0
- equiframe/_report_io.py +591 -0
- equiframe/_stability_inventory.py +1421 -0
- equiframe/_timezones.py +257 -0
- equiframe/_vendor/cyclonedx/LICENSE +201 -0
- equiframe/_vendor/cyclonedx/PROVENANCE.md +18 -0
- equiframe/_vendor/cyclonedx/bom-1.6.schema.json +5673 -0
- equiframe/_vendor/cyclonedx/jsf-0.82.schema.json +240 -0
- equiframe/_vendor/cyclonedx/spdx.schema.json +712 -0
- equiframe/adapters.py +454 -0
- equiframe/assertion_types.py +122 -0
- equiframe/assertions.py +271 -0
- equiframe/benchmark.py +101 -0
- equiframe/bundle.py +240 -0
- equiframe/cli.py +693 -0
- equiframe/comparison.py +937 -0
- equiframe/comparison_report.py +660 -0
- equiframe/comparison_types.py +257 -0
- equiframe/compatibility.py +546 -0
- equiframe/contracts.py +879 -0
- equiframe/datasets.py +1301 -0
- equiframe/decision.py +34 -0
- equiframe/destinations.py +295 -0
- equiframe/detector_types.py +431 -0
- equiframe/detectors.py +389 -0
- equiframe/diff.py +192 -0
- equiframe/diff_contracts.py +523 -0
- equiframe/docs/adoption.md +329 -0
- equiframe/docs/api.md +534 -0
- equiframe/docs/arquitecto.md +143 -0
- equiframe/docs/choosing-api.md +84 -0
- equiframe/docs/cientifico.md +180 -0
- equiframe/docs/compatibility.md +254 -0
- equiframe/docs/contributing.md +171 -0
- equiframe/docs/dependencies.md +237 -0
- equiframe/docs/eda.md +166 -0
- equiframe/docs/extending.md +158 -0
- equiframe/docs/fast-ci.md +52 -0
- equiframe/docs/first-use.md +78 -0
- equiframe/docs/index.md +142 -0
- equiframe/docs/ingestion.md +145 -0
- equiframe/docs/inicio.md +129 -0
- equiframe/docs/installed-private.md +97 -0
- equiframe/docs/journeys.md +362 -0
- equiframe/docs/lectura-conversion.md +211 -0
- equiframe/docs/licensing.md +17 -0
- equiframe/docs/maintenance.md +233 -0
- equiframe/docs/migration-ir.md +333 -0
- equiframe/docs/option-audit.md +45 -0
- equiframe/docs/outcome-gates.md +134 -0
- equiframe/docs/performance.md +263 -0
- equiframe/docs/piloto-simulado.md +56 -0
- equiframe/docs/quickstart.md +156 -0
- equiframe/docs/r_review.md +212 -0
- equiframe/docs/recipes.md +248 -0
- equiframe/docs/releasing.md +183 -0
- equiframe/docs/security.md +218 -0
- equiframe/docs/semantics.md +274 -0
- equiframe/docs/sqlite-snapshots.md +262 -0
- equiframe/docs/universal-sources.md +49 -0
- equiframe/docs/universalidad-cinco-pasos.md +107 -0
- equiframe/docs/upgrading.md +538 -0
- equiframe/duckdb_capture.py +410 -0
- equiframe/eda/__init__.py +36 -0
- equiframe/eda/__main__.py +3 -0
- equiframe/eda/_backend.py +612 -0
- equiframe/eda/_inventory.py +49 -0
- equiframe/eda/_paths.py +49 -0
- equiframe/eda/_report.py +291 -0
- equiframe/eda/api.py +535 -0
- equiframe/eda/cli.py +102 -0
- equiframe/eda/models.py +239 -0
- equiframe/errors.py +83 -0
- equiframe/evidence.py +375 -0
- equiframe/examples/integrations/eda_generic.py +60 -0
- equiframe/examples/integrations/outcome_gates.py +94 -0
- equiframe/examples/integrations/sqlite_snapshot.py +246 -0
- equiframe/examples/integrations/sqlite_snapshot_demo.py +226 -0
- equiframe/examples/integrations/universal_first_use.py +296 -0
- equiframe/examples/journeys/README.md +31 -0
- equiframe/examples/journeys/__init__.py +1 -0
- equiframe/examples/journeys/a_pandas_refactor.py +88 -0
- equiframe/examples/journeys/b_native_migration.py +181 -0
- equiframe/examples/journeys/c_retail_etl.py +460 -0
- equiframe/examples/journeys/common.py +136 -0
- equiframe/examples/journeys/d_financial_reconciliation.py +96 -0
- equiframe/examples/journeys/e_telemetry.py +135 -0
- equiframe/examples/journeys/f_data_science_preparation.py +84 -0
- equiframe/examples/journeys/g_external_snapshots.py +197 -0
- equiframe/examples/journeys/h_portable_ci.py +129 -0
- equiframe/examples/journeys/requirements.lock +249 -0
- equiframe/examples/journeys/verify_journeys.py +117 -0
- equiframe/examples/load_convert_compare.py +85 -0
- equiframe/execution.py +355 -0
- equiframe/execution_types.py +177 -0
- equiframe/ingestion.py +744 -0
- equiframe/local.py +126 -0
- equiframe/migration/__init__.py +94 -0
- equiframe/migration/_physical.py +8 -0
- equiframe/migration/backends/_timestamp_transport.py +109 -0
- equiframe/migration/backends/duckdb.py +651 -0
- equiframe/migration/backends/duckdb_decimal.py +125 -0
- equiframe/migration/backends/duckdb_time.py +87 -0
- equiframe/migration/backends/pandas.py +544 -0
- equiframe/migration/backends/polars.py +915 -0
- equiframe/migration/backends/polars_decimal.py +231 -0
- equiframe/migration/io.py +198 -0
- equiframe/migration/ports.py +96 -0
- equiframe/migration/runner.py +545 -0
- equiframe/migration/runner_types.py +405 -0
- equiframe/migration/types.py +354 -0
- equiframe/migration/validation.py +619 -0
- equiframe/normalization.py +358 -0
- equiframe/pipeline.py +274 -0
- equiframe/pipeline_types.py +247 -0
- equiframe/ports.py +39 -0
- equiframe/preprocessing.py +110 -0
- equiframe/profile_types.py +343 -0
- equiframe/profiles.py +447 -0
- equiframe/py.typed +0 -0
- equiframe/r_review.py +706 -0
- equiframe/reduction.py +518 -0
- equiframe/registry.py +121 -0
- equiframe/report_rendering.py +366 -0
- equiframe/report_types.py +294 -0
- equiframe/reporting.py +26 -0
- equiframe/rule_types.py +178 -0
- equiframe/rules.py +819 -0
- equiframe/schemas/r-execution-binding-1.0.schema.json +46 -0
- equiframe/schemas/r-review-binding-1.0.schema.json +28 -0
- equiframe/schemas/r-review-options-1.0.schema.json +38 -0
- equiframe/schemas/r-review-result-1.0.schema.json +207 -0
- equiframe/schemas/reviewed-decision-1.0.schema.json +3394 -0
- equiframe/serialization.py +152 -0
- equiframe/snapshot.py +296 -0
- equiframe/sources.py +283 -0
- equiframe/tables.py +459 -0
- equiframe/types.py +71 -0
- equiframe/values.py +252 -0
- equiframe/verification_request.py +35 -0
- equiframe/verify.py +799 -0
- equiframe/verify_types.py +255 -0
- equiframe/workflow.py +510 -0
- equiframe/workflow_request.py +38 -0
- equiframe-0.12.4.dist-info/METADATA +268 -0
- equiframe-0.12.4.dist-info/RECORD +170 -0
- equiframe-0.12.4.dist-info/WHEEL +4 -0
- equiframe-0.12.4.dist-info/entry_points.txt +3 -0
- equiframe-0.12.4.dist-info/licenses/LICENSE +21 -0
- equiframe-0.12.4.dist-info/licenses/THIRD_PARTY_NOTICES.md +42 -0
- equiframe-0.12.4.dist-info/licenses/tools/cyclonedx/LICENSE +201 -0
- equiframe-0.12.4.dist-info/licenses/tools/cyclonedx/PROVENANCE.md +18 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# A consistent SQLite snapshot through the existing ports
|
|
2
|
+
|
|
3
|
+
[Handbook](index.md) · [Extension ports](extending.md) · [Recipes](recipes.md)
|
|
4
|
+
|
|
5
|
+
**For architects and data engineers:** capture related tables from an existing
|
|
6
|
+
SQLite database once, supply them through `SnapshotClient`, and let the existing
|
|
7
|
+
`SnapshotDataSource` check and record the evidence. No benchmark, new framework,
|
|
8
|
+
remote service, ORM or core dependency is introduced.
|
|
9
|
+
|
|
10
|
+
The wheel and source distribution carry two complete external application files:
|
|
11
|
+
|
|
12
|
+
- `examples/integrations/sqlite_snapshot.py`: the small producer and detached client
|
|
13
|
+
- `examples/integrations/sqlite_snapshot_demo.py`: a synthetic runnable integration
|
|
14
|
+
|
|
15
|
+
They intentionally live outside `equiframe`. The producer is a concrete SQLite
|
|
16
|
+
DB-API example, not a promise that every DB-API database has SQLite's transaction
|
|
17
|
+
semantics. The core snapshot exports remain experimental; existing manifest 1.0,
|
|
18
|
+
fingerprints, imports and benchmark aliases are unchanged.
|
|
19
|
+
|
|
20
|
+
## Install an identified artifact
|
|
21
|
+
|
|
22
|
+
The producer needs Python's standard-library `sqlite3`, the identified
|
|
23
|
+
EquiFrame wheel and its `arrow` dependencies. The demo also executes a native
|
|
24
|
+
DuckDB join, so it needs the `duckdb` extra. It does not require pandas, Polars,
|
|
25
|
+
scikit-learn, network access, credentials or any real data to run.
|
|
26
|
+
|
|
27
|
+
Use the [verified first-use installation](recipes.md#1-obtain-verify-and-install-the-kit)
|
|
28
|
+
in a fresh supported Python environment outside the checkout. Commands below
|
|
29
|
+
show 3.12; use the minor recorded by your interpreter-specific kit. Its hash-pinned
|
|
30
|
+
`requirements.lock` covers this demo as well as the other recipes; pandas/pytest
|
|
31
|
+
in that shared kit are not new dependencies of this producer. Copy **both**
|
|
32
|
+
integration files from the installed wheel's resources (shown below) or the
|
|
33
|
+
corresponding verified source distribution into a new working directory.
|
|
34
|
+
Use a registry artifact only after verifying its project, version and identity;
|
|
35
|
+
the string `0.12.4` alone does not identify bytes. An offline kit is optional,
|
|
36
|
+
not a separate public-release channel promised by this documentation.
|
|
37
|
+
|
|
38
|
+
For example, after verifying the private kit, use these POSIX commands from
|
|
39
|
+
outside the checkout. `KIT` is the extracted verified first-use kit:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python3.12 -m venv sqlite-example-env
|
|
43
|
+
sqlite-example-env/bin/python -m pip install --require-hashes --only-binary=:all: \
|
|
44
|
+
-r "$KIT/requirements.lock"
|
|
45
|
+
sqlite-example-env/bin/python -m pip install --no-index \
|
|
46
|
+
"$KIT/wheels/equiframe-0.12.4-py3-none-any.whl"
|
|
47
|
+
sqlite-example-env/bin/python -m pip check
|
|
48
|
+
mkdir sqlite-example-work
|
|
49
|
+
sqlite-example-env/bin/python - <<'PY'
|
|
50
|
+
from importlib.resources import files
|
|
51
|
+
from pathlib import Path
|
|
52
|
+
|
|
53
|
+
source = files("equiframe").joinpath("examples/integrations")
|
|
54
|
+
for name in ("sqlite_snapshot.py", "sqlite_snapshot_demo.py"):
|
|
55
|
+
Path("sqlite-example-work", name).write_bytes(source.joinpath(name).read_bytes())
|
|
56
|
+
PY
|
|
57
|
+
cd sqlite-example-work
|
|
58
|
+
../sqlite-example-env/bin/python sqlite_snapshot_demo.py --case good --output good
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The first pip command deliberately downloads pinned dependencies from the
|
|
62
|
+
configured index. For offline use, obtain the same reviewed dependency wheels
|
|
63
|
+
and add `--no-index --find-links /absolute/path/wheelhouse`. See the kit guide for
|
|
64
|
+
Windows environment commands; this example does not independently qualify a
|
|
65
|
+
Windows, macOS or ARM64 platform. Run the script normally: unlike single-file
|
|
66
|
+
recipes, these two files require their containing directory on Python's import
|
|
67
|
+
path, which `python -I script.py` deliberately omits.
|
|
68
|
+
|
|
69
|
+
Focused development checks used Python **3.12.14**, SQLite **3.53.1**, PyArrow
|
|
70
|
+
**25.0.1**, DuckDB **1.5.6**, Pydantic **2.13.5**, Typer **0.27.2** and tzdata
|
|
71
|
+
**2026.4** on Linux. Exact transitive versions/hashes are the unchanged candidate
|
|
72
|
+
lock and shared first-use lock. These are observed versions, not minimum support
|
|
73
|
+
claims. Record the final commit and wheel/sdist hashes alongside a new run.
|
|
74
|
+
|
|
75
|
+
## Three runnable outcomes
|
|
76
|
+
|
|
77
|
+
Every case requires a **new** output directory; reuse fails before changing it.
|
|
78
|
+
All data is synthetic: two accounts and three selected ledger entries. A fourth
|
|
79
|
+
entry belongs to another cohort and is excluded by a bound SQL parameter.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python sqlite_snapshot_demo.py --case good --output good-new
|
|
83
|
+
# execution=completed; comparison=equivalent; exit=0
|
|
84
|
+
|
|
85
|
+
python sqlite_snapshot_demo.py --case different --output different-new
|
|
86
|
+
# execution=completed; comparison=different; exit=1
|
|
87
|
+
|
|
88
|
+
python sqlite_snapshot_demo.py --case corrupt --output corrupt-new
|
|
89
|
+
# execution=invalid_input; comparison=not_evaluable; exit=3
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The literal expected output is independently authored in `expected_output()`:
|
|
93
|
+
`A / Café / 1.10 / USD`, `B / Café / NULL / USD`, and
|
|
94
|
+
`C / NULL / -0.01 / EUR`. It is never generated by the SQLite query or DuckDB
|
|
95
|
+
join. The discrepancy changes A to `1.11`; currencies stay separate columns and
|
|
96
|
+
no aggregate total hides the record-level difference. The controlled failure
|
|
97
|
+
changes one downloaded byte while preserving the original manifest. The existing
|
|
98
|
+
consumer rejects the SHA-256 mismatch before exposing the table to DuckDB.
|
|
99
|
+
|
|
100
|
+
Each output directory contains:
|
|
101
|
+
|
|
102
|
+
- `synthetic.sqlite`: only the demo's small synthetic source database
|
|
103
|
+
- `execution/<run-id>/`: native execution evidence, pinned manifest, input
|
|
104
|
+
artifacts and, on success, the joined output
|
|
105
|
+
- `comparison/`: complete captured comparison evidence for successful execution
|
|
106
|
+
- `report/`: default-redacted JSON/text/HTML report, written before the assertion
|
|
107
|
+
- `receipt.json`: case, identity/fingerprint, explicit states and exit code
|
|
108
|
+
|
|
109
|
+
Failed execution has no successful output/comparison/report. Its receipt states
|
|
110
|
+
`not_evaluable` as the demo's gate status; it does not fabricate an evaluated
|
|
111
|
+
`ComparisonResult`. Inspect the execution's failure diagnostics. A producer
|
|
112
|
+
exception before execution raises normally and yields no published snapshot
|
|
113
|
+
client; incomplete output folders must not be accepted as successful runs.
|
|
114
|
+
|
|
115
|
+
Acceptance is the process exit code **0** and a successful
|
|
116
|
+
`assert_equivalent(comparison)`, not existence of a report, a completed join or
|
|
117
|
+
truthiness of a result. A matching synthetic expected output establishes this
|
|
118
|
+
fixture's behavior, not independent human review, business correctness in
|
|
119
|
+
production, or scientific evidence.
|
|
120
|
+
|
|
121
|
+
## Use your own trusted queries
|
|
122
|
+
|
|
123
|
+
The producer keeps the native boundary explicit:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from pathlib import Path
|
|
127
|
+
import pyarrow as pa
|
|
128
|
+
import equiframe as ef
|
|
129
|
+
from sqlite_snapshot import SQLiteQuery, capture_sqlite_snapshot
|
|
130
|
+
|
|
131
|
+
client = capture_sqlite_snapshot(
|
|
132
|
+
Path("existing.sqlite"),
|
|
133
|
+
snapshot_id="ledger.2026-10-06.001",
|
|
134
|
+
queries=(
|
|
135
|
+
SQLiteQuery(
|
|
136
|
+
name="entries",
|
|
137
|
+
sql="SELECT id, amount FROM entries WHERE cohort = ? ORDER BY id",
|
|
138
|
+
parameters=("reviewed-cohort",),
|
|
139
|
+
schema=pa.schema(
|
|
140
|
+
[
|
|
141
|
+
pa.field("id", pa.string(), nullable=False),
|
|
142
|
+
pa.field("amount", pa.decimal128(10, 2)),
|
|
143
|
+
]
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
domain_schema_revision="ledger-schema.1",
|
|
148
|
+
extraction_revision="reviewed-query.1",
|
|
149
|
+
provenance=("Authorized ledger capture; exact amounts stored as TEXT",),
|
|
150
|
+
)
|
|
151
|
+
source = ef.SnapshotDataSource(
|
|
152
|
+
"ledger.2026-10-06.001",
|
|
153
|
+
client=client,
|
|
154
|
+
expected_domain_schema_revision="ledger-schema.1",
|
|
155
|
+
)
|
|
156
|
+
# Pass source to the existing execute, migrate or run_workflow API.
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
All related tables must be included in **one** capture call. Two calls open two
|
|
160
|
+
transactions and do not establish cross-call consistency. The producer owns a
|
|
161
|
+
new read-only connection to the existing main database, explicitly begins one
|
|
162
|
+
native SQLite transaction and reads every query inside it. The first native
|
|
163
|
+
read anchors the snapshot. In WAL mode, another connection can commit while
|
|
164
|
+
this reader continues seeing the original table versions; the functional test
|
|
165
|
+
does exactly this between the two SELECTs. In rollback-journal mode writers can
|
|
166
|
+
wait on readers. The producer does not change journal mode or configure writers.
|
|
167
|
+
|
|
168
|
+
After every query succeeds, the producer releases the read transaction with
|
|
169
|
+
rollback and closes the connection before returning detached Arrow IPC bytes.
|
|
170
|
+
The same cleanup runs on SQL, schema, conversion and budget failures. Cursors
|
|
171
|
+
are closed explicitly. It does not borrow, commit or close a caller-owned
|
|
172
|
+
connection. Later database changes or removal do not change the captured client.
|
|
173
|
+
There is no implicit recapture, retry or `latest` resolution.
|
|
174
|
+
|
|
175
|
+
Choose a distinct `snapshot_id` for each publication and meaningful schema/query
|
|
176
|
+
revisions. This process-local example has no global ID registry, persistent
|
|
177
|
+
catalog, lease or restart recovery. Its opaque artifact URIs are identifiers
|
|
178
|
+
under `example.invalid`, never fetched as URLs. Their per-capture namespace keeps
|
|
179
|
+
references distinct, so separate captures can have different fingerprints even
|
|
180
|
+
with identical rows. Fixed identity means the chosen client/manifest stays
|
|
181
|
+
unchanged for a consuming run, not that a human label authenticates a producer.
|
|
182
|
+
|
|
183
|
+
## Deliberately supported schema
|
|
184
|
+
|
|
185
|
+
The SQL cursor names/order must exactly match the declared Arrow schema.
|
|
186
|
+
SQLite's dynamic value types are checked before Arrow conversion:
|
|
187
|
+
|
|
188
|
+
| Declared Arrow type | Required non-NULL SQLite value | Behavior |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| `int64` | INTEGER / Python `int` | Exact signed 64-bit value; REAL/text casts are refused |
|
|
191
|
+
| `string` | TEXT / Python `str` | Unicode preserved; no normalization or coercion |
|
|
192
|
+
| `decimal128(p, s)` with `0 <= s <= p` | Plain decimal TEXT | Explicit Decimal parse, exact scale/precision check, no float intermediary |
|
|
193
|
+
|
|
194
|
+
NULL is preserved only for nullable fields. Duplicates, empty tables, column
|
|
195
|
+
order and returned row order are preserved in the capture; the demo comparison
|
|
196
|
+
explicitly uses bag semantics. Declare `ORDER BY` where query order matters.
|
|
197
|
+
Decimal negative zero is refused because Arrow decimal cannot retain its sign.
|
|
198
|
+
Exponents, NaN/Infinity strings, REAL money, overflowing precision and values
|
|
199
|
+
requiring rounding are refused. Do not cast existing REAL money to TEXT and
|
|
200
|
+
assume that this recovers precision already lost in storage.
|
|
201
|
+
|
|
202
|
+
Dates, timestamps/time zones, BLOBs, floats/NaNs, other integer widths, structured
|
|
203
|
+
types, custom SQLite converters and Arrow field/schema metadata are outside this
|
|
204
|
+
small producer's supported subset. They fail explicitly instead of being
|
|
205
|
+
silently coerced. This does not narrow the core's separate
|
|
206
|
+
[semantic support](semantics.md). Adding a new producer type needs its own exact
|
|
207
|
+
mapping, independent expectations and rejection tests.
|
|
208
|
+
|
|
209
|
+
`SnapshotDataSource` then independently checks downloaded size/SHA-256, row
|
|
210
|
+
count, names/order, physical/logical type and declared non-nullability. It saves
|
|
211
|
+
the complete manifest and fingerprint, including when selecting fewer tables.
|
|
212
|
+
The producer's declared provenance/revisions are copied into that manifest;
|
|
213
|
+
they are assertions by the trusted application, not verified origin claims.
|
|
214
|
+
|
|
215
|
+
## Operational limits and privacy
|
|
216
|
+
|
|
217
|
+
Keep this implementation for small, trusted datasets. Defaults are 10,000 rows
|
|
218
|
+
per table, 256 rows per fetch, 16 MiB total serialized payload and a five-second
|
|
219
|
+
SQLite lock wait. The byte budget is checked **after** Arrow serialization;
|
|
220
|
+
queries, fetches, string/Decimal conversion and Arrow allocation can consume
|
|
221
|
+
resources before checks. Complete detached payloads remain in memory. Lock
|
|
222
|
+
timeout is not query timeout. No hard memory, execution-time, disk, process or
|
|
223
|
+
network quota is provided; no cancellation/streaming service is implemented.
|
|
224
|
+
|
|
225
|
+
The SQLite database, application SQL, injected client and dependencies must be
|
|
226
|
+
trusted. `mode=ro`, SELECT/WITH admission, bound values and hashes do not make
|
|
227
|
+
this a sandbox, authenticate an author or establish a no-egress guarantee. The
|
|
228
|
+
transaction's consistency claim covers the ordinary persistent main database
|
|
229
|
+
tables in these queries; it does not synchronize remote systems, attached
|
|
230
|
+
databases, volatile functions, external virtual tables or filesystem effects.
|
|
231
|
+
|
|
232
|
+
SQL text, parameters and the source database pathname are not included in the
|
|
233
|
+
snapshot manifest by this producer. Query/database exceptions may still expose
|
|
234
|
+
application details to a caller; handle them according to your logging policy.
|
|
235
|
+
Provenance is caller-authored and raw SQLite/Arrow/comparison evidence can contain
|
|
236
|
+
private values. Use authorized private directories and keep synthetic examples
|
|
237
|
+
separate from real data. The report's redaction does not redact or delete raw
|
|
238
|
+
artifacts. This example neither implements José's service nor defines Jhair's
|
|
239
|
+
DW, publication rules or acceptance criteria.
|
|
240
|
+
|
|
241
|
+
## Run the focused checks
|
|
242
|
+
|
|
243
|
+
From the repository root in the existing locked development environment:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
uv run --locked --all-extras ruff check examples/integrations tests/test_sqlite_snapshot_example.py
|
|
247
|
+
uv run --locked --all-extras ruff format --check examples/integrations tests/test_sqlite_snapshot_example.py
|
|
248
|
+
uv run --locked --all-extras mypy --python-version 3.12 examples/integrations/sqlite_snapshot.py examples/integrations/sqlite_snapshot_demo.py
|
|
249
|
+
uv run --locked --all-extras pytest -q tests/test_sqlite_snapshot_example.py
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The tests cover native concurrent WAL consistency, lifecycle cleanup on success
|
|
253
|
+
and errors, fixed manifest selection/provenance, schema-preserving empty/NULL
|
|
254
|
+
tables, controlled size/hash/schema/row/nullability mismatches, exact conversion
|
|
255
|
+
admission and the copied standalone demo's three exit codes. These are ordinary
|
|
256
|
+
functional checks, not a security assessment, production load test, final
|
|
257
|
+
artifact verification or new platform matrix. Final candidate distribution
|
|
258
|
+
verification remains a separate gate.
|
|
259
|
+
|
|
260
|
+
Existing orchestrators can call the Python API inside a task or run the demo's
|
|
261
|
+
CLI-shaped gate as a subprocess, retain its private evidence, and propagate
|
|
262
|
+
nonzero exits. No scheduler or new orchestration framework is required.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Inputs from files, SQL and API responses
|
|
2
|
+
|
|
3
|
+
`LocalDataSource` accepts existing `FileInput` (CSV/JSON/JSONL), `ArtifactInput`
|
|
4
|
+
(hashed Parquet/Arrow), `ArrowInput` and the new experimental `SQLiteInput` and
|
|
5
|
+
`StreamInput`. Every input exposes `load(context)` and produces a `LoadedTable`.
|
|
6
|
+
Use the same declared columns, null, Decimal and timestamp policies regardless
|
|
7
|
+
of where the data came from. Ingestion retains original bytes and conversion
|
|
8
|
+
receipts; a rejected or quarantined input fails execution.
|
|
9
|
+
|
|
10
|
+
`SQLiteInput(path, query, spec, parameters=(), batch_rows=4096)` reads one trusted
|
|
11
|
+
parameterized SELECT/WITH in an owned read-only transaction. Its `IngestionSpec`
|
|
12
|
+
uses JSONL with direct record mappings. Decimal fields require SQLite TEXT;
|
|
13
|
+
REAL cannot recover exact monetary values. Textual identifiers, duplicate rows
|
|
14
|
+
and NULLs remain visible. BLOB and ambiguous column names are rejected. It does
|
|
15
|
+
not manage a remote warehouse or claim another database provider's isolation.
|
|
16
|
+
|
|
17
|
+
`StreamInput(open_stream, spec, expected_sha256=None, batch_rows=4096)` consumes
|
|
18
|
+
an explicitly injected binary response context manager. The caller implements
|
|
19
|
+
API authentication, transport and timeouts. EquiFrame never opens an implicit
|
|
20
|
+
URL. It closes the response on success or failure, checks byte budgets and an
|
|
21
|
+
optional separately supplied digest, then uses the existing typed ingestion.
|
|
22
|
+
Blocking transport calls remain the caller's responsibility. JSON documents
|
|
23
|
+
still materialize; CSV/JSONL parsing is incremental. Final tables and public
|
|
24
|
+
conversion receipts materialize within cooperative budgets, not a hard RSS cap.
|
|
25
|
+
|
|
26
|
+
`RecordBatchInput(open_batches, schema)` accepts an explicit Arrow batch reader
|
|
27
|
+
context manager. Every batch must match the exact schema, including metadata;
|
|
28
|
+
nonnullable fields cannot contain NULL. Row and Arrow-byte budgets accumulate
|
|
29
|
+
across all batches before constructing the final chunked table. The reader is
|
|
30
|
+
closed after success, schema failure, cancellation or budget exhaustion. The
|
|
31
|
+
final table still materializes; this is not a disk-spilling execution engine.
|
|
32
|
+
|
|
33
|
+
`CompositeDataSource({"logical_name": (provider, "provider_table")})` binds
|
|
34
|
+
independent `DataSource` implementations to the names of one contract. Bindings
|
|
35
|
+
are copied, tables are checked and failures propagate. No plugin is discovered
|
|
36
|
+
or imported implicitly. Providers own their immutable snapshots, authentication
|
|
37
|
+
and type fidelity. Composition does not create an atomic snapshot across
|
|
38
|
+
databases or APIs. `LocalDataSource` also accepts external objects with
|
|
39
|
+
`load(context) -> LoadedTable`, so a new input need not modify core.
|
|
40
|
+
|
|
41
|
+
SQLite additionally checks cancellation/timeouts through its native progress
|
|
42
|
+
handler every 1000 VM instructions, including before the first row is produced.
|
|
43
|
+
Its busy lock wait uses a 5-second SQLite timeout; interruption remains
|
|
44
|
+
cooperative. Stream reads and external batch retrieval remain caller-controlled
|
|
45
|
+
blocking operations and require transport timeouts supplied by that caller.
|
|
46
|
+
|
|
47
|
+
These adapters are library inputs. Untrusted SQL/callbacks are not sandboxed.
|
|
48
|
+
Register external inputs explicitly; document their type, consistency and size
|
|
49
|
+
guarantees before using them as interchangeable sources.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# EquiFrame más universal en cinco pasos
|
|
2
|
+
|
|
3
|
+
[Inicio](inicio.md) · [Fuentes](universal-sources.md) · [Extensiones](extending.md)
|
|
4
|
+
|
|
5
|
+
EquiFrame 0.12.4 permite cambiar la entrada conservando un
|
|
6
|
+
contrato y un plan nativo común. El siguiente recorrido utiliza datos sintéticos
|
|
7
|
+
y un esperado literal independiente. No necesita benchmark, servicios remotos,
|
|
8
|
+
credenciales, GitHub Actions ni recursos de pago.
|
|
9
|
+
|
|
10
|
+
1. **Entradas y contrato común.** CSV, JSON/JSONL, Parquet y Arrow reutilizan los
|
|
11
|
+
adaptadores existentes. `SQLiteInput` añade consultas locales parametrizadas
|
|
12
|
+
de solo lectura. `StreamInput` recibe respuestas binarias de un cliente
|
|
13
|
+
proporcionado por el consumidor. Las columnas, tipos, claves, ausencias,
|
|
14
|
+
monedas, fechas y duplicados siguen siendo decisiones explícitas del contrato.
|
|
15
|
+
2. **Operaciones nativas.** Se reutilizan filtros, proyecciones, conversiones
|
|
16
|
+
exactas admitidas, agregaciones, ordenamientos y seis tipos de join. El nuevo
|
|
17
|
+
`Window` añade numeración y valores anteriores/siguientes por partición o
|
|
18
|
+
sobre toda la entrada: `row_number`, `lag` y `lead`, con offset declarado.
|
|
19
|
+
Pandas, Polars y DuckDB ejecutan cada uno su implementación nativa.
|
|
20
|
+
3. **Significado de los datos.** Las ventanas conservan tipos y unidades, valores
|
|
21
|
+
extremos, precisión Decimal, NULL/NaN y zonas horarias admitidas. Los empates
|
|
22
|
+
se rechazan para impedir elecciones arbitrarias. Un tipo incompatible o una
|
|
23
|
+
capacidad no admitida se explica antes de ejecutar; un fallo nunca aprueba.
|
|
24
|
+
4. **Extensiones y límites.** `CompositeDataSource` vincula proveedores externos
|
|
25
|
+
a nombres lógicos. `RecordBatchInput` verifica esquemas Arrow explícitos y
|
|
26
|
+
límites acumulados. SQLite comprueba cancelación y tiempo durante consultas
|
|
27
|
+
largas. Un nuevo proveedor implementa `table_names` y `resolve`; un nuevo
|
|
28
|
+
input local implementa `load(context)`. No necesita modificar el núcleo.
|
|
29
|
+
5. **Primer uso completo.** El ejemplo instalado compara seis entradas bajo el
|
|
30
|
+
mismo contrato y plan en los tres motores: 18 ejecuciones por caso. Guarda
|
|
31
|
+
capturas, comprobaciones, reportes y un recibo; distingue coincidencia,
|
|
32
|
+
discrepancia e integridad fallida.
|
|
33
|
+
|
|
34
|
+
## Instala y ejecuta el ejemplo
|
|
35
|
+
|
|
36
|
+
Usa un wheel identificado por revisión y SHA-256. El código propio tiene
|
|
37
|
+
licencia MIT; los componentes de terceros conservan sus términos. La publicación
|
|
38
|
+
pública sigue pendiente. Desde una carpeta nueva fuera del checkout:
|
|
39
|
+
|
|
40
|
+
```powershell
|
|
41
|
+
py -3.12 -m venv .venv
|
|
42
|
+
.\.venv\Scripts\python.exe -m pip install 'C:\ruta\equiframe-0.12.4-py3-none-any.whl[engines]'
|
|
43
|
+
.\.venv\Scripts\python.exe -m pip check
|
|
44
|
+
.\.venv\Scripts\python.exe -m equiframe.examples.integrations.universal_first_use --output caso-correcto
|
|
45
|
+
.\.venv\Scripts\python.exe -m equiframe.examples.integrations.universal_first_use --case different --output caso-distinto
|
|
46
|
+
.\.venv\Scripts\python.exe -m equiframe.examples.integrations.universal_first_use --case corrupt --output caso-danado
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
En Linux/macOS sustituye el intérprete por `python3.12` y el ejecutable del
|
|
50
|
+
entorno por `.venv/bin/python`. Son instrucciones de uso; cada entorno requiere
|
|
51
|
+
su propia ejecución registrada. No presupongas validación nativa de macOS.
|
|
52
|
+
|
|
53
|
+
Los directorios de salida deben ser nuevos. El caso correcto termina con código
|
|
54
|
+
0 y 18 comparaciones equivalentes. `different` altera únicamente SQLite de
|
|
55
|
+
12.30 a 12.31: produce discrepancias en los tres motores y termina con código 1.
|
|
56
|
+
`corrupt` declara un digest incorrecto para la respuesta de API: rechaza esa
|
|
57
|
+
entrada en los tres motores, no compara sus resultados y termina con código 3.
|
|
58
|
+
Cada caso conserva un `receipt.json`; los casos evaluados también conservan
|
|
59
|
+
reportes bajo cada combinación de motor y fuente. El cliente API del ejemplo
|
|
60
|
+
es una respuesta en memoria explícitamente sintética.
|
|
61
|
+
|
|
62
|
+
## Cambia únicamente la configuración de entrada
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
source = ef.LocalDataSource({"input": ef.FileInput(csv_path, csv_spec)})
|
|
66
|
+
# La alternativa usa las mismas columnas declaradas con formato JSONL:
|
|
67
|
+
source = ef.LocalDataSource(
|
|
68
|
+
{
|
|
69
|
+
"input": ef.SQLiteInput(
|
|
70
|
+
database,
|
|
71
|
+
"SELECT id, sequence, amount FROM entries WHERE cohort = ?",
|
|
72
|
+
jsonl_spec,
|
|
73
|
+
("demo",),
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
El contrato y el plan no cambian. La especificación de lectura sí declara el
|
|
80
|
+
formato, los campos y las conversiones. No se infieren monedas ni se convierten
|
|
81
|
+
SQL REAL a Decimal exacto. Para un API real, pasa un context manager de respuesta
|
|
82
|
+
a `StreamInput`; tu cliente controla autenticación y timeouts. Para otro motor
|
|
83
|
+
SQL, implementa el puerto con tipos e identidad explícitos y comprueba su
|
|
84
|
+
consistencia; este candidato no incluye adaptadores PostgreSQL/MySQL.
|
|
85
|
+
|
|
86
|
+
## Límites comprobables
|
|
87
|
+
|
|
88
|
+
El IR y las ventanas admitidas forman parte del contrato estable; los proveedores,
|
|
89
|
+
las fuentes extendidas y la orquestación conservan su alcance experimental.
|
|
90
|
+
Las capacidades exactas y las
|
|
91
|
+
restricciones de ventanas están en [migration-ir](migration-ir.md) y en
|
|
92
|
+
`get_adapter_capabilities()`. DuckDB materializa y comprueba la ventana antes de
|
|
93
|
+
filtros o proyecciones posteriores, para que no oculten sus errores. No hay
|
|
94
|
+
frames arbitrarios ni agregaciones de ventana admitidos.
|
|
95
|
+
|
|
96
|
+
Los lotes reducen y controlan la lectura; las tablas finales y los recibos de
|
|
97
|
+
ingestión todavía se materializan. Los límites son cooperativos, no una cuota
|
|
98
|
+
dura de RAM ni procesamiento distribuido. Los bloqueos de transporte requieren
|
|
99
|
+
timeouts del cliente. Combinar proveedores no crea una transacción común entre
|
|
100
|
+
bases o APIs. Los contratos admiten dominios distintos, pero un ejemplo
|
|
101
|
+
sintético no acredita adopción real en salud, finanzas ni producción.
|
|
102
|
+
|
|
103
|
+
Los cinco pasos de implementación no son un porcentaje de Universalidad
|
|
104
|
+
UV01–UV12 ni una declaración de compatibilidad universal al 100 %. Windows,
|
|
105
|
+
Linux y macOS, versiones de Python y arquitecturas requieren recibos del mismo
|
|
106
|
+
candidato. La matriz histórica, licencias, revisión humana y publicación no se
|
|
107
|
+
cierran mediante estas mejoras técnicas.
|