truthound 1.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- truthound/__init__.py +162 -0
- truthound/adapters.py +100 -0
- truthound/api.py +365 -0
- truthound/audit/__init__.py +248 -0
- truthound/audit/core.py +967 -0
- truthound/audit/filters.py +620 -0
- truthound/audit/formatters.py +707 -0
- truthound/audit/logger.py +902 -0
- truthound/audit/middleware.py +571 -0
- truthound/audit/storage.py +1083 -0
- truthound/benchmark/__init__.py +123 -0
- truthound/benchmark/base.py +757 -0
- truthound/benchmark/comparison.py +635 -0
- truthound/benchmark/generators.py +706 -0
- truthound/benchmark/reporters.py +718 -0
- truthound/benchmark/runner.py +635 -0
- truthound/benchmark/scenarios.py +712 -0
- truthound/cache.py +252 -0
- truthound/checkpoint/__init__.py +136 -0
- truthound/checkpoint/actions/__init__.py +164 -0
- truthound/checkpoint/actions/base.py +324 -0
- truthound/checkpoint/actions/custom.py +234 -0
- truthound/checkpoint/actions/discord_notify.py +290 -0
- truthound/checkpoint/actions/email_notify.py +405 -0
- truthound/checkpoint/actions/github_action.py +406 -0
- truthound/checkpoint/actions/opsgenie.py +1499 -0
- truthound/checkpoint/actions/pagerduty.py +226 -0
- truthound/checkpoint/actions/slack_notify.py +233 -0
- truthound/checkpoint/actions/store_result.py +249 -0
- truthound/checkpoint/actions/teams_notify.py +1570 -0
- truthound/checkpoint/actions/telegram_notify.py +419 -0
- truthound/checkpoint/actions/update_docs.py +552 -0
- truthound/checkpoint/actions/webhook.py +293 -0
- truthound/checkpoint/analytics/__init__.py +147 -0
- truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
- truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
- truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
- truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
- truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
- truthound/checkpoint/analytics/analyzers/base.py +270 -0
- truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
- truthound/checkpoint/analytics/analyzers/trend.py +314 -0
- truthound/checkpoint/analytics/models.py +292 -0
- truthound/checkpoint/analytics/protocols.py +549 -0
- truthound/checkpoint/analytics/service.py +718 -0
- truthound/checkpoint/analytics/stores/__init__.py +16 -0
- truthound/checkpoint/analytics/stores/base.py +306 -0
- truthound/checkpoint/analytics/stores/memory_store.py +353 -0
- truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
- truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
- truthound/checkpoint/async_actions.py +794 -0
- truthound/checkpoint/async_base.py +708 -0
- truthound/checkpoint/async_checkpoint.py +617 -0
- truthound/checkpoint/async_runner.py +639 -0
- truthound/checkpoint/checkpoint.py +527 -0
- truthound/checkpoint/ci/__init__.py +61 -0
- truthound/checkpoint/ci/detector.py +355 -0
- truthound/checkpoint/ci/reporter.py +436 -0
- truthound/checkpoint/ci/templates.py +454 -0
- truthound/checkpoint/circuitbreaker/__init__.py +133 -0
- truthound/checkpoint/circuitbreaker/breaker.py +542 -0
- truthound/checkpoint/circuitbreaker/core.py +252 -0
- truthound/checkpoint/circuitbreaker/detection.py +459 -0
- truthound/checkpoint/circuitbreaker/middleware.py +389 -0
- truthound/checkpoint/circuitbreaker/registry.py +357 -0
- truthound/checkpoint/distributed/__init__.py +139 -0
- truthound/checkpoint/distributed/backends/__init__.py +35 -0
- truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
- truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
- truthound/checkpoint/distributed/backends/local_backend.py +397 -0
- truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
- truthound/checkpoint/distributed/base.py +774 -0
- truthound/checkpoint/distributed/orchestrator.py +765 -0
- truthound/checkpoint/distributed/protocols.py +842 -0
- truthound/checkpoint/distributed/registry.py +449 -0
- truthound/checkpoint/idempotency/__init__.py +120 -0
- truthound/checkpoint/idempotency/core.py +295 -0
- truthound/checkpoint/idempotency/fingerprint.py +454 -0
- truthound/checkpoint/idempotency/locking.py +604 -0
- truthound/checkpoint/idempotency/service.py +592 -0
- truthound/checkpoint/idempotency/stores.py +653 -0
- truthound/checkpoint/monitoring/__init__.py +134 -0
- truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
- truthound/checkpoint/monitoring/aggregators/base.py +372 -0
- truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
- truthound/checkpoint/monitoring/aggregators/window.py +493 -0
- truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
- truthound/checkpoint/monitoring/collectors/base.py +257 -0
- truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
- truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
- truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
- truthound/checkpoint/monitoring/events.py +410 -0
- truthound/checkpoint/monitoring/protocols.py +636 -0
- truthound/checkpoint/monitoring/service.py +578 -0
- truthound/checkpoint/monitoring/views/__init__.py +17 -0
- truthound/checkpoint/monitoring/views/base.py +172 -0
- truthound/checkpoint/monitoring/views/queue_view.py +220 -0
- truthound/checkpoint/monitoring/views/task_view.py +240 -0
- truthound/checkpoint/monitoring/views/worker_view.py +263 -0
- truthound/checkpoint/registry.py +337 -0
- truthound/checkpoint/runner.py +356 -0
- truthound/checkpoint/transaction/__init__.py +133 -0
- truthound/checkpoint/transaction/base.py +389 -0
- truthound/checkpoint/transaction/compensatable.py +537 -0
- truthound/checkpoint/transaction/coordinator.py +576 -0
- truthound/checkpoint/transaction/executor.py +622 -0
- truthound/checkpoint/transaction/idempotency.py +534 -0
- truthound/checkpoint/transaction/saga/__init__.py +143 -0
- truthound/checkpoint/transaction/saga/builder.py +584 -0
- truthound/checkpoint/transaction/saga/definition.py +515 -0
- truthound/checkpoint/transaction/saga/event_store.py +542 -0
- truthound/checkpoint/transaction/saga/patterns.py +833 -0
- truthound/checkpoint/transaction/saga/runner.py +718 -0
- truthound/checkpoint/transaction/saga/state_machine.py +793 -0
- truthound/checkpoint/transaction/saga/strategies.py +780 -0
- truthound/checkpoint/transaction/saga/testing.py +886 -0
- truthound/checkpoint/triggers/__init__.py +58 -0
- truthound/checkpoint/triggers/base.py +237 -0
- truthound/checkpoint/triggers/event.py +385 -0
- truthound/checkpoint/triggers/schedule.py +355 -0
- truthound/cli.py +2358 -0
- truthound/cli_modules/__init__.py +124 -0
- truthound/cli_modules/advanced/__init__.py +45 -0
- truthound/cli_modules/advanced/benchmark.py +343 -0
- truthound/cli_modules/advanced/docs.py +225 -0
- truthound/cli_modules/advanced/lineage.py +209 -0
- truthound/cli_modules/advanced/ml.py +320 -0
- truthound/cli_modules/advanced/realtime.py +196 -0
- truthound/cli_modules/checkpoint/__init__.py +46 -0
- truthound/cli_modules/checkpoint/init.py +114 -0
- truthound/cli_modules/checkpoint/list.py +71 -0
- truthound/cli_modules/checkpoint/run.py +159 -0
- truthound/cli_modules/checkpoint/validate.py +67 -0
- truthound/cli_modules/common/__init__.py +71 -0
- truthound/cli_modules/common/errors.py +414 -0
- truthound/cli_modules/common/options.py +419 -0
- truthound/cli_modules/common/output.py +507 -0
- truthound/cli_modules/common/protocol.py +552 -0
- truthound/cli_modules/core/__init__.py +48 -0
- truthound/cli_modules/core/check.py +123 -0
- truthound/cli_modules/core/compare.py +104 -0
- truthound/cli_modules/core/learn.py +57 -0
- truthound/cli_modules/core/mask.py +77 -0
- truthound/cli_modules/core/profile.py +65 -0
- truthound/cli_modules/core/scan.py +61 -0
- truthound/cli_modules/profiler/__init__.py +51 -0
- truthound/cli_modules/profiler/auto_profile.py +175 -0
- truthound/cli_modules/profiler/metadata.py +107 -0
- truthound/cli_modules/profiler/suite.py +283 -0
- truthound/cli_modules/registry.py +431 -0
- truthound/cli_modules/scaffolding/__init__.py +89 -0
- truthound/cli_modules/scaffolding/base.py +631 -0
- truthound/cli_modules/scaffolding/commands.py +545 -0
- truthound/cli_modules/scaffolding/plugins.py +1072 -0
- truthound/cli_modules/scaffolding/reporters.py +594 -0
- truthound/cli_modules/scaffolding/validators.py +1127 -0
- truthound/common/__init__.py +18 -0
- truthound/common/resilience/__init__.py +130 -0
- truthound/common/resilience/bulkhead.py +266 -0
- truthound/common/resilience/circuit_breaker.py +516 -0
- truthound/common/resilience/composite.py +332 -0
- truthound/common/resilience/config.py +292 -0
- truthound/common/resilience/protocols.py +217 -0
- truthound/common/resilience/rate_limiter.py +404 -0
- truthound/common/resilience/retry.py +341 -0
- truthound/datadocs/__init__.py +260 -0
- truthound/datadocs/base.py +571 -0
- truthound/datadocs/builder.py +761 -0
- truthound/datadocs/charts.py +764 -0
- truthound/datadocs/dashboard/__init__.py +63 -0
- truthound/datadocs/dashboard/app.py +576 -0
- truthound/datadocs/dashboard/components.py +584 -0
- truthound/datadocs/dashboard/state.py +240 -0
- truthound/datadocs/engine/__init__.py +46 -0
- truthound/datadocs/engine/context.py +376 -0
- truthound/datadocs/engine/pipeline.py +618 -0
- truthound/datadocs/engine/registry.py +469 -0
- truthound/datadocs/exporters/__init__.py +49 -0
- truthound/datadocs/exporters/base.py +198 -0
- truthound/datadocs/exporters/html.py +178 -0
- truthound/datadocs/exporters/json_exporter.py +253 -0
- truthound/datadocs/exporters/markdown.py +284 -0
- truthound/datadocs/exporters/pdf.py +392 -0
- truthound/datadocs/i18n/__init__.py +86 -0
- truthound/datadocs/i18n/catalog.py +960 -0
- truthound/datadocs/i18n/formatting.py +505 -0
- truthound/datadocs/i18n/loader.py +256 -0
- truthound/datadocs/i18n/plurals.py +378 -0
- truthound/datadocs/renderers/__init__.py +42 -0
- truthound/datadocs/renderers/base.py +401 -0
- truthound/datadocs/renderers/custom.py +342 -0
- truthound/datadocs/renderers/jinja.py +697 -0
- truthound/datadocs/sections.py +736 -0
- truthound/datadocs/styles.py +931 -0
- truthound/datadocs/themes/__init__.py +101 -0
- truthound/datadocs/themes/base.py +336 -0
- truthound/datadocs/themes/default.py +417 -0
- truthound/datadocs/themes/enterprise.py +419 -0
- truthound/datadocs/themes/loader.py +336 -0
- truthound/datadocs/themes.py +301 -0
- truthound/datadocs/transformers/__init__.py +57 -0
- truthound/datadocs/transformers/base.py +268 -0
- truthound/datadocs/transformers/enrichers.py +544 -0
- truthound/datadocs/transformers/filters.py +447 -0
- truthound/datadocs/transformers/i18n.py +468 -0
- truthound/datadocs/versioning/__init__.py +62 -0
- truthound/datadocs/versioning/diff.py +639 -0
- truthound/datadocs/versioning/storage.py +497 -0
- truthound/datadocs/versioning/version.py +358 -0
- truthound/datasources/__init__.py +223 -0
- truthound/datasources/_async_protocols.py +222 -0
- truthound/datasources/_protocols.py +159 -0
- truthound/datasources/adapters.py +428 -0
- truthound/datasources/async_base.py +599 -0
- truthound/datasources/async_factory.py +511 -0
- truthound/datasources/base.py +516 -0
- truthound/datasources/factory.py +433 -0
- truthound/datasources/nosql/__init__.py +47 -0
- truthound/datasources/nosql/base.py +487 -0
- truthound/datasources/nosql/elasticsearch.py +801 -0
- truthound/datasources/nosql/mongodb.py +636 -0
- truthound/datasources/pandas_optimized.py +582 -0
- truthound/datasources/pandas_source.py +216 -0
- truthound/datasources/polars_source.py +395 -0
- truthound/datasources/spark_source.py +479 -0
- truthound/datasources/sql/__init__.py +154 -0
- truthound/datasources/sql/base.py +710 -0
- truthound/datasources/sql/bigquery.py +410 -0
- truthound/datasources/sql/cloud_base.py +199 -0
- truthound/datasources/sql/databricks.py +471 -0
- truthound/datasources/sql/mysql.py +316 -0
- truthound/datasources/sql/oracle.py +427 -0
- truthound/datasources/sql/postgresql.py +321 -0
- truthound/datasources/sql/redshift.py +479 -0
- truthound/datasources/sql/snowflake.py +439 -0
- truthound/datasources/sql/sqlite.py +286 -0
- truthound/datasources/sql/sqlserver.py +437 -0
- truthound/datasources/streaming/__init__.py +47 -0
- truthound/datasources/streaming/base.py +350 -0
- truthound/datasources/streaming/kafka.py +670 -0
- truthound/decorators.py +98 -0
- truthound/docs/__init__.py +69 -0
- truthound/docs/extractor.py +971 -0
- truthound/docs/generator.py +601 -0
- truthound/docs/parser.py +1037 -0
- truthound/docs/renderer.py +999 -0
- truthound/drift/__init__.py +22 -0
- truthound/drift/compare.py +189 -0
- truthound/drift/detectors.py +464 -0
- truthound/drift/report.py +160 -0
- truthound/execution/__init__.py +65 -0
- truthound/execution/_protocols.py +324 -0
- truthound/execution/base.py +576 -0
- truthound/execution/distributed/__init__.py +179 -0
- truthound/execution/distributed/aggregations.py +731 -0
- truthound/execution/distributed/arrow_bridge.py +817 -0
- truthound/execution/distributed/base.py +550 -0
- truthound/execution/distributed/dask_engine.py +976 -0
- truthound/execution/distributed/mixins.py +766 -0
- truthound/execution/distributed/protocols.py +756 -0
- truthound/execution/distributed/ray_engine.py +1127 -0
- truthound/execution/distributed/registry.py +446 -0
- truthound/execution/distributed/spark_engine.py +1011 -0
- truthound/execution/distributed/validator_adapter.py +682 -0
- truthound/execution/pandas_engine.py +401 -0
- truthound/execution/polars_engine.py +497 -0
- truthound/execution/pushdown/__init__.py +230 -0
- truthound/execution/pushdown/ast.py +1550 -0
- truthound/execution/pushdown/builder.py +1550 -0
- truthound/execution/pushdown/dialects.py +1072 -0
- truthound/execution/pushdown/executor.py +829 -0
- truthound/execution/pushdown/optimizer.py +1041 -0
- truthound/execution/sql_engine.py +518 -0
- truthound/infrastructure/__init__.py +189 -0
- truthound/infrastructure/audit.py +1515 -0
- truthound/infrastructure/config.py +1133 -0
- truthound/infrastructure/encryption.py +1132 -0
- truthound/infrastructure/logging.py +1503 -0
- truthound/infrastructure/metrics.py +1220 -0
- truthound/lineage/__init__.py +89 -0
- truthound/lineage/base.py +746 -0
- truthound/lineage/impact_analysis.py +474 -0
- truthound/lineage/integrations/__init__.py +22 -0
- truthound/lineage/integrations/openlineage.py +548 -0
- truthound/lineage/tracker.py +512 -0
- truthound/lineage/visualization/__init__.py +33 -0
- truthound/lineage/visualization/protocols.py +145 -0
- truthound/lineage/visualization/renderers/__init__.py +20 -0
- truthound/lineage/visualization/renderers/cytoscape.py +329 -0
- truthound/lineage/visualization/renderers/d3.py +331 -0
- truthound/lineage/visualization/renderers/graphviz.py +276 -0
- truthound/lineage/visualization/renderers/mermaid.py +308 -0
- truthound/maskers.py +113 -0
- truthound/ml/__init__.py +124 -0
- truthound/ml/anomaly_models/__init__.py +31 -0
- truthound/ml/anomaly_models/ensemble.py +362 -0
- truthound/ml/anomaly_models/isolation_forest.py +444 -0
- truthound/ml/anomaly_models/statistical.py +392 -0
- truthound/ml/base.py +1178 -0
- truthound/ml/drift_detection/__init__.py +26 -0
- truthound/ml/drift_detection/concept.py +381 -0
- truthound/ml/drift_detection/distribution.py +361 -0
- truthound/ml/drift_detection/feature.py +442 -0
- truthound/ml/drift_detection/multivariate.py +495 -0
- truthound/ml/monitoring/__init__.py +88 -0
- truthound/ml/monitoring/alerting/__init__.py +33 -0
- truthound/ml/monitoring/alerting/handlers.py +427 -0
- truthound/ml/monitoring/alerting/rules.py +508 -0
- truthound/ml/monitoring/collectors/__init__.py +19 -0
- truthound/ml/monitoring/collectors/composite.py +105 -0
- truthound/ml/monitoring/collectors/drift.py +324 -0
- truthound/ml/monitoring/collectors/performance.py +179 -0
- truthound/ml/monitoring/collectors/quality.py +369 -0
- truthound/ml/monitoring/monitor.py +536 -0
- truthound/ml/monitoring/protocols.py +451 -0
- truthound/ml/monitoring/stores/__init__.py +15 -0
- truthound/ml/monitoring/stores/memory.py +201 -0
- truthound/ml/monitoring/stores/prometheus.py +296 -0
- truthound/ml/rule_learning/__init__.py +25 -0
- truthound/ml/rule_learning/constraint_miner.py +443 -0
- truthound/ml/rule_learning/pattern_learner.py +499 -0
- truthound/ml/rule_learning/profile_learner.py +462 -0
- truthound/multitenancy/__init__.py +326 -0
- truthound/multitenancy/core.py +852 -0
- truthound/multitenancy/integration.py +597 -0
- truthound/multitenancy/isolation.py +630 -0
- truthound/multitenancy/manager.py +770 -0
- truthound/multitenancy/middleware.py +765 -0
- truthound/multitenancy/quota.py +537 -0
- truthound/multitenancy/resolvers.py +603 -0
- truthound/multitenancy/storage.py +703 -0
- truthound/observability/__init__.py +307 -0
- truthound/observability/context.py +531 -0
- truthound/observability/instrumentation.py +611 -0
- truthound/observability/logging.py +887 -0
- truthound/observability/metrics.py +1157 -0
- truthound/observability/tracing/__init__.py +178 -0
- truthound/observability/tracing/baggage.py +310 -0
- truthound/observability/tracing/config.py +426 -0
- truthound/observability/tracing/exporter.py +787 -0
- truthound/observability/tracing/integration.py +1018 -0
- truthound/observability/tracing/otel/__init__.py +146 -0
- truthound/observability/tracing/otel/adapter.py +982 -0
- truthound/observability/tracing/otel/bridge.py +1177 -0
- truthound/observability/tracing/otel/compat.py +681 -0
- truthound/observability/tracing/otel/config.py +691 -0
- truthound/observability/tracing/otel/detection.py +327 -0
- truthound/observability/tracing/otel/protocols.py +426 -0
- truthound/observability/tracing/processor.py +561 -0
- truthound/observability/tracing/propagator.py +757 -0
- truthound/observability/tracing/provider.py +569 -0
- truthound/observability/tracing/resource.py +515 -0
- truthound/observability/tracing/sampler.py +487 -0
- truthound/observability/tracing/span.py +676 -0
- truthound/plugins/__init__.py +198 -0
- truthound/plugins/base.py +599 -0
- truthound/plugins/cli.py +680 -0
- truthound/plugins/dependencies/__init__.py +42 -0
- truthound/plugins/dependencies/graph.py +422 -0
- truthound/plugins/dependencies/resolver.py +417 -0
- truthound/plugins/discovery.py +379 -0
- truthound/plugins/docs/__init__.py +46 -0
- truthound/plugins/docs/extractor.py +444 -0
- truthound/plugins/docs/renderer.py +499 -0
- truthound/plugins/enterprise_manager.py +877 -0
- truthound/plugins/examples/__init__.py +19 -0
- truthound/plugins/examples/custom_validators.py +317 -0
- truthound/plugins/examples/slack_notifier.py +312 -0
- truthound/plugins/examples/xml_reporter.py +254 -0
- truthound/plugins/hooks.py +558 -0
- truthound/plugins/lifecycle/__init__.py +43 -0
- truthound/plugins/lifecycle/hot_reload.py +402 -0
- truthound/plugins/lifecycle/manager.py +371 -0
- truthound/plugins/manager.py +736 -0
- truthound/plugins/registry.py +338 -0
- truthound/plugins/security/__init__.py +93 -0
- truthound/plugins/security/exceptions.py +332 -0
- truthound/plugins/security/policies.py +348 -0
- truthound/plugins/security/protocols.py +643 -0
- truthound/plugins/security/sandbox/__init__.py +45 -0
- truthound/plugins/security/sandbox/context.py +158 -0
- truthound/plugins/security/sandbox/engines/__init__.py +19 -0
- truthound/plugins/security/sandbox/engines/container.py +379 -0
- truthound/plugins/security/sandbox/engines/noop.py +144 -0
- truthound/plugins/security/sandbox/engines/process.py +336 -0
- truthound/plugins/security/sandbox/factory.py +211 -0
- truthound/plugins/security/signing/__init__.py +57 -0
- truthound/plugins/security/signing/service.py +330 -0
- truthound/plugins/security/signing/trust_store.py +368 -0
- truthound/plugins/security/signing/verifier.py +459 -0
- truthound/plugins/versioning/__init__.py +41 -0
- truthound/plugins/versioning/constraints.py +297 -0
- truthound/plugins/versioning/resolver.py +329 -0
- truthound/profiler/__init__.py +1729 -0
- truthound/profiler/_lazy.py +452 -0
- truthound/profiler/ab_testing/__init__.py +80 -0
- truthound/profiler/ab_testing/analysis.py +449 -0
- truthound/profiler/ab_testing/base.py +257 -0
- truthound/profiler/ab_testing/experiment.py +395 -0
- truthound/profiler/ab_testing/tracking.py +368 -0
- truthound/profiler/auto_threshold.py +1170 -0
- truthound/profiler/base.py +579 -0
- truthound/profiler/cache_patterns.py +911 -0
- truthound/profiler/caching.py +1303 -0
- truthound/profiler/column_profiler.py +712 -0
- truthound/profiler/comparison.py +1007 -0
- truthound/profiler/custom_patterns.py +1170 -0
- truthound/profiler/dashboard/__init__.py +50 -0
- truthound/profiler/dashboard/app.py +476 -0
- truthound/profiler/dashboard/components.py +457 -0
- truthound/profiler/dashboard/config.py +72 -0
- truthound/profiler/distributed/__init__.py +83 -0
- truthound/profiler/distributed/base.py +281 -0
- truthound/profiler/distributed/dask_backend.py +498 -0
- truthound/profiler/distributed/local_backend.py +293 -0
- truthound/profiler/distributed/profiler.py +304 -0
- truthound/profiler/distributed/ray_backend.py +374 -0
- truthound/profiler/distributed/spark_backend.py +375 -0
- truthound/profiler/distributed.py +1366 -0
- truthound/profiler/enterprise_sampling.py +1065 -0
- truthound/profiler/errors.py +488 -0
- truthound/profiler/evolution/__init__.py +91 -0
- truthound/profiler/evolution/alerts.py +426 -0
- truthound/profiler/evolution/changes.py +206 -0
- truthound/profiler/evolution/compatibility.py +365 -0
- truthound/profiler/evolution/detector.py +372 -0
- truthound/profiler/evolution/protocols.py +121 -0
- truthound/profiler/generators/__init__.py +48 -0
- truthound/profiler/generators/base.py +384 -0
- truthound/profiler/generators/ml_rules.py +375 -0
- truthound/profiler/generators/pattern_rules.py +384 -0
- truthound/profiler/generators/schema_rules.py +267 -0
- truthound/profiler/generators/stats_rules.py +324 -0
- truthound/profiler/generators/suite_generator.py +857 -0
- truthound/profiler/i18n.py +1542 -0
- truthound/profiler/incremental.py +554 -0
- truthound/profiler/incremental_validation.py +1710 -0
- truthound/profiler/integration/__init__.py +73 -0
- truthound/profiler/integration/adapters.py +345 -0
- truthound/profiler/integration/context.py +371 -0
- truthound/profiler/integration/executor.py +527 -0
- truthound/profiler/integration/naming.py +75 -0
- truthound/profiler/integration/protocols.py +243 -0
- truthound/profiler/memory.py +1185 -0
- truthound/profiler/migration/__init__.py +60 -0
- truthound/profiler/migration/base.py +345 -0
- truthound/profiler/migration/manager.py +444 -0
- truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
- truthound/profiler/ml/__init__.py +73 -0
- truthound/profiler/ml/base.py +244 -0
- truthound/profiler/ml/classifier.py +507 -0
- truthound/profiler/ml/feature_extraction.py +604 -0
- truthound/profiler/ml/pretrained.py +448 -0
- truthound/profiler/ml_inference.py +1276 -0
- truthound/profiler/native_patterns.py +815 -0
- truthound/profiler/observability.py +1184 -0
- truthound/profiler/process_timeout.py +1566 -0
- truthound/profiler/progress.py +568 -0
- truthound/profiler/progress_callbacks.py +1734 -0
- truthound/profiler/quality.py +1345 -0
- truthound/profiler/resilience.py +1180 -0
- truthound/profiler/sampled_matcher.py +794 -0
- truthound/profiler/sampling.py +1288 -0
- truthound/profiler/scheduling/__init__.py +82 -0
- truthound/profiler/scheduling/protocols.py +214 -0
- truthound/profiler/scheduling/scheduler.py +474 -0
- truthound/profiler/scheduling/storage.py +457 -0
- truthound/profiler/scheduling/triggers.py +449 -0
- truthound/profiler/schema.py +603 -0
- truthound/profiler/streaming.py +685 -0
- truthound/profiler/streaming_patterns.py +1354 -0
- truthound/profiler/suite_cli.py +625 -0
- truthound/profiler/suite_config.py +789 -0
- truthound/profiler/suite_export.py +1268 -0
- truthound/profiler/table_profiler.py +547 -0
- truthound/profiler/timeout.py +565 -0
- truthound/profiler/validation.py +1532 -0
- truthound/profiler/visualization/__init__.py +118 -0
- truthound/profiler/visualization/base.py +346 -0
- truthound/profiler/visualization/generator.py +1259 -0
- truthound/profiler/visualization/plotly_renderer.py +811 -0
- truthound/profiler/visualization/renderers.py +669 -0
- truthound/profiler/visualization/sections.py +540 -0
- truthound/profiler/visualization.py +2122 -0
- truthound/profiler/yaml_validation.py +1151 -0
- truthound/py.typed +0 -0
- truthound/ratelimit/__init__.py +248 -0
- truthound/ratelimit/algorithms.py +1108 -0
- truthound/ratelimit/core.py +573 -0
- truthound/ratelimit/integration.py +532 -0
- truthound/ratelimit/limiter.py +663 -0
- truthound/ratelimit/middleware.py +700 -0
- truthound/ratelimit/policy.py +792 -0
- truthound/ratelimit/storage.py +763 -0
- truthound/rbac/__init__.py +340 -0
- truthound/rbac/core.py +976 -0
- truthound/rbac/integration.py +760 -0
- truthound/rbac/manager.py +1052 -0
- truthound/rbac/middleware.py +842 -0
- truthound/rbac/policy.py +954 -0
- truthound/rbac/storage.py +878 -0
- truthound/realtime/__init__.py +141 -0
- truthound/realtime/adapters/__init__.py +43 -0
- truthound/realtime/adapters/base.py +533 -0
- truthound/realtime/adapters/kafka.py +487 -0
- truthound/realtime/adapters/kinesis.py +479 -0
- truthound/realtime/adapters/mock.py +243 -0
- truthound/realtime/base.py +553 -0
- truthound/realtime/factory.py +382 -0
- truthound/realtime/incremental.py +660 -0
- truthound/realtime/processing/__init__.py +67 -0
- truthound/realtime/processing/exactly_once.py +575 -0
- truthound/realtime/processing/state.py +547 -0
- truthound/realtime/processing/windows.py +647 -0
- truthound/realtime/protocols.py +569 -0
- truthound/realtime/streaming.py +605 -0
- truthound/realtime/testing/__init__.py +32 -0
- truthound/realtime/testing/containers.py +615 -0
- truthound/realtime/testing/fixtures.py +484 -0
- truthound/report.py +280 -0
- truthound/reporters/__init__.py +46 -0
- truthound/reporters/_protocols.py +30 -0
- truthound/reporters/base.py +324 -0
- truthound/reporters/ci/__init__.py +66 -0
- truthound/reporters/ci/azure.py +436 -0
- truthound/reporters/ci/base.py +509 -0
- truthound/reporters/ci/bitbucket.py +567 -0
- truthound/reporters/ci/circleci.py +547 -0
- truthound/reporters/ci/detection.py +364 -0
- truthound/reporters/ci/factory.py +182 -0
- truthound/reporters/ci/github.py +388 -0
- truthound/reporters/ci/gitlab.py +471 -0
- truthound/reporters/ci/jenkins.py +525 -0
- truthound/reporters/console_reporter.py +299 -0
- truthound/reporters/factory.py +211 -0
- truthound/reporters/html_reporter.py +524 -0
- truthound/reporters/json_reporter.py +256 -0
- truthound/reporters/markdown_reporter.py +280 -0
- truthound/reporters/sdk/__init__.py +174 -0
- truthound/reporters/sdk/builder.py +558 -0
- truthound/reporters/sdk/mixins.py +1150 -0
- truthound/reporters/sdk/schema.py +1493 -0
- truthound/reporters/sdk/templates.py +666 -0
- truthound/reporters/sdk/testing.py +968 -0
- truthound/scanners.py +170 -0
- truthound/scheduling/__init__.py +122 -0
- truthound/scheduling/cron.py +1136 -0
- truthound/scheduling/presets.py +212 -0
- truthound/schema.py +275 -0
- truthound/secrets/__init__.py +173 -0
- truthound/secrets/base.py +618 -0
- truthound/secrets/cloud.py +682 -0
- truthound/secrets/integration.py +507 -0
- truthound/secrets/manager.py +633 -0
- truthound/secrets/oidc/__init__.py +172 -0
- truthound/secrets/oidc/base.py +902 -0
- truthound/secrets/oidc/credential_provider.py +623 -0
- truthound/secrets/oidc/exchangers.py +1001 -0
- truthound/secrets/oidc/github/__init__.py +110 -0
- truthound/secrets/oidc/github/claims.py +718 -0
- truthound/secrets/oidc/github/enhanced_provider.py +693 -0
- truthound/secrets/oidc/github/trust_policy.py +742 -0
- truthound/secrets/oidc/github/verification.py +723 -0
- truthound/secrets/oidc/github/workflow.py +691 -0
- truthound/secrets/oidc/providers.py +825 -0
- truthound/secrets/providers.py +506 -0
- truthound/secrets/resolver.py +495 -0
- truthound/stores/__init__.py +177 -0
- truthound/stores/backends/__init__.py +18 -0
- truthound/stores/backends/_protocols.py +340 -0
- truthound/stores/backends/azure_blob.py +530 -0
- truthound/stores/backends/concurrent_filesystem.py +915 -0
- truthound/stores/backends/connection_pool.py +1365 -0
- truthound/stores/backends/database.py +743 -0
- truthound/stores/backends/filesystem.py +538 -0
- truthound/stores/backends/gcs.py +399 -0
- truthound/stores/backends/memory.py +354 -0
- truthound/stores/backends/s3.py +434 -0
- truthound/stores/backpressure/__init__.py +84 -0
- truthound/stores/backpressure/base.py +375 -0
- truthound/stores/backpressure/circuit_breaker.py +434 -0
- truthound/stores/backpressure/monitor.py +376 -0
- truthound/stores/backpressure/strategies.py +677 -0
- truthound/stores/base.py +551 -0
- truthound/stores/batching/__init__.py +65 -0
- truthound/stores/batching/base.py +305 -0
- truthound/stores/batching/buffer.py +370 -0
- truthound/stores/batching/store.py +248 -0
- truthound/stores/batching/writer.py +521 -0
- truthound/stores/caching/__init__.py +60 -0
- truthound/stores/caching/backends.py +684 -0
- truthound/stores/caching/base.py +356 -0
- truthound/stores/caching/store.py +305 -0
- truthound/stores/compression/__init__.py +193 -0
- truthound/stores/compression/adaptive.py +694 -0
- truthound/stores/compression/base.py +514 -0
- truthound/stores/compression/pipeline.py +868 -0
- truthound/stores/compression/providers.py +672 -0
- truthound/stores/compression/streaming.py +832 -0
- truthound/stores/concurrency/__init__.py +81 -0
- truthound/stores/concurrency/atomic.py +556 -0
- truthound/stores/concurrency/index.py +775 -0
- truthound/stores/concurrency/locks.py +576 -0
- truthound/stores/concurrency/manager.py +482 -0
- truthound/stores/encryption/__init__.py +297 -0
- truthound/stores/encryption/base.py +952 -0
- truthound/stores/encryption/keys.py +1191 -0
- truthound/stores/encryption/pipeline.py +903 -0
- truthound/stores/encryption/providers.py +953 -0
- truthound/stores/encryption/streaming.py +950 -0
- truthound/stores/expectations.py +227 -0
- truthound/stores/factory.py +246 -0
- truthound/stores/migration/__init__.py +75 -0
- truthound/stores/migration/base.py +480 -0
- truthound/stores/migration/manager.py +347 -0
- truthound/stores/migration/registry.py +382 -0
- truthound/stores/migration/store.py +559 -0
- truthound/stores/observability/__init__.py +106 -0
- truthound/stores/observability/audit.py +718 -0
- truthound/stores/observability/config.py +270 -0
- truthound/stores/observability/factory.py +208 -0
- truthound/stores/observability/metrics.py +636 -0
- truthound/stores/observability/protocols.py +410 -0
- truthound/stores/observability/store.py +570 -0
- truthound/stores/observability/tracing.py +784 -0
- truthound/stores/replication/__init__.py +76 -0
- truthound/stores/replication/base.py +260 -0
- truthound/stores/replication/monitor.py +269 -0
- truthound/stores/replication/store.py +439 -0
- truthound/stores/replication/syncer.py +391 -0
- truthound/stores/results.py +359 -0
- truthound/stores/retention/__init__.py +77 -0
- truthound/stores/retention/base.py +378 -0
- truthound/stores/retention/policies.py +621 -0
- truthound/stores/retention/scheduler.py +279 -0
- truthound/stores/retention/store.py +526 -0
- truthound/stores/streaming/__init__.py +138 -0
- truthound/stores/streaming/base.py +801 -0
- truthound/stores/streaming/database.py +984 -0
- truthound/stores/streaming/filesystem.py +719 -0
- truthound/stores/streaming/reader.py +629 -0
- truthound/stores/streaming/s3.py +843 -0
- truthound/stores/streaming/writer.py +790 -0
- truthound/stores/tiering/__init__.py +108 -0
- truthound/stores/tiering/base.py +462 -0
- truthound/stores/tiering/manager.py +249 -0
- truthound/stores/tiering/policies.py +692 -0
- truthound/stores/tiering/store.py +526 -0
- truthound/stores/versioning/__init__.py +56 -0
- truthound/stores/versioning/base.py +376 -0
- truthound/stores/versioning/store.py +660 -0
- truthound/stores/versioning/strategies.py +353 -0
- truthound/types.py +56 -0
- truthound/validators/__init__.py +774 -0
- truthound/validators/aggregate/__init__.py +27 -0
- truthound/validators/aggregate/central.py +116 -0
- truthound/validators/aggregate/extremes.py +116 -0
- truthound/validators/aggregate/spread.py +118 -0
- truthound/validators/aggregate/sum.py +64 -0
- truthound/validators/aggregate/type.py +78 -0
- truthound/validators/anomaly/__init__.py +93 -0
- truthound/validators/anomaly/base.py +431 -0
- truthound/validators/anomaly/ml_based.py +1190 -0
- truthound/validators/anomaly/multivariate.py +647 -0
- truthound/validators/anomaly/statistical.py +599 -0
- truthound/validators/base.py +1089 -0
- truthound/validators/business_rule/__init__.py +46 -0
- truthound/validators/business_rule/base.py +147 -0
- truthound/validators/business_rule/checksum.py +509 -0
- truthound/validators/business_rule/financial.py +526 -0
- truthound/validators/cache.py +733 -0
- truthound/validators/completeness/__init__.py +39 -0
- truthound/validators/completeness/conditional.py +73 -0
- truthound/validators/completeness/default.py +98 -0
- truthound/validators/completeness/empty.py +103 -0
- truthound/validators/completeness/nan.py +337 -0
- truthound/validators/completeness/null.py +152 -0
- truthound/validators/cross_table/__init__.py +17 -0
- truthound/validators/cross_table/aggregate.py +333 -0
- truthound/validators/cross_table/row_count.py +122 -0
- truthound/validators/datetime/__init__.py +29 -0
- truthound/validators/datetime/format.py +78 -0
- truthound/validators/datetime/freshness.py +269 -0
- truthound/validators/datetime/order.py +73 -0
- truthound/validators/datetime/parseable.py +185 -0
- truthound/validators/datetime/range.py +202 -0
- truthound/validators/datetime/timezone.py +69 -0
- truthound/validators/distribution/__init__.py +49 -0
- truthound/validators/distribution/distribution.py +128 -0
- truthound/validators/distribution/monotonic.py +119 -0
- truthound/validators/distribution/outlier.py +178 -0
- truthound/validators/distribution/quantile.py +80 -0
- truthound/validators/distribution/range.py +254 -0
- truthound/validators/distribution/set.py +125 -0
- truthound/validators/distribution/statistical.py +459 -0
- truthound/validators/drift/__init__.py +79 -0
- truthound/validators/drift/base.py +427 -0
- truthound/validators/drift/multi_feature.py +401 -0
- truthound/validators/drift/numeric.py +395 -0
- truthound/validators/drift/psi.py +446 -0
- truthound/validators/drift/statistical.py +510 -0
- truthound/validators/enterprise.py +1658 -0
- truthound/validators/geospatial/__init__.py +80 -0
- truthound/validators/geospatial/base.py +97 -0
- truthound/validators/geospatial/boundary.py +238 -0
- truthound/validators/geospatial/coordinate.py +351 -0
- truthound/validators/geospatial/distance.py +399 -0
- truthound/validators/geospatial/polygon.py +665 -0
- truthound/validators/i18n/__init__.py +308 -0
- truthound/validators/i18n/bidi.py +571 -0
- truthound/validators/i18n/catalogs.py +570 -0
- truthound/validators/i18n/dialects.py +763 -0
- truthound/validators/i18n/extended_catalogs.py +549 -0
- truthound/validators/i18n/formatting.py +1434 -0
- truthound/validators/i18n/loader.py +1020 -0
- truthound/validators/i18n/messages.py +521 -0
- truthound/validators/i18n/plural.py +683 -0
- truthound/validators/i18n/protocols.py +855 -0
- truthound/validators/i18n/tms.py +1162 -0
- truthound/validators/localization/__init__.py +53 -0
- truthound/validators/localization/base.py +122 -0
- truthound/validators/localization/chinese.py +362 -0
- truthound/validators/localization/japanese.py +275 -0
- truthound/validators/localization/korean.py +524 -0
- truthound/validators/memory/__init__.py +94 -0
- truthound/validators/memory/approximate_knn.py +506 -0
- truthound/validators/memory/base.py +547 -0
- truthound/validators/memory/sgd_online.py +719 -0
- truthound/validators/memory/streaming_ecdf.py +753 -0
- truthound/validators/ml_feature/__init__.py +54 -0
- truthound/validators/ml_feature/base.py +249 -0
- truthound/validators/ml_feature/correlation.py +299 -0
- truthound/validators/ml_feature/leakage.py +344 -0
- truthound/validators/ml_feature/null_impact.py +270 -0
- truthound/validators/ml_feature/scale.py +264 -0
- truthound/validators/multi_column/__init__.py +89 -0
- truthound/validators/multi_column/arithmetic.py +284 -0
- truthound/validators/multi_column/base.py +231 -0
- truthound/validators/multi_column/comparison.py +273 -0
- truthound/validators/multi_column/consistency.py +312 -0
- truthound/validators/multi_column/statistical.py +299 -0
- truthound/validators/optimization/__init__.py +164 -0
- truthound/validators/optimization/aggregation.py +563 -0
- truthound/validators/optimization/covariance.py +556 -0
- truthound/validators/optimization/geo.py +626 -0
- truthound/validators/optimization/graph.py +587 -0
- truthound/validators/optimization/orchestrator.py +970 -0
- truthound/validators/optimization/profiling.py +1312 -0
- truthound/validators/privacy/__init__.py +223 -0
- truthound/validators/privacy/base.py +635 -0
- truthound/validators/privacy/ccpa.py +670 -0
- truthound/validators/privacy/gdpr.py +728 -0
- truthound/validators/privacy/global_patterns.py +604 -0
- truthound/validators/privacy/plugins.py +867 -0
- truthound/validators/profiling/__init__.py +52 -0
- truthound/validators/profiling/base.py +175 -0
- truthound/validators/profiling/cardinality.py +312 -0
- truthound/validators/profiling/entropy.py +391 -0
- truthound/validators/profiling/frequency.py +455 -0
- truthound/validators/pushdown_support.py +660 -0
- truthound/validators/query/__init__.py +91 -0
- truthound/validators/query/aggregate.py +346 -0
- truthound/validators/query/base.py +246 -0
- truthound/validators/query/column.py +249 -0
- truthound/validators/query/expression.py +274 -0
- truthound/validators/query/result.py +323 -0
- truthound/validators/query/row_count.py +264 -0
- truthound/validators/referential/__init__.py +80 -0
- truthound/validators/referential/base.py +395 -0
- truthound/validators/referential/cascade.py +391 -0
- truthound/validators/referential/circular.py +563 -0
- truthound/validators/referential/foreign_key.py +624 -0
- truthound/validators/referential/orphan.py +485 -0
- truthound/validators/registry.py +112 -0
- truthound/validators/schema/__init__.py +41 -0
- truthound/validators/schema/column_count.py +142 -0
- truthound/validators/schema/column_exists.py +80 -0
- truthound/validators/schema/column_order.py +82 -0
- truthound/validators/schema/column_pair.py +85 -0
- truthound/validators/schema/column_pair_set.py +195 -0
- truthound/validators/schema/column_type.py +94 -0
- truthound/validators/schema/multi_column.py +53 -0
- truthound/validators/schema/multi_column_aggregate.py +175 -0
- truthound/validators/schema/referential.py +274 -0
- truthound/validators/schema/table_schema.py +91 -0
- truthound/validators/schema_validator.py +219 -0
- truthound/validators/sdk/__init__.py +250 -0
- truthound/validators/sdk/builder.py +680 -0
- truthound/validators/sdk/decorators.py +474 -0
- truthound/validators/sdk/enterprise/__init__.py +211 -0
- truthound/validators/sdk/enterprise/docs.py +725 -0
- truthound/validators/sdk/enterprise/fuzzing.py +659 -0
- truthound/validators/sdk/enterprise/licensing.py +709 -0
- truthound/validators/sdk/enterprise/manager.py +543 -0
- truthound/validators/sdk/enterprise/resources.py +628 -0
- truthound/validators/sdk/enterprise/sandbox.py +766 -0
- truthound/validators/sdk/enterprise/signing.py +603 -0
- truthound/validators/sdk/enterprise/templates.py +865 -0
- truthound/validators/sdk/enterprise/versioning.py +659 -0
- truthound/validators/sdk/templates.py +757 -0
- truthound/validators/sdk/testing.py +807 -0
- truthound/validators/security/__init__.py +181 -0
- truthound/validators/security/redos/__init__.py +182 -0
- truthound/validators/security/redos/core.py +861 -0
- truthound/validators/security/redos/cpu_monitor.py +593 -0
- truthound/validators/security/redos/cve_database.py +791 -0
- truthound/validators/security/redos/ml/__init__.py +155 -0
- truthound/validators/security/redos/ml/base.py +785 -0
- truthound/validators/security/redos/ml/datasets.py +618 -0
- truthound/validators/security/redos/ml/features.py +359 -0
- truthound/validators/security/redos/ml/models.py +1000 -0
- truthound/validators/security/redos/ml/predictor.py +507 -0
- truthound/validators/security/redos/ml/storage.py +632 -0
- truthound/validators/security/redos/ml/training.py +571 -0
- truthound/validators/security/redos/ml_analyzer.py +937 -0
- truthound/validators/security/redos/optimizer.py +674 -0
- truthound/validators/security/redos/profiler.py +682 -0
- truthound/validators/security/redos/re2_engine.py +709 -0
- truthound/validators/security/redos.py +886 -0
- truthound/validators/security/sql_security.py +1247 -0
- truthound/validators/streaming/__init__.py +126 -0
- truthound/validators/streaming/base.py +292 -0
- truthound/validators/streaming/completeness.py +210 -0
- truthound/validators/streaming/mixin.py +575 -0
- truthound/validators/streaming/range.py +308 -0
- truthound/validators/streaming/sources.py +846 -0
- truthound/validators/string/__init__.py +57 -0
- truthound/validators/string/casing.py +158 -0
- truthound/validators/string/charset.py +96 -0
- truthound/validators/string/format.py +501 -0
- truthound/validators/string/json.py +77 -0
- truthound/validators/string/json_schema.py +184 -0
- truthound/validators/string/length.py +104 -0
- truthound/validators/string/like_pattern.py +237 -0
- truthound/validators/string/regex.py +202 -0
- truthound/validators/string/regex_extended.py +435 -0
- truthound/validators/table/__init__.py +88 -0
- truthound/validators/table/base.py +78 -0
- truthound/validators/table/column_count.py +198 -0
- truthound/validators/table/freshness.py +362 -0
- truthound/validators/table/row_count.py +251 -0
- truthound/validators/table/schema.py +333 -0
- truthound/validators/table/size.py +285 -0
- truthound/validators/timeout/__init__.py +102 -0
- truthound/validators/timeout/advanced/__init__.py +247 -0
- truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
- truthound/validators/timeout/advanced/prediction.py +773 -0
- truthound/validators/timeout/advanced/priority.py +618 -0
- truthound/validators/timeout/advanced/redis_backend.py +770 -0
- truthound/validators/timeout/advanced/retry.py +721 -0
- truthound/validators/timeout/advanced/sampling.py +788 -0
- truthound/validators/timeout/advanced/sla.py +661 -0
- truthound/validators/timeout/advanced/telemetry.py +804 -0
- truthound/validators/timeout/cascade.py +477 -0
- truthound/validators/timeout/deadline.py +657 -0
- truthound/validators/timeout/degradation.py +525 -0
- truthound/validators/timeout/distributed.py +597 -0
- truthound/validators/timeseries/__init__.py +89 -0
- truthound/validators/timeseries/base.py +326 -0
- truthound/validators/timeseries/completeness.py +617 -0
- truthound/validators/timeseries/gap.py +485 -0
- truthound/validators/timeseries/monotonic.py +310 -0
- truthound/validators/timeseries/seasonality.py +422 -0
- truthound/validators/timeseries/trend.py +510 -0
- truthound/validators/uniqueness/__init__.py +59 -0
- truthound/validators/uniqueness/approximate.py +475 -0
- truthound/validators/uniqueness/distinct_values.py +253 -0
- truthound/validators/uniqueness/duplicate.py +118 -0
- truthound/validators/uniqueness/primary_key.py +140 -0
- truthound/validators/uniqueness/unique.py +191 -0
- truthound/validators/uniqueness/within_record.py +599 -0
- truthound/validators/utils.py +756 -0
- truthound-1.0.8.dist-info/METADATA +474 -0
- truthound-1.0.8.dist-info/RECORD +877 -0
- truthound-1.0.8.dist-info/WHEEL +4 -0
- truthound-1.0.8.dist-info/entry_points.txt +2 -0
- truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
truthound/report.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Report generation for validation results."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from truthound.types import Severity
|
|
10
|
+
from truthound.validators.base import ValidationIssue
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Report:
|
|
15
|
+
"""Validation report containing all issues found."""
|
|
16
|
+
|
|
17
|
+
issues: list[ValidationIssue] = field(default_factory=list)
|
|
18
|
+
source: str = "unknown"
|
|
19
|
+
row_count: int = 0
|
|
20
|
+
column_count: int = 0
|
|
21
|
+
|
|
22
|
+
def __str__(self) -> str:
|
|
23
|
+
"""Return a formatted string representation using Rich."""
|
|
24
|
+
console = Console(force_terminal=True, width=80)
|
|
25
|
+
with console.capture() as capture:
|
|
26
|
+
self._print_to_console(console)
|
|
27
|
+
return capture.get()
|
|
28
|
+
|
|
29
|
+
def _print_to_console(self, console: Console) -> None:
|
|
30
|
+
"""Print the report to a Rich console."""
|
|
31
|
+
console.print()
|
|
32
|
+
console.print("[bold]Truthound Report[/bold]")
|
|
33
|
+
console.print("━" * 52)
|
|
34
|
+
|
|
35
|
+
if not self.issues:
|
|
36
|
+
console.print("[green]✓ No issues found[/green]")
|
|
37
|
+
console.print()
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
table = Table(show_header=True, header_style="bold")
|
|
41
|
+
table.add_column("Column", style="cyan")
|
|
42
|
+
table.add_column("Issue", style="white")
|
|
43
|
+
table.add_column("Count", justify="right")
|
|
44
|
+
table.add_column("Severity", justify="center")
|
|
45
|
+
|
|
46
|
+
# Sort issues by severity (highest first)
|
|
47
|
+
severity_order = {
|
|
48
|
+
Severity.CRITICAL: 0,
|
|
49
|
+
Severity.HIGH: 1,
|
|
50
|
+
Severity.MEDIUM: 2,
|
|
51
|
+
Severity.LOW: 3,
|
|
52
|
+
}
|
|
53
|
+
sorted_issues = sorted(self.issues, key=lambda x: severity_order[x.severity])
|
|
54
|
+
|
|
55
|
+
for issue in sorted_issues:
|
|
56
|
+
severity_style = self._get_severity_style(issue.severity)
|
|
57
|
+
table.add_row(
|
|
58
|
+
issue.column,
|
|
59
|
+
issue.issue_type,
|
|
60
|
+
f"{issue.count:,}",
|
|
61
|
+
f"[{severity_style}]{issue.severity.value}[/{severity_style}]",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
console.print(table)
|
|
65
|
+
console.print()
|
|
66
|
+
|
|
67
|
+
# Summary
|
|
68
|
+
unique_columns = len({i.column for i in self.issues if i.column != "*"})
|
|
69
|
+
console.print(f"Summary: {len(self.issues)} issues found in {unique_columns} columns")
|
|
70
|
+
console.print()
|
|
71
|
+
|
|
72
|
+
def _get_severity_style(self, severity: Severity) -> str:
|
|
73
|
+
"""Get Rich style for severity level."""
|
|
74
|
+
return {
|
|
75
|
+
Severity.CRITICAL: "bold red",
|
|
76
|
+
Severity.HIGH: "red",
|
|
77
|
+
Severity.MEDIUM: "yellow",
|
|
78
|
+
Severity.LOW: "dim",
|
|
79
|
+
}[severity]
|
|
80
|
+
|
|
81
|
+
def print(self) -> None:
|
|
82
|
+
"""Print the report to stdout."""
|
|
83
|
+
console = Console()
|
|
84
|
+
self._print_to_console(console)
|
|
85
|
+
|
|
86
|
+
def to_dict(self) -> dict:
|
|
87
|
+
"""Convert report to dictionary for JSON serialization."""
|
|
88
|
+
return {
|
|
89
|
+
"source": self.source,
|
|
90
|
+
"row_count": self.row_count,
|
|
91
|
+
"column_count": self.column_count,
|
|
92
|
+
"issue_count": len(self.issues),
|
|
93
|
+
"issues": [issue.to_dict() for issue in self.issues],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
def to_json(self, indent: int = 2) -> str:
|
|
97
|
+
"""Convert report to JSON string."""
|
|
98
|
+
return json.dumps(self.to_dict(), indent=indent)
|
|
99
|
+
|
|
100
|
+
def filter_by_severity(self, min_severity: Severity) -> "Report":
|
|
101
|
+
"""Return a new report with only issues at or above the given severity.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
min_severity: Minimum severity level to include.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
New Report with filtered issues.
|
|
108
|
+
"""
|
|
109
|
+
filtered_issues = [i for i in self.issues if i.severity >= min_severity]
|
|
110
|
+
return Report(
|
|
111
|
+
issues=filtered_issues,
|
|
112
|
+
source=self.source,
|
|
113
|
+
row_count=self.row_count,
|
|
114
|
+
column_count=self.column_count,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def has_issues(self) -> bool:
|
|
119
|
+
"""Check if the report contains any issues."""
|
|
120
|
+
return len(self.issues) > 0
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def has_critical(self) -> bool:
|
|
124
|
+
"""Check if the report contains critical issues."""
|
|
125
|
+
return any(i.severity == Severity.CRITICAL for i in self.issues)
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def has_high(self) -> bool:
|
|
129
|
+
"""Check if the report contains high severity issues."""
|
|
130
|
+
return any(i.severity >= Severity.HIGH for i in self.issues)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class PIIReport:
|
|
135
|
+
"""Report for PII scan results."""
|
|
136
|
+
|
|
137
|
+
findings: list[dict] = field(default_factory=list)
|
|
138
|
+
source: str = "unknown"
|
|
139
|
+
row_count: int = 0
|
|
140
|
+
|
|
141
|
+
def __str__(self) -> str:
|
|
142
|
+
"""Return a formatted string representation using Rich."""
|
|
143
|
+
console = Console(force_terminal=True, width=80)
|
|
144
|
+
with console.capture() as capture:
|
|
145
|
+
self._print_to_console(console)
|
|
146
|
+
return capture.get()
|
|
147
|
+
|
|
148
|
+
def _print_to_console(self, console: Console) -> None:
|
|
149
|
+
"""Print the PII report to a Rich console."""
|
|
150
|
+
console.print()
|
|
151
|
+
console.print("[bold]Truthound PII Scan[/bold]")
|
|
152
|
+
console.print("━" * 52)
|
|
153
|
+
|
|
154
|
+
if not self.findings:
|
|
155
|
+
console.print("[green]✓ No PII detected[/green]")
|
|
156
|
+
console.print()
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
table = Table(show_header=True, header_style="bold")
|
|
160
|
+
table.add_column("Column", style="cyan")
|
|
161
|
+
table.add_column("PII Type", style="white")
|
|
162
|
+
table.add_column("Count", justify="right")
|
|
163
|
+
table.add_column("Confidence", justify="center")
|
|
164
|
+
|
|
165
|
+
for finding in self.findings:
|
|
166
|
+
table.add_row(
|
|
167
|
+
finding["column"],
|
|
168
|
+
finding["pii_type"],
|
|
169
|
+
f"{finding['count']:,}",
|
|
170
|
+
f"{finding['confidence']}%",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
console.print(table)
|
|
174
|
+
console.print()
|
|
175
|
+
|
|
176
|
+
console.print(f"[yellow]Warning: Found {len(self.findings)} columns with potential PII[/yellow]")
|
|
177
|
+
console.print()
|
|
178
|
+
|
|
179
|
+
def print(self) -> None:
|
|
180
|
+
"""Print the report to stdout."""
|
|
181
|
+
console = Console()
|
|
182
|
+
self._print_to_console(console)
|
|
183
|
+
|
|
184
|
+
def to_dict(self) -> dict:
|
|
185
|
+
"""Convert report to dictionary."""
|
|
186
|
+
return {
|
|
187
|
+
"source": self.source,
|
|
188
|
+
"row_count": self.row_count,
|
|
189
|
+
"finding_count": len(self.findings),
|
|
190
|
+
"findings": self.findings,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
def to_json(self, indent: int = 2) -> str:
|
|
194
|
+
"""Convert report to JSON string."""
|
|
195
|
+
return json.dumps(self.to_dict(), indent=indent)
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def has_pii(self) -> bool:
|
|
199
|
+
"""Check if PII was detected."""
|
|
200
|
+
return len(self.findings) > 0
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@dataclass
|
|
204
|
+
class ProfileReport:
|
|
205
|
+
"""Report for dataset profiling."""
|
|
206
|
+
|
|
207
|
+
source: str = "unknown"
|
|
208
|
+
row_count: int = 0
|
|
209
|
+
column_count: int = 0
|
|
210
|
+
size_bytes: int = 0
|
|
211
|
+
columns: list[dict] = field(default_factory=list)
|
|
212
|
+
|
|
213
|
+
def __str__(self) -> str:
|
|
214
|
+
"""Return a formatted string representation using Rich."""
|
|
215
|
+
console = Console(force_terminal=True, width=80)
|
|
216
|
+
with console.capture() as capture:
|
|
217
|
+
self._print_to_console(console)
|
|
218
|
+
return capture.get()
|
|
219
|
+
|
|
220
|
+
def _print_to_console(self, console: Console) -> None:
|
|
221
|
+
"""Print the profile report to a Rich console."""
|
|
222
|
+
console.print()
|
|
223
|
+
console.print("[bold]Truthound Profile[/bold]")
|
|
224
|
+
console.print("━" * 52)
|
|
225
|
+
|
|
226
|
+
size_str = self._format_size(self.size_bytes)
|
|
227
|
+
console.print(f"Dataset: {self.source}")
|
|
228
|
+
console.print(f"Rows: {self.row_count:,} | Columns: {self.column_count} | Size: {size_str}")
|
|
229
|
+
console.print()
|
|
230
|
+
|
|
231
|
+
if not self.columns:
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
table = Table(show_header=True, header_style="bold")
|
|
235
|
+
table.add_column("Column", style="cyan")
|
|
236
|
+
table.add_column("Type", style="white")
|
|
237
|
+
table.add_column("Nulls", justify="right")
|
|
238
|
+
table.add_column("Unique", justify="right")
|
|
239
|
+
table.add_column("Min", justify="right")
|
|
240
|
+
table.add_column("Max", justify="right")
|
|
241
|
+
|
|
242
|
+
for col in self.columns:
|
|
243
|
+
table.add_row(
|
|
244
|
+
col["name"],
|
|
245
|
+
col["dtype"],
|
|
246
|
+
col["null_pct"],
|
|
247
|
+
col["unique_pct"],
|
|
248
|
+
col.get("min", "-"),
|
|
249
|
+
col.get("max", "-"),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
console.print(table)
|
|
253
|
+
console.print()
|
|
254
|
+
|
|
255
|
+
def _format_size(self, size_bytes: int) -> str:
|
|
256
|
+
"""Format byte size to human readable string."""
|
|
257
|
+
for unit in ["B", "KB", "MB", "GB"]:
|
|
258
|
+
if size_bytes < 1024:
|
|
259
|
+
return f"{size_bytes:.1f} {unit}"
|
|
260
|
+
size_bytes /= 1024
|
|
261
|
+
return f"{size_bytes:.1f} TB"
|
|
262
|
+
|
|
263
|
+
def print(self) -> None:
|
|
264
|
+
"""Print the report to stdout."""
|
|
265
|
+
console = Console()
|
|
266
|
+
self._print_to_console(console)
|
|
267
|
+
|
|
268
|
+
def to_dict(self) -> dict:
|
|
269
|
+
"""Convert report to dictionary."""
|
|
270
|
+
return {
|
|
271
|
+
"source": self.source,
|
|
272
|
+
"row_count": self.row_count,
|
|
273
|
+
"column_count": self.column_count,
|
|
274
|
+
"size_bytes": self.size_bytes,
|
|
275
|
+
"columns": self.columns,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
def to_json(self, indent: int = 2) -> str:
|
|
279
|
+
"""Convert report to JSON string."""
|
|
280
|
+
return json.dumps(self.to_dict(), indent=indent)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Reporters module for generating validation reports.
|
|
2
|
+
|
|
3
|
+
This module provides a unified interface for generating reports in various
|
|
4
|
+
formats (JSON, HTML, Console, CI/CD platforms) from validation results.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from truthound.reporters import get_reporter
|
|
8
|
+
>>>
|
|
9
|
+
>>> # Use factory function
|
|
10
|
+
>>> reporter = get_reporter("json", output_path="report.json")
|
|
11
|
+
>>> reporter.report(validation_result)
|
|
12
|
+
>>>
|
|
13
|
+
>>> # CI reporter (auto-detect platform)
|
|
14
|
+
>>> reporter = get_reporter("ci")
|
|
15
|
+
>>> exit_code = reporter.report_to_ci(validation_result)
|
|
16
|
+
>>>
|
|
17
|
+
>>> # Specific CI platform
|
|
18
|
+
>>> reporter = get_reporter("github")
|
|
19
|
+
>>> reporter.report_to_ci(validation_result)
|
|
20
|
+
|
|
21
|
+
Supported CI Platforms:
|
|
22
|
+
- GitHub Actions: "github"
|
|
23
|
+
- GitLab CI: "gitlab"
|
|
24
|
+
- Jenkins: "jenkins"
|
|
25
|
+
- Azure DevOps: "azure"
|
|
26
|
+
- CircleCI: "circleci"
|
|
27
|
+
- Bitbucket Pipelines: "bitbucket"
|
|
28
|
+
- Auto-detect: "ci"
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from truthound.reporters.base import (
|
|
32
|
+
BaseReporter,
|
|
33
|
+
ReporterConfig,
|
|
34
|
+
ReporterError,
|
|
35
|
+
)
|
|
36
|
+
from truthound.reporters.factory import get_reporter, register_reporter
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
# Base classes
|
|
40
|
+
"BaseReporter",
|
|
41
|
+
"ReporterConfig",
|
|
42
|
+
"ReporterError",
|
|
43
|
+
# Factory functions
|
|
44
|
+
"get_reporter",
|
|
45
|
+
"register_reporter",
|
|
46
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Protocol definitions for reporter dependencies.
|
|
2
|
+
|
|
3
|
+
This module defines structural typing protocols for external library clients,
|
|
4
|
+
allowing type-safe code without requiring type stubs packages.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class Jinja2TemplateProtocol(Protocol):
|
|
14
|
+
"""Protocol for Jinja2 Template."""
|
|
15
|
+
|
|
16
|
+
def render(self, **context: Any) -> str:
|
|
17
|
+
"""Render the template with given context."""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@runtime_checkable
|
|
22
|
+
class Jinja2EnvironmentProtocol(Protocol):
|
|
23
|
+
"""Protocol for Jinja2 Environment.
|
|
24
|
+
|
|
25
|
+
Defines the minimal interface used by HTMLReporter.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def get_template(self, name: str) -> Jinja2TemplateProtocol:
|
|
29
|
+
"""Get a template by name."""
|
|
30
|
+
...
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""Base classes and interfaces for reporters.
|
|
2
|
+
|
|
3
|
+
This module defines the abstract base classes and protocols that all reporter
|
|
4
|
+
implementations must follow. Reporters are responsible for transforming
|
|
5
|
+
validation results into human-readable or machine-parseable formats.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from truthound.stores.results import ValidationResult
|
|
18
|
+
from truthound.report import Report
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# =============================================================================
|
|
22
|
+
# Exceptions
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ReporterError(Exception):
|
|
27
|
+
"""Base exception for all reporter-related errors."""
|
|
28
|
+
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RenderError(ReporterError):
|
|
33
|
+
"""Raised when rendering a report fails."""
|
|
34
|
+
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class WriteError(ReporterError):
|
|
39
|
+
"""Raised when writing a report to file fails."""
|
|
40
|
+
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# =============================================================================
|
|
45
|
+
# Configuration
|
|
46
|
+
# =============================================================================
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class ReporterConfig:
|
|
51
|
+
"""Base configuration for all reporters.
|
|
52
|
+
|
|
53
|
+
Subclasses can extend this with format-specific options.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
output_path: Optional path to write the report to.
|
|
57
|
+
title: Title for the report.
|
|
58
|
+
include_metadata: Whether to include metadata in the report.
|
|
59
|
+
include_statistics: Whether to include statistics in the report.
|
|
60
|
+
include_details: Whether to include detailed issue information.
|
|
61
|
+
timestamp_format: Format string for timestamps.
|
|
62
|
+
max_sample_values: Maximum number of sample values to include.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
output_path: str | Path | None = None
|
|
66
|
+
title: str = "Truthound Validation Report"
|
|
67
|
+
include_metadata: bool = True
|
|
68
|
+
include_statistics: bool = True
|
|
69
|
+
include_details: bool = True
|
|
70
|
+
timestamp_format: str = "%Y-%m-%d %H:%M:%S"
|
|
71
|
+
max_sample_values: int = 5
|
|
72
|
+
|
|
73
|
+
def get_output_path(self) -> Path | None:
|
|
74
|
+
"""Get the output path as a Path object."""
|
|
75
|
+
if self.output_path is None:
|
|
76
|
+
return None
|
|
77
|
+
return Path(self.output_path)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# =============================================================================
|
|
81
|
+
# Type Variables
|
|
82
|
+
# =============================================================================
|
|
83
|
+
|
|
84
|
+
ConfigT = TypeVar("ConfigT", bound=ReporterConfig)
|
|
85
|
+
InputT = TypeVar("InputT")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# =============================================================================
|
|
89
|
+
# Abstract Base Reporter
|
|
90
|
+
# =============================================================================
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class BaseReporter(ABC, Generic[ConfigT, InputT]):
|
|
94
|
+
"""Abstract base class for all reporters.
|
|
95
|
+
|
|
96
|
+
Reporters transform validation results into various output formats.
|
|
97
|
+
They can render to strings or write directly to files.
|
|
98
|
+
|
|
99
|
+
Type Parameters:
|
|
100
|
+
ConfigT: The configuration type for this reporter.
|
|
101
|
+
InputT: The input type this reporter accepts.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
>>> class MyReporter(BaseReporter[MyConfig, ValidationResult]):
|
|
105
|
+
... def render(self, result: ValidationResult) -> str:
|
|
106
|
+
... return f"Status: {result.status}"
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
# Class-level attributes
|
|
110
|
+
name: str = "base"
|
|
111
|
+
file_extension: str = ".txt"
|
|
112
|
+
content_type: str = "text/plain"
|
|
113
|
+
|
|
114
|
+
def __init__(self, config: ConfigT | None = None, **kwargs: Any) -> None:
|
|
115
|
+
"""Initialize the reporter with optional configuration.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
config: Reporter configuration. If None, uses default configuration.
|
|
119
|
+
**kwargs: Additional configuration options to override.
|
|
120
|
+
"""
|
|
121
|
+
self._config = config or self._default_config()
|
|
122
|
+
|
|
123
|
+
# Apply kwargs to config
|
|
124
|
+
for key, value in kwargs.items():
|
|
125
|
+
if hasattr(self._config, key):
|
|
126
|
+
setattr(self._config, key, value)
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
@abstractmethod
|
|
130
|
+
def _default_config(cls) -> ConfigT:
|
|
131
|
+
"""Create default configuration for this reporter type."""
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def config(self) -> ConfigT:
|
|
136
|
+
"""Get the reporter configuration."""
|
|
137
|
+
return self._config
|
|
138
|
+
|
|
139
|
+
# -------------------------------------------------------------------------
|
|
140
|
+
# Abstract Methods
|
|
141
|
+
# -------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
@abstractmethod
|
|
144
|
+
def render(self, data: InputT) -> str:
|
|
145
|
+
"""Render the input data to a string.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
data: The data to render.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
The rendered report as a string.
|
|
152
|
+
|
|
153
|
+
Raises:
|
|
154
|
+
RenderError: If rendering fails.
|
|
155
|
+
"""
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
# -------------------------------------------------------------------------
|
|
159
|
+
# Concrete Methods
|
|
160
|
+
# -------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
def render_to_bytes(self, data: InputT, encoding: str = "utf-8") -> bytes:
|
|
163
|
+
"""Render the input data to bytes.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
data: The data to render.
|
|
167
|
+
encoding: The encoding to use.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The rendered report as bytes.
|
|
171
|
+
"""
|
|
172
|
+
return self.render(data).encode(encoding)
|
|
173
|
+
|
|
174
|
+
def write(self, data: InputT, path: str | Path | None = None) -> Path:
|
|
175
|
+
"""Write the rendered report to a file.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
data: The data to render.
|
|
179
|
+
path: Optional path to write to. Uses config.output_path if not specified.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
The path where the report was written.
|
|
183
|
+
|
|
184
|
+
Raises:
|
|
185
|
+
WriteError: If no path is specified and config.output_path is None.
|
|
186
|
+
WriteError: If writing fails.
|
|
187
|
+
"""
|
|
188
|
+
output_path = Path(path) if path else self._config.get_output_path()
|
|
189
|
+
|
|
190
|
+
if output_path is None:
|
|
191
|
+
raise WriteError(
|
|
192
|
+
"No output path specified. Either pass a path argument "
|
|
193
|
+
"or set output_path in the reporter configuration."
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
# Create parent directories if needed
|
|
198
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
199
|
+
|
|
200
|
+
# Render and write
|
|
201
|
+
content = self.render(data)
|
|
202
|
+
output_path.write_text(content, encoding="utf-8")
|
|
203
|
+
|
|
204
|
+
return output_path
|
|
205
|
+
|
|
206
|
+
except (OSError, IOError) as e:
|
|
207
|
+
raise WriteError(f"Failed to write report to {output_path}: {e}")
|
|
208
|
+
|
|
209
|
+
def report(self, data: InputT, path: str | Path | None = None) -> str:
|
|
210
|
+
"""Generate a report, optionally writing to file.
|
|
211
|
+
|
|
212
|
+
This is a convenience method that renders the report and optionally
|
|
213
|
+
writes it to a file if a path is specified.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
data: The data to render.
|
|
217
|
+
path: Optional path to write to.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
The rendered report as a string.
|
|
221
|
+
"""
|
|
222
|
+
content = self.render(data)
|
|
223
|
+
|
|
224
|
+
output_path = Path(path) if path else self._config.get_output_path()
|
|
225
|
+
if output_path:
|
|
226
|
+
self.write(data, output_path)
|
|
227
|
+
|
|
228
|
+
return content
|
|
229
|
+
|
|
230
|
+
def generate_filename(
|
|
231
|
+
self,
|
|
232
|
+
data: InputT,
|
|
233
|
+
timestamp: bool = True,
|
|
234
|
+
) -> str:
|
|
235
|
+
"""Generate a filename for the report.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
data: The data being reported (used for naming).
|
|
239
|
+
timestamp: Whether to include a timestamp in the filename.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
A generated filename with the appropriate extension.
|
|
243
|
+
"""
|
|
244
|
+
base_name = self.name
|
|
245
|
+
|
|
246
|
+
if timestamp:
|
|
247
|
+
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
248
|
+
base_name = f"{base_name}_{ts}"
|
|
249
|
+
|
|
250
|
+
return f"{base_name}{self.file_extension}"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# =============================================================================
|
|
254
|
+
# Specialized Reporter Types
|
|
255
|
+
# =============================================================================
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class ValidationReporter(BaseReporter[ConfigT, "ValidationResult"], Generic[ConfigT]):
|
|
259
|
+
"""Reporter specialized for ValidationResult objects.
|
|
260
|
+
|
|
261
|
+
Provides additional helper methods for working with validation results.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
def get_severity_counts(self, result: "ValidationResult") -> dict[str, int]:
|
|
265
|
+
"""Get counts of issues by severity.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
result: The validation result.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Dictionary mapping severity to count.
|
|
272
|
+
"""
|
|
273
|
+
counts: dict[str, int] = {
|
|
274
|
+
"critical": 0,
|
|
275
|
+
"high": 0,
|
|
276
|
+
"medium": 0,
|
|
277
|
+
"low": 0,
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
for validator_result in result.results:
|
|
281
|
+
if not validator_result.success and validator_result.severity:
|
|
282
|
+
severity = validator_result.severity.lower()
|
|
283
|
+
if severity in counts:
|
|
284
|
+
counts[severity] += 1
|
|
285
|
+
|
|
286
|
+
return counts
|
|
287
|
+
|
|
288
|
+
def get_column_issues(
|
|
289
|
+
self,
|
|
290
|
+
result: "ValidationResult",
|
|
291
|
+
) -> dict[str, list[dict[str, Any]]]:
|
|
292
|
+
"""Get issues grouped by column.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
result: The validation result.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
Dictionary mapping column names to lists of issues.
|
|
299
|
+
"""
|
|
300
|
+
column_issues: dict[str, list[dict[str, Any]]] = {}
|
|
301
|
+
|
|
302
|
+
for validator_result in result.results:
|
|
303
|
+
if not validator_result.success:
|
|
304
|
+
column = validator_result.column or "_table_"
|
|
305
|
+
if column not in column_issues:
|
|
306
|
+
column_issues[column] = []
|
|
307
|
+
column_issues[column].append({
|
|
308
|
+
"validator": validator_result.validator_name,
|
|
309
|
+
"issue_type": validator_result.issue_type,
|
|
310
|
+
"count": validator_result.count,
|
|
311
|
+
"severity": validator_result.severity,
|
|
312
|
+
"message": validator_result.message,
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
return column_issues
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class ReportReporter(BaseReporter[ConfigT, "Report"], Generic[ConfigT]):
|
|
319
|
+
"""Reporter specialized for Report objects (legacy format).
|
|
320
|
+
|
|
321
|
+
Provides compatibility with the existing Report class.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
pass
|