qavf 0.4.0__tar.gz

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 (382) hide show
  1. qavf-0.4.0/.gitattributes +1 -0
  2. qavf-0.4.0/.github/workflows/ci.yml +61 -0
  3. qavf-0.4.0/.github/workflows/verify.yml +273 -0
  4. qavf-0.4.0/.gitignore +23 -0
  5. qavf-0.4.0/CHANGELOG.md +21 -0
  6. qavf-0.4.0/LICENSE +21 -0
  7. qavf-0.4.0/PKG-INFO +257 -0
  8. qavf-0.4.0/README.md +213 -0
  9. qavf-0.4.0/config/qavf.full.yaml +193 -0
  10. qavf-0.4.0/config/qavf.minimal.yaml +11 -0
  11. qavf-0.4.0/docs/api.md +216 -0
  12. qavf-0.4.0/docs/baseline_acceptance.md +50 -0
  13. qavf-0.4.0/docs/code_organization.md +119 -0
  14. qavf-0.4.0/docs/coherent_observations.md +117 -0
  15. qavf-0.4.0/docs/correctness_testing.md +75 -0
  16. qavf-0.4.0/docs/execution_limits.md +214 -0
  17. qavf-0.4.0/docs/full_data_loading.md +95 -0
  18. qavf-0.4.0/docs/integrations.md +82 -0
  19. qavf-0.4.0/docs/numerics.md +40 -0
  20. qavf-0.4.0/docs/performance_benchmarks.md +182 -0
  21. qavf-0.4.0/docs/plugins.md +250 -0
  22. qavf-0.4.0/docs/profiling.md +49 -0
  23. qavf-0.4.0/docs/regression_streaming.md +71 -0
  24. qavf-0.4.0/docs/release_assurance.md +164 -0
  25. qavf-0.4.0/docs/releasing.md +57 -0
  26. qavf-0.4.0/docs/resources.md +181 -0
  27. qavf-0.4.0/docs/rules.md +800 -0
  28. qavf-0.4.0/docs/scan_optimization.md +78 -0
  29. qavf-0.4.0/docs/sources.md +57 -0
  30. qavf-0.4.0/pyproject.toml +121 -0
  31. qavf-0.4.0/qavf/__init__.py +128 -0
  32. qavf-0.4.0/qavf/_audit_temporal.py +43 -0
  33. qavf-0.4.0/qavf/_float_accumulator.py +52 -0
  34. qavf-0.4.0/qavf/_observations.py +71 -0
  35. qavf-0.4.0/qavf/_version.py +3 -0
  36. qavf-0.4.0/qavf/aggregation.py +84 -0
  37. qavf-0.4.0/qavf/api.py +34 -0
  38. qavf-0.4.0/qavf/audit_codec.py +428 -0
  39. qavf-0.4.0/qavf/cli.py +429 -0
  40. qavf-0.4.0/qavf/demo.py +236 -0
  41. qavf-0.4.0/qavf/demo_config.yaml +155 -0
  42. qavf-0.4.0/qavf/docgen.py +191 -0
  43. qavf-0.4.0/qavf/engine/__init__.py +1 -0
  44. qavf-0.4.0/qavf/engine/cache_ownership.py +372 -0
  45. qavf-0.4.0/qavf/engine/capabilities.py +27 -0
  46. qavf-0.4.0/qavf/engine/catalog.py +285 -0
  47. qavf-0.4.0/qavf/engine/compilation.py +216 -0
  48. qavf-0.4.0/qavf/engine/connections.py +199 -0
  49. qavf-0.4.0/qavf/engine/context.py +1322 -0
  50. qavf-0.4.0/qavf/engine/observation.py +369 -0
  51. qavf-0.4.0/qavf/engine/observation_plan.py +204 -0
  52. qavf-0.4.0/qavf/engine/owned_ipc.py +31 -0
  53. qavf-0.4.0/qavf/engine/planning.py +135 -0
  54. qavf-0.4.0/qavf/engine/projection.py +76 -0
  55. qavf-0.4.0/qavf/engine/registry.py +493 -0
  56. qavf-0.4.0/qavf/engine/relations.py +27 -0
  57. qavf-0.4.0/qavf/engine/resources.py +425 -0
  58. qavf-0.4.0/qavf/engine/runner.py +912 -0
  59. qavf-0.4.0/qavf/engine/session.py +133 -0
  60. qavf-0.4.0/qavf/engine/severity.py +80 -0
  61. qavf-0.4.0/qavf/engine/source_read.py +148 -0
  62. qavf-0.4.0/qavf/engine/spill.py +129 -0
  63. qavf-0.4.0/qavf/engine/sql.py +172 -0
  64. qavf-0.4.0/qavf/engine/sql_read.py +227 -0
  65. qavf-0.4.0/qavf/engine/sqlite_store.py +128 -0
  66. qavf-0.4.0/qavf/engine/thread_budget.py +23 -0
  67. qavf-0.4.0/qavf/execution/__init__.py +0 -0
  68. qavf-0.4.0/qavf/execution/_windows_job.py +279 -0
  69. qavf-0.4.0/qavf/execution/bootstrap.py +61 -0
  70. qavf-0.4.0/qavf/execution/dispatch.py +46 -0
  71. qavf-0.4.0/qavf/execution/errors.py +11 -0
  72. qavf-0.4.0/qavf/execution/inputs.py +33 -0
  73. qavf-0.4.0/qavf/execution/os_limits.py +87 -0
  74. qavf-0.4.0/qavf/execution/policy.py +8 -0
  75. qavf-0.4.0/qavf/execution/protocol.py +86 -0
  76. qavf-0.4.0/qavf/execution/supervisor.py +434 -0
  77. qavf-0.4.0/qavf/execution/worker.py +99 -0
  78. qavf-0.4.0/qavf/inputs/__init__.py +4 -0
  79. qavf-0.4.0/qavf/inputs/adapters.py +136 -0
  80. qavf-0.4.0/qavf/inputs/descriptors.py +201 -0
  81. qavf-0.4.0/qavf/inputs/normalize.py +285 -0
  82. qavf-0.4.0/qavf/inputs/resources.py +120 -0
  83. qavf-0.4.0/qavf/io.py +109 -0
  84. qavf-0.4.0/qavf/key_codec.py +35 -0
  85. qavf-0.4.0/qavf/keys.py +112 -0
  86. qavf-0.4.0/qavf/numeric.py +150 -0
  87. qavf-0.4.0/qavf/ordering.py +59 -0
  88. qavf-0.4.0/qavf/output_format.py +35 -0
  89. qavf-0.4.0/qavf/profiling/__init__.py +108 -0
  90. qavf-0.4.0/qavf/profiling/accumulator.py +133 -0
  91. qavf-0.4.0/qavf/profiling/values.py +134 -0
  92. qavf-0.4.0/qavf/py.typed +0 -0
  93. qavf-0.4.0/qavf/redaction.py +221 -0
  94. qavf-0.4.0/qavf/regression/__init__.py +28 -0
  95. qavf-0.4.0/qavf/regression/_baseline_writer.py +176 -0
  96. qavf-0.4.0/qavf/regression/_cells.py +125 -0
  97. qavf-0.4.0/qavf/regression/_columns.py +10 -0
  98. qavf-0.4.0/qavf/regression/_comparison.py +270 -0
  99. qavf-0.4.0/qavf/regression/_evidence.py +167 -0
  100. qavf-0.4.0/qavf/regression/_indexed.py +205 -0
  101. qavf-0.4.0/qavf/regression/_inputs.py +146 -0
  102. qavf-0.4.0/qavf/regression/_numeric.py +96 -0
  103. qavf-0.4.0/qavf/regression/_plan.py +208 -0
  104. qavf-0.4.0/qavf/regression/_schema.py +77 -0
  105. qavf-0.4.0/qavf/regression/_streaming.py +221 -0
  106. qavf-0.4.0/qavf/regression/baselines.py +357 -0
  107. qavf-0.4.0/qavf/regression/compare_outputs.py +282 -0
  108. qavf-0.4.0/qavf/regression/models.py +163 -0
  109. qavf-0.4.0/qavf/reports/__init__.py +1 -0
  110. qavf-0.4.0/qavf/reports/charts.py +101 -0
  111. qavf-0.4.0/qavf/reports/html.py +287 -0
  112. qavf-0.4.0/qavf/reports/models.py +508 -0
  113. qavf-0.4.0/qavf/reports/presentation.py +117 -0
  114. qavf-0.4.0/qavf/reports/templates/records.html.j2 +12 -0
  115. qavf-0.4.0/qavf/reports/templates/report.html.j2 +288 -0
  116. qavf-0.4.0/qavf/rules/__init__.py +1 -0
  117. qavf-0.4.0/qavf/rules/_column_batch.py +235 -0
  118. qavf-0.4.0/qavf/rules/_column_plan.py +543 -0
  119. qavf-0.4.0/qavf/rules/_column_rows.py +131 -0
  120. qavf-0.4.0/qavf/rules/_column_stream.py +110 -0
  121. qavf-0.4.0/qavf/rules/_contracts.py +58 -0
  122. qavf-0.4.0/qavf/rules/_coordinate_sql.py +98 -0
  123. qavf-0.4.0/qavf/rules/_coverage.py +247 -0
  124. qavf-0.4.0/qavf/rules/_distribution.py +170 -0
  125. qavf-0.4.0/qavf/rules/_domains.py +77 -0
  126. qavf-0.4.0/qavf/rules/_exact_grouped.py +196 -0
  127. qavf-0.4.0/qavf/rules/_execution.py +334 -0
  128. qavf-0.4.0/qavf/rules/_grid.py +210 -0
  129. qavf-0.4.0/qavf/rules/_grouped.py +338 -0
  130. qavf-0.4.0/qavf/rules/_key_summary.py +131 -0
  131. qavf-0.4.0/qavf/rules/_ordered_grouped.py +107 -0
  132. qavf-0.4.0/qavf/rules/_outlier_batches.py +298 -0
  133. qavf-0.4.0/qavf/rules/_predicates.py +378 -0
  134. qavf-0.4.0/qavf/rules/_regex.py +32 -0
  135. qavf-0.4.0/qavf/rules/_row_summary.py +97 -0
  136. qavf-0.4.0/qavf/rules/_spatial_aggregation.py +120 -0
  137. qavf-0.4.0/qavf/rules/_statistics_plan.py +69 -0
  138. qavf-0.4.0/qavf/rules/_temporal.py +82 -0
  139. qavf-0.4.0/qavf/rules/freshness_checks.py +205 -0
  140. qavf-0.4.0/qavf/rules/geo_checks.py +244 -0
  141. qavf-0.4.0/qavf/rules/helpers.py +468 -0
  142. qavf-0.4.0/qavf/rules/input_checks/__init__.py +23 -0
  143. qavf-0.4.0/qavf/rules/input_checks/_column_eager.py +347 -0
  144. qavf-0.4.0/qavf/rules/input_checks/_column_simple.py +326 -0
  145. qavf-0.4.0/qavf/rules/input_checks/_column_values.py +192 -0
  146. qavf-0.4.0/qavf/rules/input_checks/_datasets.py +107 -0
  147. qavf-0.4.0/qavf/rules/input_checks/availability.py +192 -0
  148. qavf-0.4.0/qavf/rules/input_checks/columns.py +160 -0
  149. qavf-0.4.0/qavf/rules/input_checks/expressions.py +184 -0
  150. qavf-0.4.0/qavf/rules/input_checks/keys.py +260 -0
  151. qavf-0.4.0/qavf/rules/input_checks/references.py +432 -0
  152. qavf-0.4.0/qavf/rules/reconciliation_checks.py +1020 -0
  153. qavf-0.4.0/qavf/rules/regression_checks.py +250 -0
  154. qavf-0.4.0/qavf/rules/spatial_checks.py +881 -0
  155. qavf-0.4.0/qavf/rules/statistical_checks.py +411 -0
  156. qavf-0.4.0/qavf/rules/temporal_checks.py +1654 -0
  157. qavf-0.4.0/qavf/scaffold.py +241 -0
  158. qavf-0.4.0/qavf/settings.py +1046 -0
  159. qavf-0.4.0/qavf/sources/__init__.py +41 -0
  160. qavf-0.4.0/qavf/sources/base.py +424 -0
  161. qavf-0.4.0/qavf/sources/builtin.py +17 -0
  162. qavf-0.4.0/qavf/sources/collection.py +18 -0
  163. qavf-0.4.0/qavf/sources/errors.py +61 -0
  164. qavf-0.4.0/qavf/sources/execution.py +104 -0
  165. qavf-0.4.0/qavf/sources/file.py +483 -0
  166. qavf-0.4.0/qavf/sources/formats.py +111 -0
  167. qavf-0.4.0/qavf/sources/frames.py +44 -0
  168. qavf-0.4.0/qavf/sources/identity.py +89 -0
  169. qavf-0.4.0/qavf/sources/memory.py +48 -0
  170. qavf-0.4.0/qavf/sources/native.py +165 -0
  171. qavf-0.4.0/qavf/sources/parameters.py +152 -0
  172. qavf-0.4.0/qavf/sources/python.py +52 -0
  173. qavf-0.4.0/qavf/sources/query.py +108 -0
  174. qavf-0.4.0/qavf/sources/sql.py +452 -0
  175. qavf-0.4.0/qavf/sources/typed.py +40 -0
  176. qavf-0.4.0/qavf/statistics/__init__.py +1 -0
  177. qavf-0.4.0/qavf/statistics/ordered.py +189 -0
  178. qavf-0.4.0/qavf/stats.py +600 -0
  179. qavf-0.4.0/qavf/testing.py +115 -0
  180. qavf-0.4.0/qavf/typed_values.py +64 -0
  181. qavf-0.4.0/scripts/benchmark_performance.py +12 -0
  182. qavf-0.4.0/scripts/benchmark_scan_work.py +360 -0
  183. qavf-0.4.0/scripts/generate_docs.py +50 -0
  184. qavf-0.4.0/scripts/peak_rss.py +51 -0
  185. qavf-0.4.0/scripts/performance/__init__.py +1 -0
  186. qavf-0.4.0/scripts/performance/cache.py +258 -0
  187. qavf-0.4.0/scripts/performance/capacity.py +152 -0
  188. qavf-0.4.0/scripts/performance/catalog.py +78 -0
  189. qavf-0.4.0/scripts/performance/cli.py +146 -0
  190. qavf-0.4.0/scripts/performance/fixtures.py +302 -0
  191. qavf-0.4.0/scripts/performance/metrics.py +360 -0
  192. qavf-0.4.0/scripts/performance/models.py +44 -0
  193. qavf-0.4.0/scripts/performance/planning.py +92 -0
  194. qavf-0.4.0/scripts/performance/policy.py +22 -0
  195. qavf-0.4.0/scripts/performance/process_tree.py +89 -0
  196. qavf-0.4.0/scripts/performance/provenance.py +118 -0
  197. qavf-0.4.0/scripts/performance/regression.py +266 -0
  198. qavf-0.4.0/scripts/performance/reporting.py +482 -0
  199. qavf-0.4.0/scripts/performance/runner.py +171 -0
  200. qavf-0.4.0/scripts/performance/storage.py +50 -0
  201. qavf-0.4.0/scripts/performance/streaming.py +133 -0
  202. qavf-0.4.0/scripts/performance/suite.py +213 -0
  203. qavf-0.4.0/scripts/performance/worker.py +118 -0
  204. qavf-0.4.0/scripts/performance/workloads.py +383 -0
  205. qavf-0.4.0/scripts/release_checks/__init__.py +1 -0
  206. qavf-0.4.0/scripts/release_checks/coverage.py +57 -0
  207. qavf-0.4.0/scripts/release_checks/extensions.py +69 -0
  208. qavf-0.4.0/scripts/release_checks/gate.py +173 -0
  209. qavf-0.4.0/scripts/release_checks/policy.py +95 -0
  210. qavf-0.4.0/scripts/release_checks/provenance.py +139 -0
  211. qavf-0.4.0/scripts/release_checks/pytest_plugin.py +84 -0
  212. qavf-0.4.0/scripts/release_checks/runner.py +112 -0
  213. qavf-0.4.0/scripts/release_checks/validation.py +69 -0
  214. qavf-0.4.0/scripts/release_checks/wheel.py +192 -0
  215. qavf-0.4.0/scripts/verify_core.py +80 -0
  216. qavf-0.4.0/scripts/verify_dist.py +92 -0
  217. qavf-0.4.0/scripts/verify_release.py +69 -0
  218. qavf-0.4.0/tests/assurance/__init__.py +5 -0
  219. qavf-0.4.0/tests/assurance/conftest.py +12 -0
  220. qavf-0.4.0/tests/assurance/support/__init__.py +1 -0
  221. qavf-0.4.0/tests/assurance/support/adapters.py +63 -0
  222. qavf-0.4.0/tests/assurance/support/contracts.py +54 -0
  223. qavf-0.4.0/tests/assurance/support/oracles.py +39 -0
  224. qavf-0.4.0/tests/assurance/support/strategies.py +16 -0
  225. qavf-0.4.0/tests/assurance/test_audit_values.py +105 -0
  226. qavf-0.4.0/tests/assurance/test_cache_labels.py +45 -0
  227. qavf-0.4.0/tests/assurance/test_execution_dispatch.py +92 -0
  228. qavf-0.4.0/tests/assurance/test_failures.py +223 -0
  229. qavf-0.4.0/tests/assurance/test_keys.py +184 -0
  230. qavf-0.4.0/tests/assurance/test_missingness.py +101 -0
  231. qavf-0.4.0/tests/assurance/test_precision.py +167 -0
  232. qavf-0.4.0/tests/assurance/test_regression.py +187 -0
  233. qavf-0.4.0/tests/assurance/test_sources.py +71 -0
  234. qavf-0.4.0/tests/assurance/test_temporal.py +184 -0
  235. qavf-0.4.0/tests/baselines/demo_counts_by_year.csv +10 -0
  236. qavf-0.4.0/tests/conftest.py +39 -0
  237. qavf-0.4.0/tests/dependency-floors.txt +12 -0
  238. qavf-0.4.0/tests/execution_support/__init__.py +1 -0
  239. qavf-0.4.0/tests/execution_support/driver.py +51 -0
  240. qavf-0.4.0/tests/execution_support/probes.py +107 -0
  241. qavf-0.4.0/tests/execution_support/processes.py +61 -0
  242. qavf-0.4.0/tests/integration/__init__.py +1 -0
  243. qavf-0.4.0/tests/integration/cleanup.py +26 -0
  244. qavf-0.4.0/tests/integration/conftest.py +17 -0
  245. qavf-0.4.0/tests/integration/contracts.py +82 -0
  246. qavf-0.4.0/tests/integration/http_service.py +78 -0
  247. qavf-0.4.0/tests/integration/policy.py +67 -0
  248. qavf-0.4.0/tests/integration/postgres_service.py +75 -0
  249. qavf-0.4.0/tests/integration/pytest_support.py +18 -0
  250. qavf-0.4.0/tests/integration/s3_service.py +123 -0
  251. qavf-0.4.0/tests/integration/test_http.py +48 -0
  252. qavf-0.4.0/tests/integration/test_policy.py +153 -0
  253. qavf-0.4.0/tests/integration/test_postgres.py +62 -0
  254. qavf-0.4.0/tests/integration/test_s3.py +52 -0
  255. qavf-0.4.0/tests/test_acceptance.py +365 -0
  256. qavf-0.4.0/tests/test_adapter_budgets.py +96 -0
  257. qavf-0.4.0/tests/test_aggregation.py +229 -0
  258. qavf-0.4.0/tests/test_api_equivalence.py +356 -0
  259. qavf-0.4.0/tests/test_api_inputs.py +238 -0
  260. qavf-0.4.0/tests/test_arrow_pandas_cache.py +140 -0
  261. qavf-0.4.0/tests/test_baseline_manifest.py +126 -0
  262. qavf-0.4.0/tests/test_baseline_staging.py +128 -0
  263. qavf-0.4.0/tests/test_cache_ownership.py +311 -0
  264. qavf-0.4.0/tests/test_column_check_plans.py +165 -0
  265. qavf-0.4.0/tests/test_concurrent_loads.py +301 -0
  266. qavf-0.4.0/tests/test_dataset_catalog.py +251 -0
  267. qavf-0.4.0/tests/test_dataset_lifetimes.py +129 -0
  268. qavf-0.4.0/tests/test_discovery.py +408 -0
  269. qavf-0.4.0/tests/test_docgen_cells.py +22 -0
  270. qavf-0.4.0/tests/test_documentation_source.py +44 -0
  271. qavf-0.4.0/tests/test_dst_coverage.py +63 -0
  272. qavf-0.4.0/tests/test_engine.py +289 -0
  273. qavf-0.4.0/tests/test_engine_locking.py +382 -0
  274. qavf-0.4.0/tests/test_engine_ownership.py +421 -0
  275. qavf-0.4.0/tests/test_engine_resources.py +373 -0
  276. qavf-0.4.0/tests/test_evidence_sampling.py +195 -0
  277. qavf-0.4.0/tests/test_exact_grouped.py +225 -0
  278. qavf-0.4.0/tests/test_examples.py +113 -0
  279. qavf-0.4.0/tests/test_execution_isolation.py +397 -0
  280. qavf-0.4.0/tests/test_execution_limits.py +207 -0
  281. qavf-0.4.0/tests/test_execution_policy.py +171 -0
  282. qavf-0.4.0/tests/test_expected_scope.py +201 -0
  283. qavf-0.4.0/tests/test_false_pass_regressions.py +375 -0
  284. qavf-0.4.0/tests/test_geo_and_drift.py +133 -0
  285. qavf-0.4.0/tests/test_indexed_statistics.py +226 -0
  286. qavf-0.4.0/tests/test_input_backends.py +404 -0
  287. qavf-0.4.0/tests/test_input_boundaries.py +236 -0
  288. qavf-0.4.0/tests/test_input_checks.py +343 -0
  289. qavf-0.4.0/tests/test_input_exactness.py +304 -0
  290. qavf-0.4.0/tests/test_input_facade.py +160 -0
  291. qavf-0.4.0/tests/test_input_identity.py +254 -0
  292. qavf-0.4.0/tests/test_input_package_contracts.py +117 -0
  293. qavf-0.4.0/tests/test_input_path_identity.py +96 -0
  294. qavf-0.4.0/tests/test_input_semantics.py +193 -0
  295. qavf-0.4.0/tests/test_isolated_supervisor.py +378 -0
  296. qavf-0.4.0/tests/test_isolation_transport.py +115 -0
  297. qavf-0.4.0/tests/test_key_joins.py +243 -0
  298. qavf-0.4.0/tests/test_limits_native_budgets.py +118 -0
  299. qavf-0.4.0/tests/test_loading_comparison_plan.py +227 -0
  300. qavf-0.4.0/tests/test_loading_contracts.py +322 -0
  301. qavf-0.4.0/tests/test_loading_group_contracts.py +135 -0
  302. qavf-0.4.0/tests/test_loading_key_coherence.py +146 -0
  303. qavf-0.4.0/tests/test_loading_native_binding.py +252 -0
  304. qavf-0.4.0/tests/test_loading_reads.py +427 -0
  305. qavf-0.4.0/tests/test_loading_reductions.py +207 -0
  306. qavf-0.4.0/tests/test_loading_regression_inputs.py +325 -0
  307. qavf-0.4.0/tests/test_loading_regression_payload_plan.py +63 -0
  308. qavf-0.4.0/tests/test_loading_regression_payloads.py +283 -0
  309. qavf-0.4.0/tests/test_loading_regression_stream.py +342 -0
  310. qavf-0.4.0/tests/test_loading_row_reductions.py +222 -0
  311. qavf-0.4.0/tests/test_loading_scalar_statistics.py +187 -0
  312. qavf-0.4.0/tests/test_loading_spill_reductions.py +122 -0
  313. qavf-0.4.0/tests/test_membership.py +267 -0
  314. qavf-0.4.0/tests/test_missing_values.py +250 -0
  315. qavf-0.4.0/tests/test_modular_connections.py +95 -0
  316. qavf-0.4.0/tests/test_modular_numeric_keys.py +100 -0
  317. qavf-0.4.0/tests/test_modular_rule_contracts.py +117 -0
  318. qavf-0.4.0/tests/test_multi_file_schemas.py +157 -0
  319. qavf-0.4.0/tests/test_native_resources.py +193 -0
  320. qavf-0.4.0/tests/test_numeric_admission.py +115 -0
  321. qavf-0.4.0/tests/test_numerical_oracles.py +154 -0
  322. qavf-0.4.0/tests/test_numerical_verdicts.py +243 -0
  323. qavf-0.4.0/tests/test_observation_plan.py +136 -0
  324. qavf-0.4.0/tests/test_ordered_grouped.py +169 -0
  325. qavf-0.4.0/tests/test_organization_sources.py +132 -0
  326. qavf-0.4.0/tests/test_outlier_edge_cases.py +282 -0
  327. qavf-0.4.0/tests/test_packaging_and_connections.py +280 -0
  328. qavf-0.4.0/tests/test_performance_capacity.py +53 -0
  329. qavf-0.4.0/tests/test_performance_metrics.py +194 -0
  330. qavf-0.4.0/tests/test_performance_process_tree.py +75 -0
  331. qavf-0.4.0/tests/test_performance_protocol.py +308 -0
  332. qavf-0.4.0/tests/test_performance_regression_cache.py +236 -0
  333. qavf-0.4.0/tests/test_performance_reporting.py +252 -0
  334. qavf-0.4.0/tests/test_performance_runner.py +213 -0
  335. qavf-0.4.0/tests/test_performance_workloads.py +243 -0
  336. qavf-0.4.0/tests/test_plugins_and_globs.py +189 -0
  337. qavf-0.4.0/tests/test_primary_keys.py +222 -0
  338. qavf-0.4.0/tests/test_profile_datetime_bounds.py +36 -0
  339. qavf-0.4.0/tests/test_public_contracts.py +147 -0
  340. qavf-0.4.0/tests/test_reconciliation.py +208 -0
  341. qavf-0.4.0/tests/test_reconciliation_aliases.py +76 -0
  342. qavf-0.4.0/tests/test_redaction.py +327 -0
  343. qavf-0.4.0/tests/test_regression.py +169 -0
  344. qavf-0.4.0/tests/test_regression_acceptance.py +334 -0
  345. qavf-0.4.0/tests/test_regression_counts.py +139 -0
  346. qavf-0.4.0/tests/test_regression_exactness.py +305 -0
  347. qavf-0.4.0/tests/test_regression_reporting.py +213 -0
  348. qavf-0.4.0/tests/test_release_artifacts.py +91 -0
  349. qavf-0.4.0/tests/test_release_checks.py +380 -0
  350. qavf-0.4.0/tests/test_report_encoding.py +114 -0
  351. qavf-0.4.0/tests/test_report_evidence.py +365 -0
  352. qavf-0.4.0/tests/test_reports_cli.py +242 -0
  353. qavf-0.4.0/tests/test_representation_parity.py +72 -0
  354. qavf-0.4.0/tests/test_rule_compilation.py +163 -0
  355. qavf-0.4.0/tests/test_rule_observations.py +121 -0
  356. qavf-0.4.0/tests/test_scaffold.py +223 -0
  357. qavf-0.4.0/tests/test_scale_invariance.py +271 -0
  358. qavf-0.4.0/tests/test_scan_column_batches.py +262 -0
  359. qavf-0.4.0/tests/test_scan_identity.py +457 -0
  360. qavf-0.4.0/tests/test_scan_key_rules.py +231 -0
  361. qavf-0.4.0/tests/test_scan_key_summaries.py +150 -0
  362. qavf-0.4.0/tests/test_schema_and_profiles.py +238 -0
  363. qavf-0.4.0/tests/test_schema_changes.py +171 -0
  364. qavf-0.4.0/tests/test_settings_validation.py +117 -0
  365. qavf-0.4.0/tests/test_shared_statistics.py +242 -0
  366. qavf-0.4.0/tests/test_snapshots.py +148 -0
  367. qavf-0.4.0/tests/test_source_boundaries.py +342 -0
  368. qavf-0.4.0/tests/test_source_expressions.py +349 -0
  369. qavf-0.4.0/tests/test_source_policy.py +333 -0
  370. qavf-0.4.0/tests/test_sources.py +262 -0
  371. qavf-0.4.0/tests/test_stale_values.py +91 -0
  372. qavf-0.4.0/tests/test_statistical_invariants.py +250 -0
  373. qavf-0.4.0/tests/test_statistical_rules.py +459 -0
  374. qavf-0.4.0/tests/test_stats.py +106 -0
  375. qavf-0.4.0/tests/test_temporal_identity.py +358 -0
  376. qavf-0.4.0/tests/test_temporal_precision.py +256 -0
  377. qavf-0.4.0/tests/test_temporal_spatial_checks.py +240 -0
  378. qavf-0.4.0/tests/test_time_series_models.py +97 -0
  379. qavf-0.4.0/tests/test_typed_limits.py +302 -0
  380. qavf-0.4.0/tests/test_units.py +85 -0
  381. qavf-0.4.0/tests/test_writers_and_readme.py +318 -0
  382. qavf-0.4.0/uv.lock +1721 -0
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,61 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ paths-ignore: ["**.md", "docs/**", "LICENSE"]
6
+ pull_request:
7
+ paths-ignore: ["**.md", "docs/**", "LICENSE"]
8
+ workflow_dispatch:
9
+ permissions:
10
+ contents: read
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ env:
16
+ UV_VERSION: "0.12.23"
17
+
18
+ jobs:
19
+ test:
20
+ name: Lint, types and tests (Linux)
21
+ runs-on: ubuntu-latest
22
+ timeout-minutes: 30
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: astral-sh/setup-uv@v6
26
+ with:
27
+ version: ${{ env.UV_VERSION }}
28
+ python-version: "3.12"
29
+ enable-cache: true
30
+ - run: uv sync --all-extras --locked
31
+ - run: uv run --no-sync ruff check .
32
+ - run: uv run --no-sync mypy qavf scripts
33
+ - run: uv run --no-sync python scripts/generate_docs.py --check
34
+ - run: uv run --no-sync python -m pytest -q -p no:cacheprovider --ignore=tests/integration
35
+
36
+ platforms:
37
+ name: Platform-specific tests (${{ matrix.os }})
38
+ runs-on: ${{ matrix.os }}
39
+ timeout-minutes: 20
40
+ strategy:
41
+ fail-fast: false
42
+ matrix:
43
+ os: [windows-latest, macos-latest]
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+ - uses: astral-sh/setup-uv@v6
47
+ with:
48
+ version: ${{ env.UV_VERSION }}
49
+ python-version: "3.12"
50
+ enable-cache: true
51
+ - run: uv sync --all-extras --locked
52
+ # Code paths that differ by operating system: isolation, locking, numerics,
53
+ # file identity and benchmark measurement. The full matrix runs on release tags.
54
+ - run: >-
55
+ uv run --no-sync python -m pytest -q -p no:cacheprovider
56
+ tests/test_isolated_supervisor.py tests/test_execution_isolation.py
57
+ tests/test_execution_policy.py tests/test_isolation_transport.py
58
+ tests/test_regression_exactness.py tests/test_statistical_rules.py
59
+ tests/test_scan_identity.py tests/test_loading_native_binding.py
60
+ tests/test_performance_metrics.py tests/test_performance_runner.py
61
+ tests/test_performance_process_tree.py
@@ -0,0 +1,273 @@
1
+ name: Release verification
2
+ # Full platform, dependency-floor and live-service matrix; run for release tags only.
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+ workflow_dispatch:
7
+ permissions:
8
+ contents: read
9
+
10
+ env:
11
+ # Keep service credentials synthetic and local to the disposable CI runner.
12
+ UV_VERSION: "0.12.23"
13
+
14
+ jobs:
15
+ test:
16
+ runs-on: ubuntu-latest
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ python: ["3.12", "3.13", "3.14"]
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: astral-sh/setup-uv@v6
24
+ with:
25
+ version: ${{ env.UV_VERSION }}
26
+ python-version: ${{ matrix.python }}
27
+ - run: uv sync --all-extras --locked
28
+ - run: uv run --no-sync python scripts/verify_release.py extensions
29
+ - run: uv run --no-sync python scripts/verify_release.py test ${{ matrix.python == '3.12' && '--coverage' || '' }} --id linux-${{ matrix.python }}-source --output evidence/linux-${{ matrix.python }}-source.json
30
+ - if: ${{ !cancelled() }}
31
+ run: uv run --no-sync python scripts/verify_release.py test --optimized --id linux-${{ matrix.python }}-source-optimized --output evidence/linux-${{ matrix.python }}-source-optimized.json
32
+ - run: uv run --no-sync ruff check .
33
+ - run: uv run --no-sync mypy qavf scripts
34
+ - run: uv run --no-sync python scripts/generate_docs.py --check
35
+ - run: uv build --clear
36
+ - run: uv run --no-sync python scripts/verify_dist.py
37
+ - run: uv run --no-sync python scripts/verify_release.py installed --id linux-${{ matrix.python }}-wheel --output-dir evidence
38
+ - uses: actions/upload-artifact@v4
39
+ if: always()
40
+ with:
41
+ name: evidence-linux-${{ matrix.python }}
42
+ path: evidence/*.json
43
+ if-no-files-found: error
44
+ - uses: actions/upload-artifact@v4
45
+ with:
46
+ name: qavf-distributions-${{ matrix.python }}
47
+ path: dist/*
48
+ if-no-files-found: error
49
+
50
+ dependency-floors:
51
+ runs-on: ubuntu-latest
52
+ steps:
53
+ - uses: actions/checkout@v4
54
+ - uses: astral-sh/setup-uv@v6
55
+ with:
56
+ version: ${{ env.UV_VERSION }}
57
+ python-version: "3.12"
58
+ - run: uv sync --all-extras --locked
59
+ - run: uv build --clear
60
+ - run: uv run --no-sync python scripts/verify_dist.py
61
+ - run: uv run --no-sync python scripts/verify_release.py installed --mode floors --id floors --output-dir evidence
62
+ - uses: actions/upload-artifact@v4
63
+ if: always()
64
+ with:
65
+ name: evidence-floors
66
+ path: evidence/*.json
67
+ if-no-files-found: error
68
+
69
+ platform-contracts:
70
+ runs-on: ${{ matrix.os }}
71
+ strategy:
72
+ fail-fast: false
73
+ matrix:
74
+ include:
75
+ - os: windows-latest
76
+ label: windows
77
+ - os: macos-latest
78
+ label: macos
79
+ steps:
80
+ - uses: actions/checkout@v4
81
+ - uses: astral-sh/setup-uv@v6
82
+ with:
83
+ version: ${{ env.UV_VERSION }}
84
+ python-version: "3.12"
85
+ - run: uv sync --all-extras --locked
86
+ - run: uv run --no-sync python scripts/verify_release.py extensions
87
+ - run: uv run --no-sync python scripts/verify_release.py test --id ${{ matrix.label }}-3.12-source --output evidence/${{ matrix.label }}-3.12-source.json
88
+ - if: ${{ !cancelled() }}
89
+ run: uv run --no-sync python scripts/verify_release.py test --optimized --id ${{ matrix.label }}-3.12-source-optimized --output evidence/${{ matrix.label }}-3.12-source-optimized.json
90
+ - run: uv build --clear
91
+ - run: uv run --no-sync python scripts/verify_dist.py
92
+ - run: uv run --no-sync python scripts/verify_release.py installed --id ${{ matrix.label }}-3.12-wheel --output-dir evidence
93
+ - uses: actions/upload-artifact@v4
94
+ if: always()
95
+ with:
96
+ name: evidence-${{ matrix.label }}-3.12
97
+ path: evidence/*.json
98
+ if-no-files-found: error
99
+
100
+ core-installation:
101
+ runs-on: ubuntu-latest
102
+ steps:
103
+ - uses: actions/checkout@v4
104
+ - uses: astral-sh/setup-uv@v6
105
+ with:
106
+ version: ${{ env.UV_VERSION }}
107
+ python-version: "3.12"
108
+ - run: uv sync --all-extras --locked
109
+ - run: uv build --clear
110
+ - run: uv run --no-sync python scripts/verify_dist.py
111
+ - run: uv run --no-sync python scripts/verify_release.py installed --mode core --id core --output-dir evidence
112
+ - uses: actions/upload-artifact@v4
113
+ if: always()
114
+ with:
115
+ name: evidence-core
116
+ path: evidence/*.json
117
+ if-no-files-found: error
118
+
119
+ integrations:
120
+ runs-on: ubuntu-latest
121
+ services:
122
+ postgres:
123
+ image: postgres:16.10
124
+ env:
125
+ POSTGRES_USER: qavf
126
+ POSTGRES_PASSWORD: qavf
127
+ POSTGRES_DB: qavf
128
+ ports: ["5432:5432"]
129
+ options: >-
130
+ --health-cmd pg_isready
131
+ --health-interval 5s
132
+ --health-timeout 5s
133
+ --health-retries 12
134
+ env:
135
+ QAVF_POSTGRES_DSN: "host=127.0.0.1 port=5432 dbname=qavf user=qavf password=qavf connect_timeout=5"
136
+ QAVF_S3_ENDPOINT: http://127.0.0.1:9000
137
+ QAVF_S3_ACCESS_KEY: qavf-integration
138
+ QAVF_S3_SECRET_KEY: local-integration-secret
139
+ steps:
140
+ - uses: actions/checkout@v4
141
+ - uses: astral-sh/setup-uv@v6
142
+ with:
143
+ version: ${{ env.UV_VERSION }}
144
+ python-version: "3.12"
145
+ - run: uv sync --all-extras --group integration --locked
146
+ - run: uv run --no-sync python scripts/verify_release.py extensions --integration
147
+ # moto's standalone server is a disposable S3-compatible endpoint; it accepts
148
+ # the synthetic credentials and needs no container registry.
149
+ - name: Start disposable S3-compatible object store
150
+ run: |
151
+ nohup uvx --from "moto[server]==5.2.3" moto_server -H 127.0.0.1 -p 9000 > moto.log 2>&1 &
152
+ echo $! > moto.pid
153
+ - name: Wait for object store readiness
154
+ run: curl --fail --silent --retry 20 --retry-all-errors --retry-delay 2 -o /dev/null http://127.0.0.1:9000/moto-api/
155
+ - name: Run required live integrations
156
+ run: uv run --no-sync python scripts/verify_release.py test --integration --id integrations --output evidence/integrations.json tests/integration
157
+ - uses: actions/upload-artifact@v4
158
+ if: always()
159
+ with:
160
+ name: evidence-integrations
161
+ path: evidence/*.json
162
+ if-no-files-found: error
163
+ - name: Stop disposable object store
164
+ if: always()
165
+ run: kill "$(cat moto.pid)" 2>/dev/null || true
166
+
167
+ release-assurance:
168
+ name: Required release assurance
169
+ runs-on: ubuntu-latest
170
+ needs: [test, dependency-floors, platform-contracts, core-installation, integrations]
171
+ if: always()
172
+ steps:
173
+ - uses: actions/checkout@v4
174
+ - uses: astral-sh/setup-uv@v6
175
+ with:
176
+ version: ${{ env.UV_VERSION }}
177
+ python-version: "3.12"
178
+ - uses: actions/download-artifact@v4
179
+ with:
180
+ pattern: evidence-*
181
+ path: collected-evidence
182
+ - name: Reject missing, stale, failed, skipped, or incomplete release jobs
183
+ env:
184
+ QAVF_CI_JOBS: ${{ toJSON(needs) }}
185
+ run: uv run --no-project python scripts/verify_release.py aggregate --evidence-dir collected-evidence --output release-assurance.json
186
+ - uses: actions/upload-artifact@v4
187
+ if: always()
188
+ with:
189
+ name: release-assurance
190
+ path: release-assurance.json
191
+ if-no-files-found: error
192
+
193
+ # Publishing runs only for v* tags, after every required job and the strict
194
+ # aggregate passed. It ships the exact distributions those jobs verified.
195
+ publish-pypi:
196
+ name: Publish to PyPI
197
+ if: startsWith(github.ref, 'refs/tags/v')
198
+ needs: release-assurance
199
+ runs-on: ubuntu-latest
200
+ environment:
201
+ name: pypi
202
+ url: https://pypi.org/project/qavf/
203
+ permissions:
204
+ id-token: write
205
+ steps:
206
+ - uses: actions/download-artifact@v4
207
+ with:
208
+ name: qavf-distributions-3.12
209
+ path: dist
210
+ - name: Require the tag to match the verified package version
211
+ env:
212
+ TAG: ${{ github.ref_name }}
213
+ run: |
214
+ ls -l dist
215
+ test -f "dist/qavf-${TAG#v}-py3-none-any.whl"
216
+ test -f "dist/qavf-${TAG#v}.tar.gz"
217
+ - uses: pypa/gh-action-pypi-publish@release/v1
218
+
219
+ github-release:
220
+ name: GitHub release
221
+ needs: publish-pypi
222
+ runs-on: ubuntu-latest
223
+ permissions:
224
+ contents: write
225
+ steps:
226
+ - uses: actions/download-artifact@v4
227
+ with:
228
+ name: qavf-distributions-3.12
229
+ path: dist
230
+ - name: Create the release with distributions and SHA-256 hashes
231
+ env:
232
+ GH_TOKEN: ${{ github.token }}
233
+ TAG: ${{ github.ref_name }}
234
+ run: |
235
+ (cd dist && sha256sum -- * > SHA256SUMS && cat SHA256SUMS)
236
+ gh release create "$TAG" dist/* --repo "$GITHUB_REPOSITORY" \
237
+ --title "QAVF ${TAG#v}" --generate-notes --verify-tag
238
+
239
+ container:
240
+ name: Container image
241
+ needs: publish-pypi
242
+ runs-on: ubuntu-latest
243
+ permissions:
244
+ contents: read
245
+ packages: write
246
+ steps:
247
+ - uses: actions/checkout@v4
248
+ - uses: actions/download-artifact@v4
249
+ with:
250
+ name: qavf-distributions-3.12
251
+ path: dist
252
+ - uses: docker/setup-qemu-action@v3
253
+ - uses: docker/setup-buildx-action@v3
254
+ - uses: docker/login-action@v3
255
+ with:
256
+ registry: ghcr.io
257
+ username: ${{ github.actor }}
258
+ password: ${{ github.token }}
259
+ - id: meta
260
+ uses: docker/metadata-action@v5
261
+ with:
262
+ images: ghcr.io/ahmedaredah/qavf
263
+ tags: |
264
+ type=semver,pattern={{version}}
265
+ type=semver,pattern={{major}}.{{minor}}
266
+ type=raw,value=latest
267
+ - uses: docker/build-push-action@v6
268
+ with:
269
+ context: .
270
+ platforms: linux/amd64,linux/arm64
271
+ push: true
272
+ tags: ${{ steps.meta.outputs.tags }}
273
+ labels: ${{ steps.meta.outputs.labels }}
qavf-0.4.0/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # QAVF generated artefacts
13
+ qa_output/
14
+ examples/data/
15
+ examples/baseline/
16
+ .coverage
17
+ htmlcov/
18
+ .pytest_cache/
19
+ .ruff_cache/
20
+ .mypy_cache/
21
+ .hypothesis/
22
+ .coverage.*
23
+ release-evidence/
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.4.0] - 2026-10-18
8
+
9
+ First public release.
10
+
11
+ - Validation API (`validate`, `QARunner`) and CLI for files, globs, URLs, databases,
12
+ SQL queries, Polars/pandas/Arrow frames and Python loaders.
13
+ - 28 built-in rules: input contracts, column limits, keys and foreign keys, row
14
+ expressions and SQL assertions, freshness, temporal and spatial checks, outlier and
15
+ drift detection, reconciliation and regression against accepted baselines.
16
+ - Fail-closed assessment: checks that cannot be assessed make a run incomplete rather
17
+ than passing, with exact counts and bounded, deterministic evidence.
18
+ - HTML and JSON reports with full run provenance and redacted credentials.
19
+ - Resource budgets for memory, spill storage, evidence and report size, and optional
20
+ isolated execution with OS-enforced limits on Linux, macOS and Windows.
21
+ - Plugin discovery for rules and sources from packages, files and entry points.
qavf-0.4.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ahmed Aredah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
qavf-0.4.0/PKG-INFO ADDED
@@ -0,0 +1,257 @@
1
+ Metadata-Version: 2.5
2
+ Name: qavf
3
+ Version: 0.4.0
4
+ Summary: Configurable, plugin-based data QA/QC: column limits, SQL checks, temporal and spatial outliers, regression testing and HTML reports, over any source DuckDB can read.
5
+ Project-URL: Homepage, https://github.com/AhmedAredah/qavf
6
+ Project-URL: Documentation, https://github.com/AhmedAredah/qavf/tree/main/docs
7
+ Project-URL: Issues, https://github.com/AhmedAredah/qavf/issues
8
+ Project-URL: Changelog, https://github.com/AhmedAredah/qavf/blob/main/CHANGELOG.md
9
+ Author-email: Ahmed Aredah <Ahmed.Aredah@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: data-quality,data-validation,duckdb,outlier-detection,polars,qa,qc,regression-testing,validation
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Database
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Software Development :: Quality Assurance
25
+ Classifier: Topic :: Software Development :: Testing
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.12
28
+ Requires-Dist: duckdb>=1.2
29
+ Requires-Dist: jinja2>=3.1
30
+ Requires-Dist: markupsafe>=2.1
31
+ Requires-Dist: numpy>=1.26
32
+ Requires-Dist: polars>=1.32
33
+ Requires-Dist: pyarrow>=16
34
+ Requires-Dist: pydantic>=2.7
35
+ Requires-Dist: pyyaml>=6.0.1
36
+ Provides-Extra: all
37
+ Requires-Dist: pandas>=2.2; extra == 'all'
38
+ Requires-Dist: scipy>=1.11.3; extra == 'all'
39
+ Provides-Extra: pandas
40
+ Requires-Dist: pandas>=2.2; extra == 'pandas'
41
+ Provides-Extra: stats
42
+ Requires-Dist: scipy>=1.11.3; extra == 'stats'
43
+ Description-Content-Type: text/markdown
44
+
45
+ # QAVF
46
+
47
+ [![CI](https://github.com/AhmedAredah/qavf/actions/workflows/ci.yml/badge.svg)](https://github.com/AhmedAredah/qavf/actions/workflows/ci.yml)
48
+ [![PyPI](https://img.shields.io/pypi/v/qavf)](https://pypi.org/project/qavf/)
49
+ [![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13%20%7C%203.14-blue)](https://github.com/AhmedAredah/qavf/blob/main/pyproject.toml)
50
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/AhmedAredah/qavf/blob/main/LICENSE)
51
+
52
+ **Configurable data QA/QC for Python.** Validate files, dataframes and database
53
+ queries against declared limits, statistical expectations and stored baselines.
54
+ QAVF reports exact violation counts with bounded evidence, and it never reports a
55
+ pass for a check it could not assess.
56
+
57
+ - **Any input.** CSV, Parquet, JSON, Excel, globs and URLs; DuckDB, SQLite, PostgreSQL
58
+ and MySQL tables; SQL queries; Polars, pandas and Arrow frames; Python loaders.
59
+ - **Column limits.** Types, nullability, ranges, allowed values, patterns, lengths,
60
+ uniqueness, keys, foreign keys and aggregate statistics, each with its own severity.
61
+ - **Statistical and temporal QA.** Trend, jump, seasonal and level-shift checks; entity
62
+ outliers; coverage gaps; distribution drift; reconciliation of totals across datasets.
63
+ - **Regression testing.** Keyed, tolerance-aware comparison against accepted baselines,
64
+ usable from pytest.
65
+ - **Reports and CI gates.** HTML and JSON reports, deterministic evidence and exit codes.
66
+ - **Extensible.** Rules and sources are plain classes discovered from packages, files or
67
+ entry points.
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ pip install "qavf[stats]" # or: uv tool install "qavf[stats]"
73
+ docker run --rm -v "$PWD:/work" ghcr.io/ahmedaredah/qavf run -c qavf.yaml
74
+ ```
75
+
76
+ Python 3.12 or newer on Linux, macOS or Windows. The `stats` extra adds SciPy-based
77
+ tests; `pandas` adds pandas adapters; `all` installs both. In GitHub Actions, use
78
+ [`AhmedAredah/qavf-action`](https://github.com/AhmedAredah/qavf-action).
79
+
80
+ ## Quick start
81
+
82
+ ```bash
83
+ qavf demo my-demo && qavf run -c my-demo/qavf.yaml # open my-demo/qa_output/qa_report.html
84
+ ```
85
+
86
+ For your own data, generate a configuration with inferred limits, review it, then run it:
87
+
88
+ ```bash
89
+ qavf init -d data/counts.csv -d crashes=data/crashes.parquet --margin 0.1
90
+ qavf run -c qavf.yaml
91
+ ```
92
+
93
+ ```yaml
94
+ datasets:
95
+ counts:
96
+ path: data/counts.csv
97
+ schema:
98
+ primary_key: [site_id, year]
99
+ columns:
100
+ site_id: {dtype: str, regex: 'S\d{2}'}
101
+ aadt:
102
+ dtype: int
103
+ ge: 0
104
+ checks:
105
+ - {le: 200000, severity: WARNING, name: implausibly high}
106
+ truck_share: {dtype: float, ge: 0, le: 1}
107
+ foreign_keys:
108
+ - {columns: [site_id], ref_dataset: sites}
109
+ sites:
110
+ path: data/sites.csv
111
+ schema: {primary_key: [site_id]}
112
+ ```
113
+
114
+ Row expressions and SQL assertions run in DuckDB, where each dataset is a table:
115
+
116
+ ```yaml
117
+ datasets:
118
+ crashes:
119
+ path: data/crashes.parquet
120
+ expressions:
121
+ - {expr: fatal <= crashes, severity: CRITICAL}
122
+ assertions:
123
+ - name: every site has counts
124
+ sql: SELECT site_id FROM sites WHERE site_id NOT IN (SELECT site_id FROM counts)
125
+ ```
126
+
127
+ [`config/qavf.full.yaml`](https://github.com/AhmedAredah/qavf/blob/main/config/qavf.full.yaml)
128
+ documents every option. Paths are relative to the configuration file, and any string
129
+ may use `${ENV_VAR}` so credentials stay out of the file.
130
+
131
+ ## Python API
132
+
133
+ ```python
134
+ from qavf import Dataset, ResourceLimits, validate
135
+
136
+ report = validate(
137
+ "readings.parquet",
138
+ schema={"columns": {"value": {"dtype": "float", "ge": 0}}},
139
+ )
140
+ print(report.verdict())
141
+
142
+ report = validate({
143
+ "counts": Dataset(counts_frame, schema={"columns": {"count": {"ge": 0}}}),
144
+ "sites": Dataset("sites.parquet", schema={"primary_key": ["site_id"]}),
145
+ })
146
+
147
+ # Run in a supervised worker process with OS-enforced limits.
148
+ report = validate("readings.parquet", config="qavf.yaml",
149
+ resources=ResourceLimits(wall_time=60, address_space="4GiB"))
150
+ ```
151
+
152
+ ## How a run is judged
153
+
154
+ Each result is **PASS**, **FAIL**, **SKIPPED** (nothing configured) or **ERROR**
155
+ (configured but not assessed, for example missing data or invalid SQL).
156
+
157
+ | Verdict | When | Exit code |
158
+ |---|---|---|
159
+ | passed | Every configured check was assessed and no failure meets `engine.fail_on` | 0 |
160
+ | failed | A failure at or above `engine.fail_on` (default `ERROR`) | 1 |
161
+ | incomplete | Any configured check could not be assessed | 1 |
162
+
163
+ Use `engine.require` to name checks that must be assessed for a run to pass.
164
+
165
+ ## Rules
166
+
167
+ | ID | Default | Checks |
168
+ |---|---|---|
169
+ | INP-001 | CRITICAL | Sources accessible and schemas inspectable |
170
+ | INP-002 | CRITICAL | Columns present with declared types; row-count limits |
171
+ | INP-003 | ERROR* | Column QA limits (*per-check severities) |
172
+ | INP-004 | ERROR | Primary keys unique |
173
+ | INP-005 | ERROR* | Foreign keys resolve |
174
+ | INP-006 | ERROR* | Row expressions hold |
175
+ | INP-007 | ERROR* | SQL assertions return no rows |
176
+ | INP-008 | ERROR | Newest date/time value is recent enough |
177
+ | TMP-001 | WARNING | Period-over-period change within a rate |
178
+ | TMP-002 | WARNING | Series trend in their declared direction |
179
+ | TMP-003 | WARNING | No period is a significant and material trend outlier |
180
+ | TMP-004 | ERROR | No group is missing a period |
181
+ | TMP-005 | WARNING | No value stuck for several periods |
182
+ | TMP-006 | ERROR | Cumulative series never decrease |
183
+ | TMP-007 | WARNING | No value is anomalous for its season |
184
+ | TMP-008 | WARNING | No series shifts to a sustained new level |
185
+ | SPA-001 | WARNING | No entity is an outlier among its peers |
186
+ | SPA-002 | WARNING | Each entity within a factor of its group's median |
187
+ | SPA-003 | ERROR | Every expected entity is covered in every period |
188
+ | GEO-001 | ERROR | Coordinates valid, present and inside a bounding box |
189
+ | STA-001 | WARNING | z-score, modified z-score or IQR outlier scans |
190
+ | STA-002 | WARNING | Distributions have not drifted from a reference |
191
+ | REC-001 | ERROR | Totals reconcile between datasets, key by key |
192
+ | REC-002 | ERROR | Shares sum to their total within each group |
193
+ | REC-003 | ERROR | The full expected key grid is present |
194
+ | REC-004 | ERROR | Two datasets have the same key set |
195
+ | REC-005 | ERROR | Related datasets carry one identical batch id |
196
+ | REG-001 | ERROR | Outputs match baselines within tolerance |
197
+
198
+ Parameters for every rule: [docs/rules.md](https://github.com/AhmedAredah/qavf/blob/main/docs/rules.md)
199
+ or `qavf list-rules`.
200
+
201
+ ## Regression testing
202
+
203
+ ```python
204
+ from qavf.regression import assert_outputs_match
205
+
206
+
207
+ def test_output_matches_baseline(tmp_path):
208
+ run_pipeline(output=tmp_path / "out.parquet")
209
+ assert_outputs_match("tests/baselines/out.parquet", tmp_path / "out.parquet",
210
+ keys=["site_id", "year"], rtol=1e-6)
211
+ ```
212
+
213
+ Rows match by key, numbers within tolerance, and changed values, added or removed rows
214
+ and schema changes are reported. Accept reviewed outputs with `qavf baseline`, which
215
+ records a manifest with the keys, column types and a SHA-256 of the file.
216
+
217
+ ## CLI
218
+
219
+ | Command | Purpose |
220
+ |---|---|
221
+ | `qavf run -c CONFIG` | Run QA and write HTML/JSON reports |
222
+ | `qavf init -d [NAME=]PATH` | Profile data and write a starting configuration |
223
+ | `qavf profile PATH` | Print inferred QA limits for a file |
224
+ | `qavf schema` | Write the configuration JSON Schema for editor support |
225
+ | `qavf list-rules` | List discovered rules |
226
+ | `qavf compare BASELINE CANDIDATE -k KEY` | Compare an output with a baseline |
227
+ | `qavf baseline SOURCE DEST -k KEY` | Accept an output as a baseline |
228
+ | `qavf demo DIR` | Write a runnable demo project |
229
+
230
+ Exit codes: `0` passed, `1` QA failed or outputs differ, `2` usage, configuration or
231
+ resource error.
232
+
233
+ ## Documentation
234
+
235
+ - [Python API](https://github.com/AhmedAredah/qavf/blob/main/docs/api.md) and
236
+ [sources](https://github.com/AhmedAredah/qavf/blob/main/docs/sources.md)
237
+ - [Rules reference](https://github.com/AhmedAredah/qavf/blob/main/docs/rules.md) and
238
+ [writing plugins](https://github.com/AhmedAredah/qavf/blob/main/docs/plugins.md)
239
+ - [Resource limits](https://github.com/AhmedAredah/qavf/blob/main/docs/resources.md) and
240
+ [isolated execution](https://github.com/AhmedAredah/qavf/blob/main/docs/execution_limits.md)
241
+ - [Numerical contracts](https://github.com/AhmedAredah/qavf/blob/main/docs/numerics.md) and
242
+ [baseline acceptance](https://github.com/AhmedAredah/qavf/blob/main/docs/baseline_acceptance.md)
243
+
244
+ ## Development
245
+
246
+ ```bash
247
+ uv sync --all-extras
248
+ uv run pytest
249
+ uv run ruff check . && uv run mypy qavf scripts
250
+ ```
251
+
252
+ See [release assurance](https://github.com/AhmedAredah/qavf/blob/main/docs/release_assurance.md)
253
+ for the full verification matrix and [CHANGELOG.md](CHANGELOG.md) for changes.
254
+
255
+ ## License
256
+
257
+ [MIT](LICENSE)