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,333 @@
|
|
|
1
|
+
# Native relational contracts
|
|
2
|
+
|
|
3
|
+
[API](api.md) · [Semantics](semantics.md) · [Compatibility](compatibility.md)
|
|
4
|
+
|
|
5
|
+
These additions were introduced experimentally in 0.12.0a1 and retained in
|
|
6
|
+
0.12.0a14. Their admitted public semantics join the stable 0.12.0
|
|
7
|
+
`RelationalPlan` and `migrate` contracts. They do not infer business rules, change comparison policy,
|
|
8
|
+
translate arbitrary code, or calculate one output for conversion into other
|
|
9
|
+
engines. Every supported compiler executes its own native transformation.
|
|
10
|
+
The tests use independently specified literal expectations; dated receipts
|
|
11
|
+
preserve failed attempts and separately establish installed-artifact
|
|
12
|
+
validation. See the existing `get_adapter_capabilities()` and
|
|
13
|
+
`get_stability_register()` records, not a second capability catalogue.
|
|
14
|
+
|
|
15
|
+
## Literal column names
|
|
16
|
+
|
|
17
|
+
Column references and output names are exact, case-sensitive strings. A name
|
|
18
|
+
such as `*`, `^a$`, a dotted name, or a name containing quotes is a literal
|
|
19
|
+
column name, never a wildcard, regular expression, or SQL expression. Input
|
|
20
|
+
names, projected aliases, group keys, join keys, sort keys, and deduplication
|
|
21
|
+
keys obey the same rule. Names and column order are preserved; a missing name
|
|
22
|
+
is rejected by IR validation rather than expanded into matching columns.
|
|
23
|
+
Backends may use temporary internal names to execute a native operation, but
|
|
24
|
+
these do not appear in captured output. Existing `Text` validation, name
|
|
25
|
+
uniqueness and engine-specific identifier limits still apply: for example,
|
|
26
|
+
DuckDB rejects NUL names and case-insensitively colliding names. This contract
|
|
27
|
+
does not relax those boundaries or the separate table-identifier restrictions.
|
|
28
|
+
|
|
29
|
+
## Deterministic windows
|
|
30
|
+
|
|
31
|
+
The admitted `Window` node appends `WindowMeasure` outputs to every input
|
|
32
|
+
row. `row_number` starts at 1 in each partition, or in the entire input when
|
|
33
|
+
`partition_by=()`. `lag` and `lead` select the preceding/following row at a
|
|
34
|
+
positive `offset` (default 1), retaining its exact logical type and unit.
|
|
35
|
+
They return NULL outside the partition and preserve NULL values inside it.
|
|
36
|
+
No arithmetic, rounding, default fill or NULL skipping occurs.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
window = ef.Window(
|
|
40
|
+
node_id="history",
|
|
41
|
+
source="events",
|
|
42
|
+
partition_by=("account",),
|
|
43
|
+
by=(ef.SortKey(column="sequence", direction="ascending", nulls="last"),),
|
|
44
|
+
measures=(
|
|
45
|
+
ef.WindowMeasure(
|
|
46
|
+
name="position",
|
|
47
|
+
operation="row_number",
|
|
48
|
+
logical_type=ef.IntegerType(bits=64, signed=True),
|
|
49
|
+
unit="rows",
|
|
50
|
+
),
|
|
51
|
+
ef.WindowMeasure(
|
|
52
|
+
name="previous_amount",
|
|
53
|
+
operation="lag",
|
|
54
|
+
column="amount",
|
|
55
|
+
logical_type=ef.DecimalType(precision=12, scale=2),
|
|
56
|
+
),
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
NULL partition components group together. Every ordering term declares its
|
|
62
|
+
direction and NULL position; partition-plus-order tuples must be unique.
|
|
63
|
+
`ties="reject"` rejects even identical tied rows, so input order cannot choose
|
|
64
|
+
an arbitrary predecessor. Float partition/order keys remain unsupported.
|
|
65
|
+
`row_number` requires signed64, nonnullable output; lag/lead output is nullable
|
|
66
|
+
even for a nonnullable input. Row multiplicity remains unchanged. Output bag
|
|
67
|
+
order is unspecified until a final `Sort` requests it.
|
|
68
|
+
|
|
69
|
+
Each compiler uses its own native operations. DuckDB materializes the checked
|
|
70
|
+
window before executing later filters/projections, so they cannot prune its
|
|
71
|
+
error guard. See the eager checkpoint contract below.
|
|
72
|
+
Arbitrary frames, rank/dense_rank and window aggregates remain unsupported.
|
|
73
|
+
The additions extended the IR and nested migration/workflow schemas; historical
|
|
74
|
+
node defaults remain unchanged. IR/migration contracts are stable in 0.12.0;
|
|
75
|
+
standalone workflow orchestration retains its separate experimental status.
|
|
76
|
+
|
|
77
|
+
## Outer joins and cardinality
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
join = ef.EquiJoin(
|
|
81
|
+
node_id="joined",
|
|
82
|
+
left="old",
|
|
83
|
+
right="new",
|
|
84
|
+
keys=(ef.JoinKey(left="id", right="id"),),
|
|
85
|
+
how="full",
|
|
86
|
+
null_keys="never",
|
|
87
|
+
cardinality="one_to_one",
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`how` admits `inner`, `left`, `right`, `full`, `semi` and `anti`. Joins preserve
|
|
92
|
+
all matching occurrences. Outer joins preserve unmatched occurrences on their
|
|
93
|
+
respective sides; the absent side has NULL in **every** column. Both original
|
|
94
|
+
key columns remain, with `right_prefix="right."` on the right. There is no
|
|
95
|
+
implicit coalescing. The output contract must include both sides except for
|
|
96
|
+
semi/anti joins, which retain only left columns and left multiplicity.
|
|
97
|
+
Unmatched sides must be nullable in the output contract.
|
|
98
|
+
|
|
99
|
+
`cardinality` describes uniqueness of the **input matchable key tuples**:
|
|
100
|
+
|
|
101
|
+
- `one_to_one`: both sides unique
|
|
102
|
+
- `one_to_many`: left unique
|
|
103
|
+
- `many_to_one`: right unique
|
|
104
|
+
- `many_to_many` (historical default): neither side required unique
|
|
105
|
+
|
|
106
|
+
With `null_keys="equal"`, NULL components compare as equal and participate in
|
|
107
|
+
uniqueness. With `"never"`, a tuple with any NULL component cannot match and is
|
|
108
|
+
excluded from cardinality uniqueness checks; it is still retained where outer
|
|
109
|
+
join semantics require it. A duplicate matchable key violates the declaration
|
|
110
|
+
even if the other input is empty or contains no matching key. Violations fail
|
|
111
|
+
execution, rather than producing a successful empty result. This declaration
|
|
112
|
+
is technical input cardinality, not an inferred business relationship.
|
|
113
|
+
|
|
114
|
+
## Deterministic selection by key
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
selection = ef.Deduplicate(
|
|
118
|
+
node_id="latest",
|
|
119
|
+
source="events",
|
|
120
|
+
keys=("event_id",),
|
|
121
|
+
keep="first",
|
|
122
|
+
by=(ef.SortKey(column="revision", direction="descending", nulls="last"),),
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The existing `keep="identical"` still requires every column in `keys` and
|
|
127
|
+
forbids `by`. The new `keep="first"` requires a nonempty `by` tuple. Sort
|
|
128
|
+
semantics are applied within each key group, then exactly one row is selected.
|
|
129
|
+
NULL keys group together (`nulls="equal"`); each sort term explicitly declares
|
|
130
|
+
direction and NULL placement. The key-plus-sort tuple must be unique:
|
|
131
|
+
`ties="reject"` rejects all remaining ties, including identical tied rows.
|
|
132
|
+
No original input order or engine-dependent arbitrary first row resolves ties.
|
|
133
|
+
Input permutation therefore preserves the selected bag. Output sequence is
|
|
134
|
+
unspecified unless a final `Sort` establishes the order required by the contract.
|
|
135
|
+
Float key/tiebreak columns remain unsupported.
|
|
136
|
+
|
|
137
|
+
## Exact integer operations
|
|
138
|
+
|
|
139
|
+
Integer `add`, `subtract` and `multiply` accept signed and unsigned 8/16/32/64-bit
|
|
140
|
+
operands of identical type with matching explicit units. The declared integer
|
|
141
|
+
output must contain the actual result; overflow and unsigned underflow fail.
|
|
142
|
+
NULL operands propagate. Integer division remains unsupported.
|
|
143
|
+
|
|
144
|
+
Pandas operates on native object-backed integer Series, DuckDB widens operands
|
|
145
|
+
to native HUGEINT and checks its final cast, and Polars uses exact native
|
|
146
|
+
Decimal(38,0) coefficients. Polars proves a bound before multiplying because
|
|
147
|
+
UInt64 squared can exceed both signed Int128 and Decimal128. Every integer
|
|
148
|
+
result is checked against its exact destination bounds before the native cast.
|
|
149
|
+
No floating intermediate or cross-engine result fallback is used. DuckDB checks
|
|
150
|
+
each nested arithmetic expression before a containing predicate or later node.
|
|
151
|
+
|
|
152
|
+
## Exact Decimal operations
|
|
153
|
+
|
|
154
|
+
`Arithmetic(operation="multiply", left=..., right=..., logical_type=..., unit=...)`
|
|
155
|
+
admits Decimal operands with potentially different declared precision/scale.
|
|
156
|
+
The result scale must equal the sum of operand scales, at most 38; each operand
|
|
157
|
+
precision must be at most 38. Each actual intermediate result must fit its declared
|
|
158
|
+
precision. There is no implicit rounding. The explicit result `unit` declares
|
|
159
|
+
the intended product unit; the compiler does not infer dimensional algebra.
|
|
160
|
+
Existing add/subtract retain identical types/units, scale and a guard digit.
|
|
161
|
+
|
|
162
|
+
Polars multiplies native Decimal(38,0) coefficients and reconstructs the declared
|
|
163
|
+
summed scale with native string kernels. Before multiplying it checks each
|
|
164
|
+
non-NULL coefficient pair against `floor((10**precision - 1) / abs(right))`.
|
|
165
|
+
Zero and NULL operands are handled explicitly. The proof precedes native
|
|
166
|
+
multiplication, so valid large products are admitted without overflowing a
|
|
167
|
+
scaled intermediate. No float intermediate or Python row callback is used.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
rounded = ef.DecimalRound(
|
|
171
|
+
argument=ef.Column(name="amount"),
|
|
172
|
+
logical_type=ef.DecimalType(precision=12, scale=2),
|
|
173
|
+
rounding="half_away_from_zero",
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`DecimalRound` requires a Decimal argument and an output scale no greater than
|
|
178
|
+
its input scale. It preserves the argument's unit and NULL values. Overflow
|
|
179
|
+
rejects. `half_away_from_zero` and `half_even` are supported by all three
|
|
180
|
+
compilers. DuckDB uses exact HUGEINT coefficient division for half-even rather
|
|
181
|
+
than its floating `round_even` function. Rounding is a requested transformation,
|
|
182
|
+
never a comparison repair.
|
|
183
|
+
Fixed-scale Arrow output does not preserve a signed zero representation.
|
|
184
|
+
|
|
185
|
+
`Arithmetic(operation="divide", ..., logical_type=DecimalType(...), unit=...,
|
|
186
|
+
rounding="exact" | "half_even" | "half_away_from_zero")` uses independent
|
|
187
|
+
native implementations in all three engines. Pandas uses isolated Decimal
|
|
188
|
+
arithmetic; Polars uses native Decimal(38,0) coefficient arithmetic with Int128
|
|
189
|
+
quotient/remainder kernels, and DuckDB uses native HUGEINT
|
|
190
|
+
SQL quotient/remainder steps. `exact` is the default and rejects any
|
|
191
|
+
nonrepresentable quotient at the declared scale, including recurring fractions.
|
|
192
|
+
The explicit rounding modes quantize to the declared scale. A non-NULL
|
|
193
|
+
zero denominator fails; NULL operands propagate without evaluating a division
|
|
194
|
+
by zero. Integer division is unsupported. Polars and DuckDB support Decimal
|
|
195
|
+
coefficients through precision 38. Modular doubling keeps each remainder below
|
|
196
|
+
its denominator without an overflowing multiplication by ten; scale adjustment
|
|
197
|
+
takes at most 76 native digit steps. No FLOAT/DOUBLE intermediate, Python row UDF
|
|
198
|
+
or common-result fallback is used. DuckDB materializes each fallible expression
|
|
199
|
+
before a containing expression or later relational node can discard it.
|
|
200
|
+
Large inputs and scales can require
|
|
201
|
+
substantial working memory/time; cooperative budgets are not hard RSS quotas.
|
|
202
|
+
|
|
203
|
+
## Timestamp transport and DST localization
|
|
204
|
+
|
|
205
|
+
`TimestampType(unit=..., timezone=...)` is admitted in IR columns with the
|
|
206
|
+
existing matching `TimestampPolicy` on their input/output contracts. Units are
|
|
207
|
+
physical `s`, `ms`, `us` and `ns`. Transport does not silently change unit/zone:
|
|
208
|
+
|
|
209
|
+
All three compilers transport all four units with naive or named-zone timestamps.
|
|
210
|
+
Polars and DuckDB IR transformations explicitly encode timestamps as unchanged
|
|
211
|
+
signed int64 ticks before native execution and restore their unit/zone metadata
|
|
212
|
+
through Arrow buffer views. A decoder requires actual int64 output; it never
|
|
213
|
+
casts floats or incorrectly typed output to fit a contract. Each engine still
|
|
214
|
+
executes its own operations. The codec is not a common ETL result or an implicit
|
|
215
|
+
comparison repair. Native input/output types and the encoding are recorded.
|
|
216
|
+
|
|
217
|
+
This encoding belongs to compiler-generated IR transformations. General trusted
|
|
218
|
+
callbacks/SQL keep their existing native unit, zone, range and infinity-sentinel
|
|
219
|
+
restrictions. Non-NULL timestamp literals, calendar arithmetic and implicit
|
|
220
|
+
zone/unit conversion have not been added.
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
localized = ef.LocalizeTimestamp(
|
|
224
|
+
argument=ef.Column(name="local_time"),
|
|
225
|
+
timezone="America/New_York",
|
|
226
|
+
ambiguous="earlier",
|
|
227
|
+
nonexistent="reject",
|
|
228
|
+
)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`LocalizeTimestamp` interprets a **naive** timestamp as local wall time in the
|
|
232
|
+
named IANA zone, preserving its physical unit and NULLs. The output contract
|
|
233
|
+
must declare that zone and the same unit. `ambiguous="reject"` rejects a fold;
|
|
234
|
+
`"earlier"` and `"later"` choose the earlier/later UTC instant, independent of
|
|
235
|
+
whether a provider labels an offset as daylight time. Gaps always reject;
|
|
236
|
+
`nonexistent="reject"` is the only admitted value. Already-zoned input is
|
|
237
|
+
rejected: conversion between zones is a different operation.
|
|
238
|
+
|
|
239
|
+
The implementations remain independent: pandas uses its native datetime route,
|
|
240
|
+
Polars computes UTC candidates with native expressions from the installed
|
|
241
|
+
`tzdata` TZif metadata, and DuckDB uses native ICU round trips. The Polars
|
|
242
|
+
compiler records the exact resource hashes in its compilation identity and
|
|
243
|
+
rejects a resource changed before execution. Explicit transition intervals and
|
|
244
|
+
POSIX continuation rules retain historical second offsets, negative daylight
|
|
245
|
+
adjustments and fractional-hour zones. Only timezone metadata is read in Python;
|
|
246
|
+
table rows, candidate counts and fold selection stay in Polars. Gaps, rejected
|
|
247
|
+
folds and output overflow remain errors, including inside a branch or filter.
|
|
248
|
+
|
|
249
|
+
DuckDB
|
|
250
|
+
enumerates every integral-second UTC offset within the IR's maximum one-day
|
|
251
|
+
displacement, counts matching instants and applies the requested policy. Integer
|
|
252
|
+
quotient/remainder preserves nanosecond residuals, including negative ticks.
|
|
253
|
+
The search includes half-hour folds, historical second offsets and skipped days;
|
|
254
|
+
it does not accept ICU's default fold/gap choice without checking it.
|
|
255
|
+
|
|
256
|
+
pandas rejects an int64-min localization input because its datetime route
|
|
257
|
+
reserves that tick for NaT. Polars seconds localization uses a checked native
|
|
258
|
+
millisecond view and rejects if it cannot preserve the value exactly. Polars and
|
|
259
|
+
pandas check NULL masks and tick displacement in an overflow-safe numeric domain
|
|
260
|
+
before a containing predicate can prune a bad value. DuckDB guards its native
|
|
261
|
+
microsecond calendar range with a two-day margin and checks final int64 ticks;
|
|
262
|
+
its search itself bounds displacement to one day. These localization limits do
|
|
263
|
+
not narrow raw IR tick transport. Native range exceptions remain failures.
|
|
264
|
+
|
|
265
|
+
DuckDB materializes the localized ticks before a containing predicate or later
|
|
266
|
+
relational operation. Searching 172,801 offsets per correlated scalar evaluation can
|
|
267
|
+
be expensive for many distinct wall times; the query uses the configured memory,
|
|
268
|
+
threads and cooperative interruption limits. Those limits are not a hard total
|
|
269
|
+
RSS quota. Provider/version differences remain recorded: agreement on tested
|
|
270
|
+
dates does not prove identical IANA databases in all engines.
|
|
271
|
+
|
|
272
|
+
Polars compiler `product-semantics-v13` adds this TZif route after a revised
|
|
273
|
+
Colombian transition in `tzdata` differed from the bundled Polars timezone
|
|
274
|
+
provider. General Polars callbacks still use their own provider. Updating
|
|
275
|
+
`tzdata` requires revalidation of affected plans; previous wheel results and
|
|
276
|
+
finite campaign coverage do not transfer automatically to a new artifact.
|
|
277
|
+
|
|
278
|
+
## Eager failures and historical evidence
|
|
279
|
+
|
|
280
|
+
DuckDB compiler v8 produces a private program of native SELECT checkpoints.
|
|
281
|
+
The native adapter consumes each node's entire declared output into Arrow
|
|
282
|
+
before registering it for subsequent nodes. It also evaluates nested arithmetic,
|
|
283
|
+
rounding and localization from the innermost expression outward, before a
|
|
284
|
+
containing predicate can simplify them. Later filters, projections, joins and
|
|
285
|
+
aggregations therefore cannot hide overflow, forbidden ties or cardinality
|
|
286
|
+
errors. Joins and deterministic deduplication retain a separate error-producing
|
|
287
|
+
guard branch, including when the native result is empty.
|
|
288
|
+
|
|
289
|
+
These Arrow tables are in-memory transport from DuckDB back into the same
|
|
290
|
+
private DuckDB connection. No result comes from another engine, Python row UDF
|
|
291
|
+
or a shared database. A native ordinal preserves observed row order across
|
|
292
|
+
scalar expression kernels; join/group/window order remains unspecified until
|
|
293
|
+
`Sort`. Relations are released after their last consumer, including shared
|
|
294
|
+
branches. The connection closes on success, failure, cancellation or timeout.
|
|
295
|
+
|
|
296
|
+
Each materialized intermediate observes the cooperative Arrow byte budget and
|
|
297
|
+
checks cancellation/time; final row limits apply to the final output. Helper
|
|
298
|
+
columns can increase working memory and native checkpoint calls add overhead.
|
|
299
|
+
These checks are not a total process RSS cap or a hard deadline. DuckDB's
|
|
300
|
+
[order-preservation rules](https://duckdb.org/docs/current/sql/dialect/order_preservation)
|
|
301
|
+
explain why expression checkpoints explicitly restore their native ordinal.
|
|
302
|
+
|
|
303
|
+
Compiler-generated programs require `DuckDBAdapter` or an implementation that
|
|
304
|
+
retains that native adapter's program execution. An unrelated adapter is
|
|
305
|
+
rejected before resolving a provider. Replaying only the program's `.sql`
|
|
306
|
+
fails explicitly; it cannot bypass the checkpoints. Ordinary registered trusted
|
|
307
|
+
`DuckDBTransformation` SQL keeps its single SELECT/WITH interface. This changes
|
|
308
|
+
the private compiled IR implementation, not serialized IR/result formats or the
|
|
309
|
+
stable admitted semantic contract.
|
|
310
|
+
|
|
311
|
+
Fallible expressions inside `Coalesce`, `Conditional` or boolean short-circuit
|
|
312
|
+
forms still require preceding projected checkpoints in the common IR validator.
|
|
313
|
+
Integer division, conservative numeric bounds and the type/zone limits above
|
|
314
|
+
remain explicit. Pandas and Polars perform their own native checks. Pandas
|
|
315
|
+
both-empty full joins preserve an explicit typed empty frame. DuckDB Decimal
|
|
316
|
+
multiplication promotes each operand to precision 38 at its own scale before
|
|
317
|
+
multiplying; an outer result cast alone would overflow a DECIMAL(18)
|
|
318
|
+
intermediate for valid large inputs.
|
|
319
|
+
|
|
320
|
+
Format 1.0 readers remain compatible. Newly added historical defaults
|
|
321
|
+
(`many_to_many`, `rounding="exact"`, empty dedup `by` and its fixed tie policy)
|
|
322
|
+
are omitted from serialized old-shaped nodes, preserving existing IR and full
|
|
323
|
+
specification fingerprints. Nondefault new options and new expression kinds
|
|
324
|
+
are serialized and fingerprinted. New compiler implementations use new code
|
|
325
|
+
identities; old stored identities are not recomputed or rewritten.
|
|
326
|
+
|
|
327
|
+
The finite new technical matrix explicitly covers the 144 combinations of six
|
|
328
|
+
join kinds, four cardinalities, two NULL policies and three engines on one
|
|
329
|
+
composite-key fixture, plus 24 engine/unit/zone timestamp transport cells and
|
|
330
|
+
separate literal/migration/overflow/DST/permutation cases. It is not exhaustive
|
|
331
|
+
over all types, timezones, numeric values, plans, directions or interactions.
|
|
332
|
+
The semantic matrix records source execution separately from final installed
|
|
333
|
+
artifact results; neither establishes thesis outcomes or business truth.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Audit options without inventing coverage
|
|
2
|
+
|
|
3
|
+
[API](api.md) · [Compatibility](compatibility.md) · [Private installation](installed-private.md)
|
|
4
|
+
|
|
5
|
+
The developer tool `tools/option_inventory.py`, shipped in the source
|
|
6
|
+
archive, inspects a **normally installed** candidate. Run it from an external
|
|
7
|
+
folder with the target venv's Python and `-I`. Give the exact wheel and source
|
|
8
|
+
commit. It refuses imports outside that environment's site-packages and checks
|
|
9
|
+
every shipped package file against the wheel before recording an inventory.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python -I option_inventory.py --wheel equiframe-0.12.4-py3-none-any.whl \
|
|
13
|
+
--commit EXACT_40_CHARACTER_COMMIT --output inventory.json --matrix obligations.json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
To include the separate benchmark, install it normally and add `--benchmark
|
|
17
|
+
--benchmark-wheel equiframe_benchmark-0.12.4-py3-none-any.whl`.
|
|
18
|
+
The inspection harness uses only dependencies already declared by the inspected
|
|
19
|
+
packages. It never executes default factories merely to discover their names.
|
|
20
|
+
|
|
21
|
+
The output extends `get_stability_register()` and `get_adapter_capabilities()`;
|
|
22
|
+
it does not replace either with a competing support catalog. It records exports,
|
|
23
|
+
alias identity, function parameters/defaults, Pydantic fields/constraints,
|
|
24
|
+
Literal/Enum alternatives, boolean defaults and both branches, declared methods,
|
|
25
|
+
CLI paths/options and extras. Output/declarative types do not count as executable
|
|
26
|
+
features. Dependencies and operating system/Python are recorded explicitly.
|
|
27
|
+
|
|
28
|
+
Every obligation begins as NOT_RUN. Independent semantic evidence is necessary
|
|
29
|
+
to promote it; a discovered signature, matching schema, import, code mention or
|
|
30
|
+
line-coverage hit is not a correctness test. Defaults and explicit boolean values
|
|
31
|
+
are separate obligations. Numeric/text spaces require declared equivalence and
|
|
32
|
+
boundary classes; there is no exhaustive test of an infinite value space.
|
|
33
|
+
|
|
34
|
+
Execution states:
|
|
35
|
+
|
|
36
|
+
- PASS: the executed case satisfied an independent expected result
|
|
37
|
+
- EXPECTED_REJECTION: the rejection behaved correctly; application data was not accepted
|
|
38
|
+
- FAIL: observed behavior contradicted the expected result
|
|
39
|
+
- BLOCKED: a named external precondition prevented execution
|
|
40
|
+
- NOT_RUN: no qualifying execution receipt is linked
|
|
41
|
+
|
|
42
|
+
Count unique test IDs separately from option rows and repeated compatibility
|
|
43
|
+
runs. Record high-risk finite cross-products and constrained pairwise choices;
|
|
44
|
+
list any uncovered interactions. Do not report 100% behavioral option coverage
|
|
45
|
+
while obligations remain unlinked or their independent expectation is missing.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Explicit outcome gates
|
|
2
|
+
|
|
3
|
+
Object presence, completed execution, observed equivalence, agreement with an
|
|
4
|
+
expected output and authorization to publish are different claims. A returned
|
|
5
|
+
object, empty output, empty alert list or generated report cannot supply the
|
|
6
|
+
missing claim.
|
|
7
|
+
|
|
8
|
+
## Python migration
|
|
9
|
+
|
|
10
|
+
The original private consolidation changed Python truth testing for the
|
|
11
|
+
operational types inventoried below; 0.12.0 retains that boundary. Previously
|
|
12
|
+
these objects inherited Python's
|
|
13
|
+
ordinary object truthiness: even a failed or unavailable result was truthy. They
|
|
14
|
+
now raise `TypeError` for `bool(result)`, `if result`, `assert result`, `not result`,
|
|
15
|
+
truth-tested `and`/`or` operands and `any`/`all` over result objects, in every state.
|
|
16
|
+
The exception names the type and points to explicit fields or the assertion API.
|
|
17
|
+
`ComparisonResult` and `EquivalenceDecision` already rejected truth testing;
|
|
18
|
+
their existing behavior and messages are preserved.
|
|
19
|
+
|
|
20
|
+
Review callers that used object truthiness for a gate. Replace it with the actual
|
|
21
|
+
required verdict; do not mechanically change an approval predicate into a
|
|
22
|
+
presence check. Existing evidence accepted solely because an object existed
|
|
23
|
+
must be reevaluated from its states, coverage and required dimensions.
|
|
24
|
+
|
|
25
|
+
Only a real optional-presence check should become `result is not None`:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
if pipeline.execution is not None:
|
|
29
|
+
print(pipeline.execution.state) # This merely displays available evidence.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For comparisons, keep the result and use the established gate:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
result = ef.diff(candidate, expected, keys=["id"], work_dir=work_dir)
|
|
36
|
+
report = ef.write_comparison_report(result, report_dir)
|
|
37
|
+
print(report.to_text()) # Reports can be generated for failed/incomplete results.
|
|
38
|
+
ef.assert_equivalent(result) # Raises for different and not_evaluable.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
A consumer of an `EquivalenceDecision` uses `.accepted` explicitly. A workflow
|
|
42
|
+
or migration consumer checks the dimensions its policy requires in addition to
|
|
43
|
+
technical completeness. The existing exit codes, defaults and semantic meanings
|
|
44
|
+
are unchanged; the guard introduces no implicit acceptance policy.
|
|
45
|
+
|
|
46
|
+
## Exact guarded inventory
|
|
47
|
+
|
|
48
|
+
These public classes reject truth testing. No new public base class is exported.
|
|
49
|
+
|
|
50
|
+
| Types | Explicit interpretation |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `ComparisonResult` | `assert_equivalent(result)`, or the explicit state/completeness and guarantee policy |
|
|
53
|
+
| `EquivalenceDecision` | `.accepted`, with guarantee and evidence diagnostics |
|
|
54
|
+
| `ComparisonReport` | Presentation only; gate the original comparison |
|
|
55
|
+
| `ExecutionResult` | `.state`; `completed` only establishes execution completion |
|
|
56
|
+
| `PipelineResult` | `.complete`, `.exit_code`, requested R/M/B/H and comparison outcomes |
|
|
57
|
+
| `MigrationResult` | `.state`, `.verification`, `.complete`, `.exit_code`; peer agreement alone is not expected-output correctness |
|
|
58
|
+
| `CompilationCheck` | `.supported`; support is preflight admission, not execution or equivalence |
|
|
59
|
+
| `VerificationResult` | `.operational_state`, `.publishable`, and explicitly required dimensions; `.publishable` is the existing operational result, not external authorization or scientific review |
|
|
60
|
+
| `VerificationCheck` | `.state`; `not_requested` and `pending_review` are not passed checks |
|
|
61
|
+
| `WorkflowResult` | `.complete`, `.exit_code` and required checkpoints |
|
|
62
|
+
| `WorkflowCheckpoint` | `.state` and the migration's explicit dimensions |
|
|
63
|
+
| `IngestionResult` | `.state`, accepted/quarantined counts and the declared ingestion policy |
|
|
64
|
+
| `RuleEvaluation` | `.decision`, `.complete`, `.coverage`; `no_alert` is not equivalence |
|
|
65
|
+
| `CheckResult` | `.state`, `.complete`, required/applicable coverage |
|
|
66
|
+
| `ProfileResult` | `.complete` and feature availability; a profile makes no approval decision |
|
|
67
|
+
| `DetectorScores` | `.state` and score values; scoring is not classification |
|
|
68
|
+
| `DetectorScoreResult` | `.m.state`, `.b.state`, `.rows`; M and B remain separate |
|
|
69
|
+
| `DetectionResult` | Individual `.m`/`.b` decisions and evaluated coverage |
|
|
70
|
+
| `ComponentDetectionResult` | `.decisions`, `.complete`, `.rows`; an empty batch cannot establish evidence about absent rows |
|
|
71
|
+
| `ComponentDecision` | `.decision`, `.complete`; `not_evaluable` is not `no_alert` |
|
|
72
|
+
| `DetectorDecision` | `.result.decision`, `.result.complete` and score/threshold |
|
|
73
|
+
| `HybridDecision` | `.result.decision`, `.result.complete`; H retains R OR M, not correctness |
|
|
74
|
+
| `RReviewResult` | `.state`, `.complete`, `.reason`; the language-R reviewer is independent of operational rules R, and unavailable R does not establish equivalence |
|
|
75
|
+
| `ReviewedDecision` | `.accepted`, `.result`, `.hybrid`, `.r_review`, optional `.comparison` and `.execution_binding`; existing alerts and comparator disagreement stay visible |
|
|
76
|
+
| `ReductionResult` | `.status`, `.baseline_verified`, `.minimality`; a reduction result is not validation of a transformation |
|
|
77
|
+
|
|
78
|
+
The scope is intentionally explicit. Contracts, options, schemas, artifact
|
|
79
|
+
references, table/profile data, compiled plans, fitted preprocessors/models,
|
|
80
|
+
calibration bundles, provenance declarations and stage bookkeeping keep their
|
|
81
|
+
existing behavior. They are not approval APIs. This change does not make
|
|
82
|
+
`FrozenModel` or every class with a `state` field reject truth testing. Fitted
|
|
83
|
+
bundles still require inspecting model/preprocessor/calibration availability
|
|
84
|
+
before their established inference APIs can be used.
|
|
85
|
+
|
|
86
|
+
`DatasetResult` holds loaded data and an optional owned DuckDB connection; it is
|
|
87
|
+
not an approval result and does not adopt these truth-value guards. Use its
|
|
88
|
+
`pandas`, `frame`, `target` and `info` fields for data access and `close()` or a
|
|
89
|
+
context manager for resource ownership. Successful file loading/conversion does
|
|
90
|
+
not establish equivalence of two independently written transformations.
|
|
91
|
+
|
|
92
|
+
## Empty and incomplete evidence
|
|
93
|
+
|
|
94
|
+
- Two empty tables can legitimately be equivalent under a contract that permits
|
|
95
|
+
emptiness. This does not prove that the business expected any rows. Require an
|
|
96
|
+
independently expected output or an explicit cardinality rule when appropriate.
|
|
97
|
+
- Ingestion can produce zero accepted records, including an empty completed
|
|
98
|
+
input. Inspect counts and policy; an existing output artifact is not approval.
|
|
99
|
+
- Empty detector batches preserve their existing coverage semantics. They do not
|
|
100
|
+
demonstrate that a required population was evaluated. Check required row coverage
|
|
101
|
+
and each component's availability before interpreting absence of alerts.
|
|
102
|
+
- A comparison report preserves `different` or `not_evaluable`. Successful report
|
|
103
|
+
publication does not change the comparison's verdict.
|
|
104
|
+
- Missing rules, unavailable features, cancelled work and partial detector
|
|
105
|
+
availability retain their explicit states and diagnostics.
|
|
106
|
+
|
|
107
|
+
## Compatibility and verification
|
|
108
|
+
|
|
109
|
+
The guard is a field-free private Python mixin. Public names, import paths,
|
|
110
|
+
model fields, JSON schema, serialization, versions, canonical bytes and
|
|
111
|
+
fingerprint algorithms are unchanged. Existing persisted documents load into
|
|
112
|
+
the same public models and acquire the safer Python behavior. No historical
|
|
113
|
+
fixture is regenerated to make this change pass.
|
|
114
|
+
|
|
115
|
+
`tests/test_operational_truthiness.py` exercises real positive, discrepant,
|
|
116
|
+
failed, unavailable, empty and cancelled routes, serialization round-trips,
|
|
117
|
+
explicit inventory and unchanged configuration behavior. Existing native,
|
|
118
|
+
verification, evidence, schema/digest and immutable historical-reader tests
|
|
119
|
+
exercise the migrated internal presence checks. The installed executable example
|
|
120
|
+
`equiframe/examples/integrations/outcome_gates.py` demonstrates the gate outside
|
|
121
|
+
the source checkout with the identified wheel and its `arrow` extra.
|
|
122
|
+
|
|
123
|
+
After installing that wheel and its reviewed dependencies, extract and run the
|
|
124
|
+
example in a fresh working directory:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
python -c "from importlib.resources import files; from pathlib import Path; Path('outcome_gates.py').write_bytes(files('equiframe').joinpath('examples/integrations/outcome_gates.py').read_bytes())"
|
|
128
|
+
python -I -O outcome_gates.py --work-dir outcome-evidence
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The script exits zero only after observing the expected assertion verdict for
|
|
132
|
+
all four synthetic cases and verifying that implicit approval is rejected. Its
|
|
133
|
+
checks also run with Python assertions disabled (`-O`). This is software behavior
|
|
134
|
+
validation, not a claim of business correctness or human adoption.
|