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.
- qavf-0.4.0/.gitattributes +1 -0
- qavf-0.4.0/.github/workflows/ci.yml +61 -0
- qavf-0.4.0/.github/workflows/verify.yml +273 -0
- qavf-0.4.0/.gitignore +23 -0
- qavf-0.4.0/CHANGELOG.md +21 -0
- qavf-0.4.0/LICENSE +21 -0
- qavf-0.4.0/PKG-INFO +257 -0
- qavf-0.4.0/README.md +213 -0
- qavf-0.4.0/config/qavf.full.yaml +193 -0
- qavf-0.4.0/config/qavf.minimal.yaml +11 -0
- qavf-0.4.0/docs/api.md +216 -0
- qavf-0.4.0/docs/baseline_acceptance.md +50 -0
- qavf-0.4.0/docs/code_organization.md +119 -0
- qavf-0.4.0/docs/coherent_observations.md +117 -0
- qavf-0.4.0/docs/correctness_testing.md +75 -0
- qavf-0.4.0/docs/execution_limits.md +214 -0
- qavf-0.4.0/docs/full_data_loading.md +95 -0
- qavf-0.4.0/docs/integrations.md +82 -0
- qavf-0.4.0/docs/numerics.md +40 -0
- qavf-0.4.0/docs/performance_benchmarks.md +182 -0
- qavf-0.4.0/docs/plugins.md +250 -0
- qavf-0.4.0/docs/profiling.md +49 -0
- qavf-0.4.0/docs/regression_streaming.md +71 -0
- qavf-0.4.0/docs/release_assurance.md +164 -0
- qavf-0.4.0/docs/releasing.md +57 -0
- qavf-0.4.0/docs/resources.md +181 -0
- qavf-0.4.0/docs/rules.md +800 -0
- qavf-0.4.0/docs/scan_optimization.md +78 -0
- qavf-0.4.0/docs/sources.md +57 -0
- qavf-0.4.0/pyproject.toml +121 -0
- qavf-0.4.0/qavf/__init__.py +128 -0
- qavf-0.4.0/qavf/_audit_temporal.py +43 -0
- qavf-0.4.0/qavf/_float_accumulator.py +52 -0
- qavf-0.4.0/qavf/_observations.py +71 -0
- qavf-0.4.0/qavf/_version.py +3 -0
- qavf-0.4.0/qavf/aggregation.py +84 -0
- qavf-0.4.0/qavf/api.py +34 -0
- qavf-0.4.0/qavf/audit_codec.py +428 -0
- qavf-0.4.0/qavf/cli.py +429 -0
- qavf-0.4.0/qavf/demo.py +236 -0
- qavf-0.4.0/qavf/demo_config.yaml +155 -0
- qavf-0.4.0/qavf/docgen.py +191 -0
- qavf-0.4.0/qavf/engine/__init__.py +1 -0
- qavf-0.4.0/qavf/engine/cache_ownership.py +372 -0
- qavf-0.4.0/qavf/engine/capabilities.py +27 -0
- qavf-0.4.0/qavf/engine/catalog.py +285 -0
- qavf-0.4.0/qavf/engine/compilation.py +216 -0
- qavf-0.4.0/qavf/engine/connections.py +199 -0
- qavf-0.4.0/qavf/engine/context.py +1322 -0
- qavf-0.4.0/qavf/engine/observation.py +369 -0
- qavf-0.4.0/qavf/engine/observation_plan.py +204 -0
- qavf-0.4.0/qavf/engine/owned_ipc.py +31 -0
- qavf-0.4.0/qavf/engine/planning.py +135 -0
- qavf-0.4.0/qavf/engine/projection.py +76 -0
- qavf-0.4.0/qavf/engine/registry.py +493 -0
- qavf-0.4.0/qavf/engine/relations.py +27 -0
- qavf-0.4.0/qavf/engine/resources.py +425 -0
- qavf-0.4.0/qavf/engine/runner.py +912 -0
- qavf-0.4.0/qavf/engine/session.py +133 -0
- qavf-0.4.0/qavf/engine/severity.py +80 -0
- qavf-0.4.0/qavf/engine/source_read.py +148 -0
- qavf-0.4.0/qavf/engine/spill.py +129 -0
- qavf-0.4.0/qavf/engine/sql.py +172 -0
- qavf-0.4.0/qavf/engine/sql_read.py +227 -0
- qavf-0.4.0/qavf/engine/sqlite_store.py +128 -0
- qavf-0.4.0/qavf/engine/thread_budget.py +23 -0
- qavf-0.4.0/qavf/execution/__init__.py +0 -0
- qavf-0.4.0/qavf/execution/_windows_job.py +279 -0
- qavf-0.4.0/qavf/execution/bootstrap.py +61 -0
- qavf-0.4.0/qavf/execution/dispatch.py +46 -0
- qavf-0.4.0/qavf/execution/errors.py +11 -0
- qavf-0.4.0/qavf/execution/inputs.py +33 -0
- qavf-0.4.0/qavf/execution/os_limits.py +87 -0
- qavf-0.4.0/qavf/execution/policy.py +8 -0
- qavf-0.4.0/qavf/execution/protocol.py +86 -0
- qavf-0.4.0/qavf/execution/supervisor.py +434 -0
- qavf-0.4.0/qavf/execution/worker.py +99 -0
- qavf-0.4.0/qavf/inputs/__init__.py +4 -0
- qavf-0.4.0/qavf/inputs/adapters.py +136 -0
- qavf-0.4.0/qavf/inputs/descriptors.py +201 -0
- qavf-0.4.0/qavf/inputs/normalize.py +285 -0
- qavf-0.4.0/qavf/inputs/resources.py +120 -0
- qavf-0.4.0/qavf/io.py +109 -0
- qavf-0.4.0/qavf/key_codec.py +35 -0
- qavf-0.4.0/qavf/keys.py +112 -0
- qavf-0.4.0/qavf/numeric.py +150 -0
- qavf-0.4.0/qavf/ordering.py +59 -0
- qavf-0.4.0/qavf/output_format.py +35 -0
- qavf-0.4.0/qavf/profiling/__init__.py +108 -0
- qavf-0.4.0/qavf/profiling/accumulator.py +133 -0
- qavf-0.4.0/qavf/profiling/values.py +134 -0
- qavf-0.4.0/qavf/py.typed +0 -0
- qavf-0.4.0/qavf/redaction.py +221 -0
- qavf-0.4.0/qavf/regression/__init__.py +28 -0
- qavf-0.4.0/qavf/regression/_baseline_writer.py +176 -0
- qavf-0.4.0/qavf/regression/_cells.py +125 -0
- qavf-0.4.0/qavf/regression/_columns.py +10 -0
- qavf-0.4.0/qavf/regression/_comparison.py +270 -0
- qavf-0.4.0/qavf/regression/_evidence.py +167 -0
- qavf-0.4.0/qavf/regression/_indexed.py +205 -0
- qavf-0.4.0/qavf/regression/_inputs.py +146 -0
- qavf-0.4.0/qavf/regression/_numeric.py +96 -0
- qavf-0.4.0/qavf/regression/_plan.py +208 -0
- qavf-0.4.0/qavf/regression/_schema.py +77 -0
- qavf-0.4.0/qavf/regression/_streaming.py +221 -0
- qavf-0.4.0/qavf/regression/baselines.py +357 -0
- qavf-0.4.0/qavf/regression/compare_outputs.py +282 -0
- qavf-0.4.0/qavf/regression/models.py +163 -0
- qavf-0.4.0/qavf/reports/__init__.py +1 -0
- qavf-0.4.0/qavf/reports/charts.py +101 -0
- qavf-0.4.0/qavf/reports/html.py +287 -0
- qavf-0.4.0/qavf/reports/models.py +508 -0
- qavf-0.4.0/qavf/reports/presentation.py +117 -0
- qavf-0.4.0/qavf/reports/templates/records.html.j2 +12 -0
- qavf-0.4.0/qavf/reports/templates/report.html.j2 +288 -0
- qavf-0.4.0/qavf/rules/__init__.py +1 -0
- qavf-0.4.0/qavf/rules/_column_batch.py +235 -0
- qavf-0.4.0/qavf/rules/_column_plan.py +543 -0
- qavf-0.4.0/qavf/rules/_column_rows.py +131 -0
- qavf-0.4.0/qavf/rules/_column_stream.py +110 -0
- qavf-0.4.0/qavf/rules/_contracts.py +58 -0
- qavf-0.4.0/qavf/rules/_coordinate_sql.py +98 -0
- qavf-0.4.0/qavf/rules/_coverage.py +247 -0
- qavf-0.4.0/qavf/rules/_distribution.py +170 -0
- qavf-0.4.0/qavf/rules/_domains.py +77 -0
- qavf-0.4.0/qavf/rules/_exact_grouped.py +196 -0
- qavf-0.4.0/qavf/rules/_execution.py +334 -0
- qavf-0.4.0/qavf/rules/_grid.py +210 -0
- qavf-0.4.0/qavf/rules/_grouped.py +338 -0
- qavf-0.4.0/qavf/rules/_key_summary.py +131 -0
- qavf-0.4.0/qavf/rules/_ordered_grouped.py +107 -0
- qavf-0.4.0/qavf/rules/_outlier_batches.py +298 -0
- qavf-0.4.0/qavf/rules/_predicates.py +378 -0
- qavf-0.4.0/qavf/rules/_regex.py +32 -0
- qavf-0.4.0/qavf/rules/_row_summary.py +97 -0
- qavf-0.4.0/qavf/rules/_spatial_aggregation.py +120 -0
- qavf-0.4.0/qavf/rules/_statistics_plan.py +69 -0
- qavf-0.4.0/qavf/rules/_temporal.py +82 -0
- qavf-0.4.0/qavf/rules/freshness_checks.py +205 -0
- qavf-0.4.0/qavf/rules/geo_checks.py +244 -0
- qavf-0.4.0/qavf/rules/helpers.py +468 -0
- qavf-0.4.0/qavf/rules/input_checks/__init__.py +23 -0
- qavf-0.4.0/qavf/rules/input_checks/_column_eager.py +347 -0
- qavf-0.4.0/qavf/rules/input_checks/_column_simple.py +326 -0
- qavf-0.4.0/qavf/rules/input_checks/_column_values.py +192 -0
- qavf-0.4.0/qavf/rules/input_checks/_datasets.py +107 -0
- qavf-0.4.0/qavf/rules/input_checks/availability.py +192 -0
- qavf-0.4.0/qavf/rules/input_checks/columns.py +160 -0
- qavf-0.4.0/qavf/rules/input_checks/expressions.py +184 -0
- qavf-0.4.0/qavf/rules/input_checks/keys.py +260 -0
- qavf-0.4.0/qavf/rules/input_checks/references.py +432 -0
- qavf-0.4.0/qavf/rules/reconciliation_checks.py +1020 -0
- qavf-0.4.0/qavf/rules/regression_checks.py +250 -0
- qavf-0.4.0/qavf/rules/spatial_checks.py +881 -0
- qavf-0.4.0/qavf/rules/statistical_checks.py +411 -0
- qavf-0.4.0/qavf/rules/temporal_checks.py +1654 -0
- qavf-0.4.0/qavf/scaffold.py +241 -0
- qavf-0.4.0/qavf/settings.py +1046 -0
- qavf-0.4.0/qavf/sources/__init__.py +41 -0
- qavf-0.4.0/qavf/sources/base.py +424 -0
- qavf-0.4.0/qavf/sources/builtin.py +17 -0
- qavf-0.4.0/qavf/sources/collection.py +18 -0
- qavf-0.4.0/qavf/sources/errors.py +61 -0
- qavf-0.4.0/qavf/sources/execution.py +104 -0
- qavf-0.4.0/qavf/sources/file.py +483 -0
- qavf-0.4.0/qavf/sources/formats.py +111 -0
- qavf-0.4.0/qavf/sources/frames.py +44 -0
- qavf-0.4.0/qavf/sources/identity.py +89 -0
- qavf-0.4.0/qavf/sources/memory.py +48 -0
- qavf-0.4.0/qavf/sources/native.py +165 -0
- qavf-0.4.0/qavf/sources/parameters.py +152 -0
- qavf-0.4.0/qavf/sources/python.py +52 -0
- qavf-0.4.0/qavf/sources/query.py +108 -0
- qavf-0.4.0/qavf/sources/sql.py +452 -0
- qavf-0.4.0/qavf/sources/typed.py +40 -0
- qavf-0.4.0/qavf/statistics/__init__.py +1 -0
- qavf-0.4.0/qavf/statistics/ordered.py +189 -0
- qavf-0.4.0/qavf/stats.py +600 -0
- qavf-0.4.0/qavf/testing.py +115 -0
- qavf-0.4.0/qavf/typed_values.py +64 -0
- qavf-0.4.0/scripts/benchmark_performance.py +12 -0
- qavf-0.4.0/scripts/benchmark_scan_work.py +360 -0
- qavf-0.4.0/scripts/generate_docs.py +50 -0
- qavf-0.4.0/scripts/peak_rss.py +51 -0
- qavf-0.4.0/scripts/performance/__init__.py +1 -0
- qavf-0.4.0/scripts/performance/cache.py +258 -0
- qavf-0.4.0/scripts/performance/capacity.py +152 -0
- qavf-0.4.0/scripts/performance/catalog.py +78 -0
- qavf-0.4.0/scripts/performance/cli.py +146 -0
- qavf-0.4.0/scripts/performance/fixtures.py +302 -0
- qavf-0.4.0/scripts/performance/metrics.py +360 -0
- qavf-0.4.0/scripts/performance/models.py +44 -0
- qavf-0.4.0/scripts/performance/planning.py +92 -0
- qavf-0.4.0/scripts/performance/policy.py +22 -0
- qavf-0.4.0/scripts/performance/process_tree.py +89 -0
- qavf-0.4.0/scripts/performance/provenance.py +118 -0
- qavf-0.4.0/scripts/performance/regression.py +266 -0
- qavf-0.4.0/scripts/performance/reporting.py +482 -0
- qavf-0.4.0/scripts/performance/runner.py +171 -0
- qavf-0.4.0/scripts/performance/storage.py +50 -0
- qavf-0.4.0/scripts/performance/streaming.py +133 -0
- qavf-0.4.0/scripts/performance/suite.py +213 -0
- qavf-0.4.0/scripts/performance/worker.py +118 -0
- qavf-0.4.0/scripts/performance/workloads.py +383 -0
- qavf-0.4.0/scripts/release_checks/__init__.py +1 -0
- qavf-0.4.0/scripts/release_checks/coverage.py +57 -0
- qavf-0.4.0/scripts/release_checks/extensions.py +69 -0
- qavf-0.4.0/scripts/release_checks/gate.py +173 -0
- qavf-0.4.0/scripts/release_checks/policy.py +95 -0
- qavf-0.4.0/scripts/release_checks/provenance.py +139 -0
- qavf-0.4.0/scripts/release_checks/pytest_plugin.py +84 -0
- qavf-0.4.0/scripts/release_checks/runner.py +112 -0
- qavf-0.4.0/scripts/release_checks/validation.py +69 -0
- qavf-0.4.0/scripts/release_checks/wheel.py +192 -0
- qavf-0.4.0/scripts/verify_core.py +80 -0
- qavf-0.4.0/scripts/verify_dist.py +92 -0
- qavf-0.4.0/scripts/verify_release.py +69 -0
- qavf-0.4.0/tests/assurance/__init__.py +5 -0
- qavf-0.4.0/tests/assurance/conftest.py +12 -0
- qavf-0.4.0/tests/assurance/support/__init__.py +1 -0
- qavf-0.4.0/tests/assurance/support/adapters.py +63 -0
- qavf-0.4.0/tests/assurance/support/contracts.py +54 -0
- qavf-0.4.0/tests/assurance/support/oracles.py +39 -0
- qavf-0.4.0/tests/assurance/support/strategies.py +16 -0
- qavf-0.4.0/tests/assurance/test_audit_values.py +105 -0
- qavf-0.4.0/tests/assurance/test_cache_labels.py +45 -0
- qavf-0.4.0/tests/assurance/test_execution_dispatch.py +92 -0
- qavf-0.4.0/tests/assurance/test_failures.py +223 -0
- qavf-0.4.0/tests/assurance/test_keys.py +184 -0
- qavf-0.4.0/tests/assurance/test_missingness.py +101 -0
- qavf-0.4.0/tests/assurance/test_precision.py +167 -0
- qavf-0.4.0/tests/assurance/test_regression.py +187 -0
- qavf-0.4.0/tests/assurance/test_sources.py +71 -0
- qavf-0.4.0/tests/assurance/test_temporal.py +184 -0
- qavf-0.4.0/tests/baselines/demo_counts_by_year.csv +10 -0
- qavf-0.4.0/tests/conftest.py +39 -0
- qavf-0.4.0/tests/dependency-floors.txt +12 -0
- qavf-0.4.0/tests/execution_support/__init__.py +1 -0
- qavf-0.4.0/tests/execution_support/driver.py +51 -0
- qavf-0.4.0/tests/execution_support/probes.py +107 -0
- qavf-0.4.0/tests/execution_support/processes.py +61 -0
- qavf-0.4.0/tests/integration/__init__.py +1 -0
- qavf-0.4.0/tests/integration/cleanup.py +26 -0
- qavf-0.4.0/tests/integration/conftest.py +17 -0
- qavf-0.4.0/tests/integration/contracts.py +82 -0
- qavf-0.4.0/tests/integration/http_service.py +78 -0
- qavf-0.4.0/tests/integration/policy.py +67 -0
- qavf-0.4.0/tests/integration/postgres_service.py +75 -0
- qavf-0.4.0/tests/integration/pytest_support.py +18 -0
- qavf-0.4.0/tests/integration/s3_service.py +123 -0
- qavf-0.4.0/tests/integration/test_http.py +48 -0
- qavf-0.4.0/tests/integration/test_policy.py +153 -0
- qavf-0.4.0/tests/integration/test_postgres.py +62 -0
- qavf-0.4.0/tests/integration/test_s3.py +52 -0
- qavf-0.4.0/tests/test_acceptance.py +365 -0
- qavf-0.4.0/tests/test_adapter_budgets.py +96 -0
- qavf-0.4.0/tests/test_aggregation.py +229 -0
- qavf-0.4.0/tests/test_api_equivalence.py +356 -0
- qavf-0.4.0/tests/test_api_inputs.py +238 -0
- qavf-0.4.0/tests/test_arrow_pandas_cache.py +140 -0
- qavf-0.4.0/tests/test_baseline_manifest.py +126 -0
- qavf-0.4.0/tests/test_baseline_staging.py +128 -0
- qavf-0.4.0/tests/test_cache_ownership.py +311 -0
- qavf-0.4.0/tests/test_column_check_plans.py +165 -0
- qavf-0.4.0/tests/test_concurrent_loads.py +301 -0
- qavf-0.4.0/tests/test_dataset_catalog.py +251 -0
- qavf-0.4.0/tests/test_dataset_lifetimes.py +129 -0
- qavf-0.4.0/tests/test_discovery.py +408 -0
- qavf-0.4.0/tests/test_docgen_cells.py +22 -0
- qavf-0.4.0/tests/test_documentation_source.py +44 -0
- qavf-0.4.0/tests/test_dst_coverage.py +63 -0
- qavf-0.4.0/tests/test_engine.py +289 -0
- qavf-0.4.0/tests/test_engine_locking.py +382 -0
- qavf-0.4.0/tests/test_engine_ownership.py +421 -0
- qavf-0.4.0/tests/test_engine_resources.py +373 -0
- qavf-0.4.0/tests/test_evidence_sampling.py +195 -0
- qavf-0.4.0/tests/test_exact_grouped.py +225 -0
- qavf-0.4.0/tests/test_examples.py +113 -0
- qavf-0.4.0/tests/test_execution_isolation.py +397 -0
- qavf-0.4.0/tests/test_execution_limits.py +207 -0
- qavf-0.4.0/tests/test_execution_policy.py +171 -0
- qavf-0.4.0/tests/test_expected_scope.py +201 -0
- qavf-0.4.0/tests/test_false_pass_regressions.py +375 -0
- qavf-0.4.0/tests/test_geo_and_drift.py +133 -0
- qavf-0.4.0/tests/test_indexed_statistics.py +226 -0
- qavf-0.4.0/tests/test_input_backends.py +404 -0
- qavf-0.4.0/tests/test_input_boundaries.py +236 -0
- qavf-0.4.0/tests/test_input_checks.py +343 -0
- qavf-0.4.0/tests/test_input_exactness.py +304 -0
- qavf-0.4.0/tests/test_input_facade.py +160 -0
- qavf-0.4.0/tests/test_input_identity.py +254 -0
- qavf-0.4.0/tests/test_input_package_contracts.py +117 -0
- qavf-0.4.0/tests/test_input_path_identity.py +96 -0
- qavf-0.4.0/tests/test_input_semantics.py +193 -0
- qavf-0.4.0/tests/test_isolated_supervisor.py +378 -0
- qavf-0.4.0/tests/test_isolation_transport.py +115 -0
- qavf-0.4.0/tests/test_key_joins.py +243 -0
- qavf-0.4.0/tests/test_limits_native_budgets.py +118 -0
- qavf-0.4.0/tests/test_loading_comparison_plan.py +227 -0
- qavf-0.4.0/tests/test_loading_contracts.py +322 -0
- qavf-0.4.0/tests/test_loading_group_contracts.py +135 -0
- qavf-0.4.0/tests/test_loading_key_coherence.py +146 -0
- qavf-0.4.0/tests/test_loading_native_binding.py +252 -0
- qavf-0.4.0/tests/test_loading_reads.py +427 -0
- qavf-0.4.0/tests/test_loading_reductions.py +207 -0
- qavf-0.4.0/tests/test_loading_regression_inputs.py +325 -0
- qavf-0.4.0/tests/test_loading_regression_payload_plan.py +63 -0
- qavf-0.4.0/tests/test_loading_regression_payloads.py +283 -0
- qavf-0.4.0/tests/test_loading_regression_stream.py +342 -0
- qavf-0.4.0/tests/test_loading_row_reductions.py +222 -0
- qavf-0.4.0/tests/test_loading_scalar_statistics.py +187 -0
- qavf-0.4.0/tests/test_loading_spill_reductions.py +122 -0
- qavf-0.4.0/tests/test_membership.py +267 -0
- qavf-0.4.0/tests/test_missing_values.py +250 -0
- qavf-0.4.0/tests/test_modular_connections.py +95 -0
- qavf-0.4.0/tests/test_modular_numeric_keys.py +100 -0
- qavf-0.4.0/tests/test_modular_rule_contracts.py +117 -0
- qavf-0.4.0/tests/test_multi_file_schemas.py +157 -0
- qavf-0.4.0/tests/test_native_resources.py +193 -0
- qavf-0.4.0/tests/test_numeric_admission.py +115 -0
- qavf-0.4.0/tests/test_numerical_oracles.py +154 -0
- qavf-0.4.0/tests/test_numerical_verdicts.py +243 -0
- qavf-0.4.0/tests/test_observation_plan.py +136 -0
- qavf-0.4.0/tests/test_ordered_grouped.py +169 -0
- qavf-0.4.0/tests/test_organization_sources.py +132 -0
- qavf-0.4.0/tests/test_outlier_edge_cases.py +282 -0
- qavf-0.4.0/tests/test_packaging_and_connections.py +280 -0
- qavf-0.4.0/tests/test_performance_capacity.py +53 -0
- qavf-0.4.0/tests/test_performance_metrics.py +194 -0
- qavf-0.4.0/tests/test_performance_process_tree.py +75 -0
- qavf-0.4.0/tests/test_performance_protocol.py +308 -0
- qavf-0.4.0/tests/test_performance_regression_cache.py +236 -0
- qavf-0.4.0/tests/test_performance_reporting.py +252 -0
- qavf-0.4.0/tests/test_performance_runner.py +213 -0
- qavf-0.4.0/tests/test_performance_workloads.py +243 -0
- qavf-0.4.0/tests/test_plugins_and_globs.py +189 -0
- qavf-0.4.0/tests/test_primary_keys.py +222 -0
- qavf-0.4.0/tests/test_profile_datetime_bounds.py +36 -0
- qavf-0.4.0/tests/test_public_contracts.py +147 -0
- qavf-0.4.0/tests/test_reconciliation.py +208 -0
- qavf-0.4.0/tests/test_reconciliation_aliases.py +76 -0
- qavf-0.4.0/tests/test_redaction.py +327 -0
- qavf-0.4.0/tests/test_regression.py +169 -0
- qavf-0.4.0/tests/test_regression_acceptance.py +334 -0
- qavf-0.4.0/tests/test_regression_counts.py +139 -0
- qavf-0.4.0/tests/test_regression_exactness.py +305 -0
- qavf-0.4.0/tests/test_regression_reporting.py +213 -0
- qavf-0.4.0/tests/test_release_artifacts.py +91 -0
- qavf-0.4.0/tests/test_release_checks.py +380 -0
- qavf-0.4.0/tests/test_report_encoding.py +114 -0
- qavf-0.4.0/tests/test_report_evidence.py +365 -0
- qavf-0.4.0/tests/test_reports_cli.py +242 -0
- qavf-0.4.0/tests/test_representation_parity.py +72 -0
- qavf-0.4.0/tests/test_rule_compilation.py +163 -0
- qavf-0.4.0/tests/test_rule_observations.py +121 -0
- qavf-0.4.0/tests/test_scaffold.py +223 -0
- qavf-0.4.0/tests/test_scale_invariance.py +271 -0
- qavf-0.4.0/tests/test_scan_column_batches.py +262 -0
- qavf-0.4.0/tests/test_scan_identity.py +457 -0
- qavf-0.4.0/tests/test_scan_key_rules.py +231 -0
- qavf-0.4.0/tests/test_scan_key_summaries.py +150 -0
- qavf-0.4.0/tests/test_schema_and_profiles.py +238 -0
- qavf-0.4.0/tests/test_schema_changes.py +171 -0
- qavf-0.4.0/tests/test_settings_validation.py +117 -0
- qavf-0.4.0/tests/test_shared_statistics.py +242 -0
- qavf-0.4.0/tests/test_snapshots.py +148 -0
- qavf-0.4.0/tests/test_source_boundaries.py +342 -0
- qavf-0.4.0/tests/test_source_expressions.py +349 -0
- qavf-0.4.0/tests/test_source_policy.py +333 -0
- qavf-0.4.0/tests/test_sources.py +262 -0
- qavf-0.4.0/tests/test_stale_values.py +91 -0
- qavf-0.4.0/tests/test_statistical_invariants.py +250 -0
- qavf-0.4.0/tests/test_statistical_rules.py +459 -0
- qavf-0.4.0/tests/test_stats.py +106 -0
- qavf-0.4.0/tests/test_temporal_identity.py +358 -0
- qavf-0.4.0/tests/test_temporal_precision.py +256 -0
- qavf-0.4.0/tests/test_temporal_spatial_checks.py +240 -0
- qavf-0.4.0/tests/test_time_series_models.py +97 -0
- qavf-0.4.0/tests/test_typed_limits.py +302 -0
- qavf-0.4.0/tests/test_units.py +85 -0
- qavf-0.4.0/tests/test_writers_and_readme.py +318 -0
- 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/
|
qavf-0.4.0/CHANGELOG.md
ADDED
|
@@ -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
|
+
[](https://github.com/AhmedAredah/qavf/actions/workflows/ci.yml)
|
|
48
|
+
[](https://pypi.org/project/qavf/)
|
|
49
|
+
[](https://github.com/AhmedAredah/qavf/blob/main/pyproject.toml)
|
|
50
|
+
[](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)
|