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.
Files changed (170) hide show
  1. equiframe/__init__.py +832 -0
  2. equiframe/__main__.py +3 -0
  3. equiframe/_base.py +84 -0
  4. equiframe/_cli_support.py +125 -0
  5. equiframe/_comparison_io.py +161 -0
  6. equiframe/_dataset_provenance.py +146 -0
  7. equiframe/_decimal.py +22 -0
  8. equiframe/_diff_capture.py +433 -0
  9. equiframe/_doctor.py +123 -0
  10. equiframe/_document_io.py +61 -0
  11. equiframe/_documents.py +79 -0
  12. equiframe/_duckdb_transport.py +149 -0
  13. equiframe/_error_reporting.py +52 -0
  14. equiframe/_first_use.py +102 -0
  15. equiframe/_path_compat.py +26 -0
  16. equiframe/_physical.py +133 -0
  17. equiframe/_r_review.R +308 -0
  18. equiframe/_regular_io.py +167 -0
  19. equiframe/_report_format_v1.py +298 -0
  20. equiframe/_report_io.py +591 -0
  21. equiframe/_stability_inventory.py +1421 -0
  22. equiframe/_timezones.py +257 -0
  23. equiframe/_vendor/cyclonedx/LICENSE +201 -0
  24. equiframe/_vendor/cyclonedx/PROVENANCE.md +18 -0
  25. equiframe/_vendor/cyclonedx/bom-1.6.schema.json +5673 -0
  26. equiframe/_vendor/cyclonedx/jsf-0.82.schema.json +240 -0
  27. equiframe/_vendor/cyclonedx/spdx.schema.json +712 -0
  28. equiframe/adapters.py +454 -0
  29. equiframe/assertion_types.py +122 -0
  30. equiframe/assertions.py +271 -0
  31. equiframe/benchmark.py +101 -0
  32. equiframe/bundle.py +240 -0
  33. equiframe/cli.py +693 -0
  34. equiframe/comparison.py +937 -0
  35. equiframe/comparison_report.py +660 -0
  36. equiframe/comparison_types.py +257 -0
  37. equiframe/compatibility.py +546 -0
  38. equiframe/contracts.py +879 -0
  39. equiframe/datasets.py +1301 -0
  40. equiframe/decision.py +34 -0
  41. equiframe/destinations.py +295 -0
  42. equiframe/detector_types.py +431 -0
  43. equiframe/detectors.py +389 -0
  44. equiframe/diff.py +192 -0
  45. equiframe/diff_contracts.py +523 -0
  46. equiframe/docs/adoption.md +329 -0
  47. equiframe/docs/api.md +534 -0
  48. equiframe/docs/arquitecto.md +143 -0
  49. equiframe/docs/choosing-api.md +84 -0
  50. equiframe/docs/cientifico.md +180 -0
  51. equiframe/docs/compatibility.md +254 -0
  52. equiframe/docs/contributing.md +171 -0
  53. equiframe/docs/dependencies.md +237 -0
  54. equiframe/docs/eda.md +166 -0
  55. equiframe/docs/extending.md +158 -0
  56. equiframe/docs/fast-ci.md +52 -0
  57. equiframe/docs/first-use.md +78 -0
  58. equiframe/docs/index.md +142 -0
  59. equiframe/docs/ingestion.md +145 -0
  60. equiframe/docs/inicio.md +129 -0
  61. equiframe/docs/installed-private.md +97 -0
  62. equiframe/docs/journeys.md +362 -0
  63. equiframe/docs/lectura-conversion.md +211 -0
  64. equiframe/docs/licensing.md +17 -0
  65. equiframe/docs/maintenance.md +233 -0
  66. equiframe/docs/migration-ir.md +333 -0
  67. equiframe/docs/option-audit.md +45 -0
  68. equiframe/docs/outcome-gates.md +134 -0
  69. equiframe/docs/performance.md +263 -0
  70. equiframe/docs/piloto-simulado.md +56 -0
  71. equiframe/docs/quickstart.md +156 -0
  72. equiframe/docs/r_review.md +212 -0
  73. equiframe/docs/recipes.md +248 -0
  74. equiframe/docs/releasing.md +183 -0
  75. equiframe/docs/security.md +218 -0
  76. equiframe/docs/semantics.md +274 -0
  77. equiframe/docs/sqlite-snapshots.md +262 -0
  78. equiframe/docs/universal-sources.md +49 -0
  79. equiframe/docs/universalidad-cinco-pasos.md +107 -0
  80. equiframe/docs/upgrading.md +538 -0
  81. equiframe/duckdb_capture.py +410 -0
  82. equiframe/eda/__init__.py +36 -0
  83. equiframe/eda/__main__.py +3 -0
  84. equiframe/eda/_backend.py +612 -0
  85. equiframe/eda/_inventory.py +49 -0
  86. equiframe/eda/_paths.py +49 -0
  87. equiframe/eda/_report.py +291 -0
  88. equiframe/eda/api.py +535 -0
  89. equiframe/eda/cli.py +102 -0
  90. equiframe/eda/models.py +239 -0
  91. equiframe/errors.py +83 -0
  92. equiframe/evidence.py +375 -0
  93. equiframe/examples/integrations/eda_generic.py +60 -0
  94. equiframe/examples/integrations/outcome_gates.py +94 -0
  95. equiframe/examples/integrations/sqlite_snapshot.py +246 -0
  96. equiframe/examples/integrations/sqlite_snapshot_demo.py +226 -0
  97. equiframe/examples/integrations/universal_first_use.py +296 -0
  98. equiframe/examples/journeys/README.md +31 -0
  99. equiframe/examples/journeys/__init__.py +1 -0
  100. equiframe/examples/journeys/a_pandas_refactor.py +88 -0
  101. equiframe/examples/journeys/b_native_migration.py +181 -0
  102. equiframe/examples/journeys/c_retail_etl.py +460 -0
  103. equiframe/examples/journeys/common.py +136 -0
  104. equiframe/examples/journeys/d_financial_reconciliation.py +96 -0
  105. equiframe/examples/journeys/e_telemetry.py +135 -0
  106. equiframe/examples/journeys/f_data_science_preparation.py +84 -0
  107. equiframe/examples/journeys/g_external_snapshots.py +197 -0
  108. equiframe/examples/journeys/h_portable_ci.py +129 -0
  109. equiframe/examples/journeys/requirements.lock +249 -0
  110. equiframe/examples/journeys/verify_journeys.py +117 -0
  111. equiframe/examples/load_convert_compare.py +85 -0
  112. equiframe/execution.py +355 -0
  113. equiframe/execution_types.py +177 -0
  114. equiframe/ingestion.py +744 -0
  115. equiframe/local.py +126 -0
  116. equiframe/migration/__init__.py +94 -0
  117. equiframe/migration/_physical.py +8 -0
  118. equiframe/migration/backends/_timestamp_transport.py +109 -0
  119. equiframe/migration/backends/duckdb.py +651 -0
  120. equiframe/migration/backends/duckdb_decimal.py +125 -0
  121. equiframe/migration/backends/duckdb_time.py +87 -0
  122. equiframe/migration/backends/pandas.py +544 -0
  123. equiframe/migration/backends/polars.py +915 -0
  124. equiframe/migration/backends/polars_decimal.py +231 -0
  125. equiframe/migration/io.py +198 -0
  126. equiframe/migration/ports.py +96 -0
  127. equiframe/migration/runner.py +545 -0
  128. equiframe/migration/runner_types.py +405 -0
  129. equiframe/migration/types.py +354 -0
  130. equiframe/migration/validation.py +619 -0
  131. equiframe/normalization.py +358 -0
  132. equiframe/pipeline.py +274 -0
  133. equiframe/pipeline_types.py +247 -0
  134. equiframe/ports.py +39 -0
  135. equiframe/preprocessing.py +110 -0
  136. equiframe/profile_types.py +343 -0
  137. equiframe/profiles.py +447 -0
  138. equiframe/py.typed +0 -0
  139. equiframe/r_review.py +706 -0
  140. equiframe/reduction.py +518 -0
  141. equiframe/registry.py +121 -0
  142. equiframe/report_rendering.py +366 -0
  143. equiframe/report_types.py +294 -0
  144. equiframe/reporting.py +26 -0
  145. equiframe/rule_types.py +178 -0
  146. equiframe/rules.py +819 -0
  147. equiframe/schemas/r-execution-binding-1.0.schema.json +46 -0
  148. equiframe/schemas/r-review-binding-1.0.schema.json +28 -0
  149. equiframe/schemas/r-review-options-1.0.schema.json +38 -0
  150. equiframe/schemas/r-review-result-1.0.schema.json +207 -0
  151. equiframe/schemas/reviewed-decision-1.0.schema.json +3394 -0
  152. equiframe/serialization.py +152 -0
  153. equiframe/snapshot.py +296 -0
  154. equiframe/sources.py +283 -0
  155. equiframe/tables.py +459 -0
  156. equiframe/types.py +71 -0
  157. equiframe/values.py +252 -0
  158. equiframe/verification_request.py +35 -0
  159. equiframe/verify.py +799 -0
  160. equiframe/verify_types.py +255 -0
  161. equiframe/workflow.py +510 -0
  162. equiframe/workflow_request.py +38 -0
  163. equiframe-0.12.4.dist-info/METADATA +268 -0
  164. equiframe-0.12.4.dist-info/RECORD +170 -0
  165. equiframe-0.12.4.dist-info/WHEEL +4 -0
  166. equiframe-0.12.4.dist-info/entry_points.txt +3 -0
  167. equiframe-0.12.4.dist-info/licenses/LICENSE +21 -0
  168. equiframe-0.12.4.dist-info/licenses/THIRD_PARTY_NOTICES.md +42 -0
  169. equiframe-0.12.4.dist-info/licenses/tools/cyclonedx/LICENSE +201 -0
  170. equiframe-0.12.4.dist-info/licenses/tools/cyclonedx/PROVENANCE.md +18 -0
@@ -0,0 +1,178 @@
1
+ """Supplementary v1 evidence for operational rules; contract 1.0 stays unchanged."""
2
+
3
+ from typing import Annotated, Literal
4
+
5
+ from pydantic import Field, model_validator
6
+ from typing_extensions import Self
7
+
8
+ from equiframe._base import (
9
+ FrozenModel,
10
+ Identifier,
11
+ NonNegativeInt,
12
+ PositiveInt,
13
+ Text,
14
+ _ExplicitOutcome,
15
+ fail,
16
+ )
17
+ from equiframe.comparison_types import ComparisonValue
18
+ from equiframe.types import ArtifactRef, DecisionState, Sha256
19
+
20
+ CheckState = Literal["satisfied", "violated", "not_applicable", "not_evaluable"]
21
+
22
+
23
+ class RuleOptions(FrozenModel):
24
+ max_rows: PositiveInt = 1_000_000
25
+ max_artifact_bytes: PositiveInt = 128 * 1024 * 1024
26
+ max_evidence_bytes: PositiveInt = 64 * 1024 * 1024
27
+ max_work: PositiveInt = 10_000_000
28
+ max_examples: NonNegativeInt = 20
29
+ timeout_seconds: Annotated[float, Field(gt=0)] | None = None
30
+
31
+
32
+ class RuleFinding(FrozenModel):
33
+ schema_version: Literal["1.0"] = "1.0"
34
+ check_id: Identifier
35
+ code: Text
36
+ table: Identifier
37
+ position: NonNegativeInt | None = None
38
+ column: str | None = None
39
+ observed: tuple[ComparisonValue, ...] = ()
40
+ expected: tuple[ComparisonValue, ...] = ()
41
+ message: Text
42
+
43
+
44
+ class CheckResult(_ExplicitOutcome, FrozenModel):
45
+ _truth_value_guidance = (
46
+ "inspect .state and .complete explicitly; not_applicable is not approval"
47
+ )
48
+
49
+ check_id: Identifier
50
+ revision: PositiveInt | None = None
51
+ kind: Literal["precondition", "rule"]
52
+ implementation: Text | None = None
53
+ state: CheckState
54
+ complete: bool
55
+ required: bool | None = None
56
+ severity: Literal["info", "warning", "error"] | None = None
57
+ reason: Text | None = None
58
+ violation_count: NonNegativeInt = 0
59
+ examined_rows: NonNegativeInt = 0
60
+ examples: tuple[RuleFinding, ...] = ()
61
+ findings: ArtifactRef | None = None
62
+
63
+ @model_validator(mode="after")
64
+ def consistent(self) -> Self:
65
+ if self.kind == "rule" and (
66
+ self.revision is None or self.required is None or self.severity is None
67
+ ):
68
+ fail("rule_metadata", "rule requires revision, required and severity", "kind")
69
+ if self.kind == "precondition" and (self.required is not None or self.severity is not None):
70
+ fail("precondition_metadata", "precondition does not carry rule policy", "kind")
71
+ if self.state == "satisfied" and (not self.complete or self.violation_count):
72
+ fail("rule_state", "satisfied requires complete check without violations", "state")
73
+ if self.state == "violated" and not self.violation_count:
74
+ fail("rule_state", "violated requires observed findings", "state")
75
+ if self.state in ("not_applicable", "not_evaluable") and not self.reason:
76
+ fail("rule_state", "skipped or unavailable checks require a reason", "reason")
77
+ if self.state == "not_applicable" and (not self.complete or self.violation_count):
78
+ fail("rule_state", "not_applicable requires a verified reason", "state")
79
+ if self.state == "not_evaluable" and (self.complete or self.violation_count):
80
+ fail("rule_state", "not_evaluable cannot claim a completed check or findings", "state")
81
+ if len(self.examples) > self.violation_count:
82
+ fail("rule_examples", "examples exceed finding count", "examples")
83
+ if any(example.check_id != self.check_id for example in self.examples):
84
+ fail("rule_examples", "example belongs to another check", "examples")
85
+ if self.findings is not None and self.findings.format != "jsonl":
86
+ fail("rule_evidence", "findings must be JSONL", "findings")
87
+ return self
88
+
89
+
90
+ class RuleCoverage(FrozenModel):
91
+ declared: NonNegativeInt
92
+ checked: NonNegativeInt
93
+ skipped: NonNegativeInt
94
+ unavailable: NonNegativeInt
95
+ required_declared: NonNegativeInt
96
+ required_checked: NonNegativeInt
97
+ alerts: NonNegativeInt
98
+ finding_count: NonNegativeInt
99
+ preconditions_declared: NonNegativeInt
100
+ preconditions_verified: NonNegativeInt
101
+ preconditions_unavailable: NonNegativeInt
102
+ highest_alert_severity: Literal["info", "warning", "error"] | None = None
103
+
104
+
105
+ class RuleEvaluation(_ExplicitOutcome, FrozenModel):
106
+ _truth_value_guidance = (
107
+ "inspect .decision, .complete and .coverage explicitly; no_alert is not equivalence"
108
+ )
109
+
110
+ schema_version: Literal["1.0"] = "1.0"
111
+ evaluation_id: Identifier
112
+ run_id: Identifier
113
+ execution_id: Identifier
114
+ contract_id: Identifier
115
+ contract_revision: PositiveInt
116
+ contract_sha256: Sha256
117
+ rule_set_sha256: Sha256
118
+ execution_state: Text
119
+ options: RuleOptions
120
+ decision: DecisionState
121
+ complete: bool
122
+ preconditions: tuple[CheckResult, ...]
123
+ rules: tuple[CheckResult, ...]
124
+ coverage: RuleCoverage
125
+ started_at: Text
126
+ elapsed_seconds: Annotated[float, Field(ge=0)]
127
+ work_units: NonNegativeInt
128
+ diagnostic: Text | None = None
129
+
130
+ @model_validator(mode="after")
131
+ def consistent(self) -> Self:
132
+ if any(item.kind != "precondition" for item in self.preconditions):
133
+ fail("rule_evidence", "precondition result has wrong kind", "preconditions")
134
+ if any(item.kind != "rule" for item in self.rules):
135
+ fail("rule_evidence", "rule result has wrong kind", "rules")
136
+ checked = tuple(r for r in self.rules if r.state in ("satisfied", "violated"))
137
+ alerts = tuple(r for r in self.rules if r.state == "violated")
138
+ severities = {"info": 1, "warning": 2, "error": 3}
139
+ highest = max(
140
+ (r.severity for r in alerts if r.severity is not None),
141
+ key=severities.__getitem__,
142
+ default=None,
143
+ )
144
+ expected_coverage = RuleCoverage(
145
+ declared=len(self.rules),
146
+ checked=len(checked),
147
+ skipped=sum(r.state == "not_applicable" for r in self.rules),
148
+ unavailable=sum(r.state == "not_evaluable" for r in self.rules),
149
+ required_declared=sum(r.required is True for r in self.rules),
150
+ required_checked=sum(r.required is True for r in checked),
151
+ alerts=len(alerts),
152
+ finding_count=sum(r.violation_count for r in self.rules),
153
+ preconditions_declared=len(self.preconditions),
154
+ preconditions_verified=sum(r.complete for r in self.preconditions),
155
+ preconditions_unavailable=sum(not r.complete for r in self.preconditions),
156
+ highest_alert_severity=highest,
157
+ )
158
+ if self.coverage != expected_coverage:
159
+ fail("rule_coverage", "coverage does not match check results", "coverage")
160
+ expected_complete = (
161
+ bool(self.rules)
162
+ and self.diagnostic is None
163
+ and self.execution_state == "completed"
164
+ and all(item.complete for item in (*self.preconditions, *self.rules))
165
+ and any(item.state in ("satisfied", "violated") for item in self.rules)
166
+ )
167
+ if self.complete != expected_complete:
168
+ fail(
169
+ "rule_completeness",
170
+ "completeness does not match checked rules and execution",
171
+ "complete",
172
+ )
173
+ expected_decision: DecisionState = (
174
+ "alert" if alerts else "no_alert" if self.complete else "not_evaluable"
175
+ )
176
+ if self.decision != expected_decision:
177
+ fail("rule_decision", "decision does not match completed checks and alerts", "decision")
178
+ return self