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
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
"""Plugin scaffold generator.
|
|
2
|
+
|
|
3
|
+
This module provides scaffolding for creating Truthound plugins with
|
|
4
|
+
various template variants:
|
|
5
|
+
- validator: Plugin that provides custom validators
|
|
6
|
+
- reporter: Plugin that provides custom reporters
|
|
7
|
+
- hook: Plugin that provides event hooks
|
|
8
|
+
- datasource: Plugin that provides data source connectors
|
|
9
|
+
- action: Plugin that provides checkpoint actions
|
|
10
|
+
- full: Full-featured plugin with all components
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any, ClassVar
|
|
16
|
+
|
|
17
|
+
from truthound.cli_modules.scaffolding.base import (
|
|
18
|
+
BaseScaffold,
|
|
19
|
+
ScaffoldConfig,
|
|
20
|
+
ScaffoldResult,
|
|
21
|
+
register_scaffold,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@register_scaffold(
|
|
26
|
+
name="plugin",
|
|
27
|
+
description="Generate a Truthound plugin",
|
|
28
|
+
aliases=("plug", "p"),
|
|
29
|
+
)
|
|
30
|
+
class PluginScaffold(BaseScaffold):
|
|
31
|
+
"""Scaffold generator for plugins.
|
|
32
|
+
|
|
33
|
+
Creates complete plugin packages with pyproject.toml, README, and tests.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
name: ClassVar[str] = "plugin"
|
|
37
|
+
description: ClassVar[str] = "Generate a Truthound plugin"
|
|
38
|
+
aliases: ClassVar[tuple[str, ...]] = ("plug", "p")
|
|
39
|
+
|
|
40
|
+
TEMPLATE_VARIANTS: ClassVar[tuple[str, ...]] = (
|
|
41
|
+
"validator",
|
|
42
|
+
"reporter",
|
|
43
|
+
"hook",
|
|
44
|
+
"datasource",
|
|
45
|
+
"action",
|
|
46
|
+
"full",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def get_options(self) -> dict[str, Any]:
|
|
50
|
+
"""Get plugin-specific options."""
|
|
51
|
+
return {
|
|
52
|
+
"plugin_type": {
|
|
53
|
+
"type": "str",
|
|
54
|
+
"default": "validator",
|
|
55
|
+
"description": "Type of plugin (validator, reporter, hook, datasource, action)",
|
|
56
|
+
"choices": ["validator", "reporter", "hook", "datasource", "action"],
|
|
57
|
+
},
|
|
58
|
+
"min_truthound_version": {
|
|
59
|
+
"type": "str",
|
|
60
|
+
"default": "0.1.0",
|
|
61
|
+
"description": "Minimum Truthound version required",
|
|
62
|
+
},
|
|
63
|
+
"python_version": {
|
|
64
|
+
"type": "str",
|
|
65
|
+
"default": "3.10",
|
|
66
|
+
"description": "Minimum Python version",
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def _generate_files(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
|
|
71
|
+
"""Generate plugin files based on variant."""
|
|
72
|
+
variant = config.template_variant
|
|
73
|
+
pkg_name = config.name.replace("-", "_")
|
|
74
|
+
|
|
75
|
+
# Generate plugin package structure
|
|
76
|
+
self._generate_pyproject(config, result)
|
|
77
|
+
self._generate_readme(config, result)
|
|
78
|
+
self._generate_package_init(config, result, pkg_name)
|
|
79
|
+
|
|
80
|
+
# Generate plugin implementation based on type
|
|
81
|
+
if variant == "validator":
|
|
82
|
+
self._generate_validator_plugin(config, result, pkg_name)
|
|
83
|
+
elif variant == "reporter":
|
|
84
|
+
self._generate_reporter_plugin(config, result, pkg_name)
|
|
85
|
+
elif variant == "hook":
|
|
86
|
+
self._generate_hook_plugin(config, result, pkg_name)
|
|
87
|
+
elif variant == "datasource":
|
|
88
|
+
self._generate_datasource_plugin(config, result, pkg_name)
|
|
89
|
+
elif variant == "action":
|
|
90
|
+
self._generate_action_plugin(config, result, pkg_name)
|
|
91
|
+
elif variant == "full":
|
|
92
|
+
self._generate_full_plugin(config, result, pkg_name)
|
|
93
|
+
else:
|
|
94
|
+
self._generate_validator_plugin(config, result, pkg_name)
|
|
95
|
+
|
|
96
|
+
# Generate tests
|
|
97
|
+
if config.include_tests:
|
|
98
|
+
self._generate_tests(config, result, pkg_name)
|
|
99
|
+
|
|
100
|
+
def _generate_pyproject(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
|
|
101
|
+
"""Generate pyproject.toml."""
|
|
102
|
+
pkg_name = config.name.replace("-", "_")
|
|
103
|
+
min_python = config.extra.get("python_version", "3.10")
|
|
104
|
+
min_truthound = config.extra.get("min_truthound_version", "0.1.0")
|
|
105
|
+
|
|
106
|
+
content = f'''[build-system]
|
|
107
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
108
|
+
build-backend = "setuptools.build_meta"
|
|
109
|
+
|
|
110
|
+
[project]
|
|
111
|
+
name = "truthound-plugin-{config.name}"
|
|
112
|
+
version = "{config.version}"
|
|
113
|
+
description = "{config.description or f'A {config.template_variant} plugin for Truthound'}"
|
|
114
|
+
readme = "README.md"
|
|
115
|
+
license = {{text = "{config.license_type}"}}
|
|
116
|
+
authors = [
|
|
117
|
+
{{name = "{config.author or 'Your Name'}", email = "your@email.com"}}
|
|
118
|
+
]
|
|
119
|
+
requires-python = ">={min_python}"
|
|
120
|
+
dependencies = [
|
|
121
|
+
"truthound>={min_truthound}",
|
|
122
|
+
]
|
|
123
|
+
classifiers = [
|
|
124
|
+
"Development Status :: 3 - Alpha",
|
|
125
|
+
"Intended Audience :: Developers",
|
|
126
|
+
"License :: OSI Approved :: MIT License",
|
|
127
|
+
"Programming Language :: Python :: 3",
|
|
128
|
+
"Programming Language :: Python :: 3.10",
|
|
129
|
+
"Programming Language :: Python :: 3.11",
|
|
130
|
+
"Programming Language :: Python :: 3.12",
|
|
131
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
132
|
+
]
|
|
133
|
+
keywords = ["truthound", "data-quality", "plugin"]
|
|
134
|
+
|
|
135
|
+
[project.optional-dependencies]
|
|
136
|
+
dev = [
|
|
137
|
+
"pytest>=7.0.0",
|
|
138
|
+
"pytest-cov>=4.0.0",
|
|
139
|
+
"ruff>=0.1.0",
|
|
140
|
+
"mypy>=1.0.0",
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
[project.urls]
|
|
144
|
+
Homepage = "https://github.com/{config.author or 'yourusername'}/truthound-plugin-{config.name}"
|
|
145
|
+
Repository = "https://github.com/{config.author or 'yourusername'}/truthound-plugin-{config.name}"
|
|
146
|
+
Issues = "https://github.com/{config.author or 'yourusername'}/truthound-plugin-{config.name}/issues"
|
|
147
|
+
|
|
148
|
+
[project.entry-points."truthound.plugins"]
|
|
149
|
+
{config.name} = "{pkg_name}:{config.class_name}Plugin"
|
|
150
|
+
|
|
151
|
+
[tool.setuptools.packages.find]
|
|
152
|
+
where = ["."]
|
|
153
|
+
|
|
154
|
+
[tool.ruff]
|
|
155
|
+
line-length = 100
|
|
156
|
+
target-version = "py310"
|
|
157
|
+
|
|
158
|
+
[tool.ruff.lint]
|
|
159
|
+
select = ["E", "F", "W", "I", "N", "B", "A", "C4", "SIM"]
|
|
160
|
+
ignore = ["E501"]
|
|
161
|
+
|
|
162
|
+
[tool.mypy]
|
|
163
|
+
python_version = "{min_python}"
|
|
164
|
+
warn_return_any = true
|
|
165
|
+
warn_unused_configs = true
|
|
166
|
+
disallow_untyped_defs = true
|
|
167
|
+
|
|
168
|
+
[tool.pytest.ini_options]
|
|
169
|
+
testpaths = ["tests"]
|
|
170
|
+
python_files = ["test_*.py"]
|
|
171
|
+
python_functions = ["test_*"]
|
|
172
|
+
addopts = "-v --tb=short"
|
|
173
|
+
'''
|
|
174
|
+
result.add_file("pyproject.toml", content)
|
|
175
|
+
|
|
176
|
+
def _generate_readme(self, config: ScaffoldConfig, result: ScaffoldResult) -> None:
|
|
177
|
+
"""Generate README.md."""
|
|
178
|
+
content = f'''# Truthound Plugin: {config.name}
|
|
179
|
+
|
|
180
|
+
{config.description or f'A {config.template_variant} plugin for Truthound.'}
|
|
181
|
+
|
|
182
|
+
## Installation
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pip install truthound-plugin-{config.name}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Or install from source:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
git clone https://github.com/{config.author or 'yourusername'}/truthound-plugin-{config.name}
|
|
192
|
+
cd truthound-plugin-{config.name}
|
|
193
|
+
pip install -e .
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Usage
|
|
197
|
+
|
|
198
|
+
The plugin is automatically discovered by Truthound:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from truthound.plugins import get_plugin_manager
|
|
202
|
+
|
|
203
|
+
# Get plugin manager
|
|
204
|
+
manager = get_plugin_manager()
|
|
205
|
+
|
|
206
|
+
# Discover and load plugins
|
|
207
|
+
manager.discover_plugins()
|
|
208
|
+
manager.load_plugin("{config.name}")
|
|
209
|
+
|
|
210
|
+
# List loaded plugins
|
|
211
|
+
print(manager.registry.list_names())
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
### Setup
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Clone the repository
|
|
220
|
+
git clone https://github.com/{config.author or 'yourusername'}/truthound-plugin-{config.name}
|
|
221
|
+
cd truthound-plugin-{config.name}
|
|
222
|
+
|
|
223
|
+
# Install in development mode
|
|
224
|
+
pip install -e ".[dev]"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Testing
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
pytest
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Linting
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
ruff check .
|
|
237
|
+
ruff format .
|
|
238
|
+
mypy .
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
{config.license_type}
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
*Version: {config.version}*
|
|
248
|
+
*Author: {config.author or 'Unknown'}*
|
|
249
|
+
'''
|
|
250
|
+
result.add_file("README.md", content)
|
|
251
|
+
|
|
252
|
+
def _generate_package_init(
|
|
253
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Generate package __init__.py."""
|
|
256
|
+
content = f'''"""Truthound plugin: {config.name}
|
|
257
|
+
|
|
258
|
+
{config.description or f'A {config.template_variant} plugin for Truthound.'}
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
from {pkg_name}.plugin import {config.class_name}Plugin
|
|
262
|
+
|
|
263
|
+
__version__ = "{config.version}"
|
|
264
|
+
__all__ = ["{config.class_name}Plugin"]
|
|
265
|
+
'''
|
|
266
|
+
result.add_file(f"{pkg_name}/__init__.py", content)
|
|
267
|
+
|
|
268
|
+
def _generate_validator_plugin(
|
|
269
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
270
|
+
) -> None:
|
|
271
|
+
"""Generate validator plugin template."""
|
|
272
|
+
content = f'''{self._get_header(config)}
|
|
273
|
+
|
|
274
|
+
from __future__ import annotations
|
|
275
|
+
|
|
276
|
+
from typing import Any
|
|
277
|
+
|
|
278
|
+
import polars as pl
|
|
279
|
+
|
|
280
|
+
from truthound.plugins import ValidatorPlugin, PluginInfo, PluginType
|
|
281
|
+
from truthound.validators.base import Validator, ValidatorConfig, ValidationIssue
|
|
282
|
+
from truthound.types import Severity
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class {config.class_name}Validator(Validator):
|
|
286
|
+
"""Custom validator provided by {config.name} plugin.
|
|
287
|
+
|
|
288
|
+
TODO: Implement your validation logic here.
|
|
289
|
+
|
|
290
|
+
Example:
|
|
291
|
+
>>> validator = {config.class_name}Validator()
|
|
292
|
+
>>> issues = validator.validate(lf)
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
name = "{config.name}"
|
|
296
|
+
category = "custom"
|
|
297
|
+
default_severity = Severity.MEDIUM
|
|
298
|
+
|
|
299
|
+
def __init__(
|
|
300
|
+
self,
|
|
301
|
+
config: ValidatorConfig | None = None,
|
|
302
|
+
**kwargs: Any,
|
|
303
|
+
) -> None:
|
|
304
|
+
"""Initialize the validator.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
config: Optional validator configuration.
|
|
308
|
+
**kwargs: Additional arguments.
|
|
309
|
+
"""
|
|
310
|
+
super().__init__(config, **kwargs)
|
|
311
|
+
|
|
312
|
+
def validate(self, lf: pl.LazyFrame) -> list[ValidationIssue]:
|
|
313
|
+
"""Perform validation.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
lf: Polars LazyFrame to validate.
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
List of validation issues found.
|
|
320
|
+
"""
|
|
321
|
+
issues: list[ValidationIssue] = []
|
|
322
|
+
total_rows = lf.select(pl.len()).collect().item()
|
|
323
|
+
|
|
324
|
+
if total_rows == 0:
|
|
325
|
+
return issues
|
|
326
|
+
|
|
327
|
+
# TODO: Implement your validation logic
|
|
328
|
+
# Example:
|
|
329
|
+
# for col in self._get_target_columns(lf):
|
|
330
|
+
# violations = lf.filter(pl.col(col).is_null()).collect()
|
|
331
|
+
# if violations.height > 0:
|
|
332
|
+
# issues.append(ValidationIssue(
|
|
333
|
+
# column=col,
|
|
334
|
+
# issue_type=self.name,
|
|
335
|
+
# count=violations.height,
|
|
336
|
+
# severity=self.default_severity,
|
|
337
|
+
# details=f"Found {{violations.height}} issues",
|
|
338
|
+
# ))
|
|
339
|
+
|
|
340
|
+
return issues
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class {config.class_name}Plugin(ValidatorPlugin):
|
|
344
|
+
"""Plugin that provides custom validators.
|
|
345
|
+
|
|
346
|
+
This plugin is automatically discovered and can be loaded using:
|
|
347
|
+
|
|
348
|
+
manager.load_plugin("{config.name}")
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
def _get_plugin_name(self) -> str:
|
|
352
|
+
return "{config.name}"
|
|
353
|
+
|
|
354
|
+
def _get_plugin_version(self) -> str:
|
|
355
|
+
return "{config.version}"
|
|
356
|
+
|
|
357
|
+
def _get_description(self) -> str:
|
|
358
|
+
return "{config.description or 'Custom validators for Truthound'}"
|
|
359
|
+
|
|
360
|
+
def _get_author(self) -> str:
|
|
361
|
+
return "{config.author or ''}"
|
|
362
|
+
|
|
363
|
+
def get_validators(self) -> list[type[Validator]]:
|
|
364
|
+
"""Return validator classes to register.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
List of validator classes.
|
|
368
|
+
"""
|
|
369
|
+
return [{config.class_name}Validator]
|
|
370
|
+
'''
|
|
371
|
+
result.add_file(f"{pkg_name}/plugin.py", content)
|
|
372
|
+
|
|
373
|
+
def _generate_reporter_plugin(
|
|
374
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
375
|
+
) -> None:
|
|
376
|
+
"""Generate reporter plugin template."""
|
|
377
|
+
content = f'''{self._get_header(config)}
|
|
378
|
+
|
|
379
|
+
from __future__ import annotations
|
|
380
|
+
|
|
381
|
+
from dataclasses import dataclass
|
|
382
|
+
from typing import TYPE_CHECKING, Any
|
|
383
|
+
|
|
384
|
+
from truthound.plugins import ReporterPlugin, PluginInfo, PluginType
|
|
385
|
+
from truthound.reporters.base import ValidationReporter, ReporterConfig
|
|
386
|
+
|
|
387
|
+
if TYPE_CHECKING:
|
|
388
|
+
from truthound.stores.results import ValidationResult
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@dataclass
|
|
392
|
+
class {config.class_name}ReporterConfig(ReporterConfig):
|
|
393
|
+
"""Configuration for {config.class_name} reporter."""
|
|
394
|
+
|
|
395
|
+
include_passed: bool = False
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
class {config.class_name}Reporter(ValidationReporter[{config.class_name}ReporterConfig]):
|
|
399
|
+
"""Custom reporter provided by {config.name} plugin.
|
|
400
|
+
|
|
401
|
+
Example:
|
|
402
|
+
>>> reporter = {config.class_name}Reporter()
|
|
403
|
+
>>> output = reporter.render(validation_result)
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
name = "{config.name}"
|
|
407
|
+
file_extension = ".txt"
|
|
408
|
+
content_type = "text/plain"
|
|
409
|
+
|
|
410
|
+
@classmethod
|
|
411
|
+
def _default_config(cls) -> {config.class_name}ReporterConfig:
|
|
412
|
+
return {config.class_name}ReporterConfig()
|
|
413
|
+
|
|
414
|
+
def render(self, data: "ValidationResult") -> str:
|
|
415
|
+
"""Render validation result to string.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
data: Validation result to render.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
Rendered string.
|
|
422
|
+
"""
|
|
423
|
+
lines = [
|
|
424
|
+
f"Validation Report: {{data.data_asset}}",
|
|
425
|
+
f"Status: {{data.status.value}}",
|
|
426
|
+
f"Total Issues: {{len([r for r in data.results if not r.success])}}",
|
|
427
|
+
"",
|
|
428
|
+
]
|
|
429
|
+
|
|
430
|
+
for result in data.results:
|
|
431
|
+
if not result.success or self._config.include_passed:
|
|
432
|
+
lines.append(
|
|
433
|
+
f"- {{result.column or 'table'}}: "
|
|
434
|
+
f"{{result.issue_type}} ({{result.severity}})"
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
return "\\n".join(lines)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class {config.class_name}Plugin(ReporterPlugin):
|
|
441
|
+
"""Plugin that provides custom reporters."""
|
|
442
|
+
|
|
443
|
+
def _get_plugin_name(self) -> str:
|
|
444
|
+
return "{config.name}"
|
|
445
|
+
|
|
446
|
+
def _get_plugin_version(self) -> str:
|
|
447
|
+
return "{config.version}"
|
|
448
|
+
|
|
449
|
+
def _get_description(self) -> str:
|
|
450
|
+
return "{config.description or 'Custom reporter for Truthound'}"
|
|
451
|
+
|
|
452
|
+
def _get_author(self) -> str:
|
|
453
|
+
return "{config.author or ''}"
|
|
454
|
+
|
|
455
|
+
def get_reporters(self) -> dict[str, type[ValidationReporter]]:
|
|
456
|
+
"""Return reporter classes to register.
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
Dictionary of reporter name to class.
|
|
460
|
+
"""
|
|
461
|
+
return {{"{config.name}": {config.class_name}Reporter}}
|
|
462
|
+
'''
|
|
463
|
+
result.add_file(f"{pkg_name}/plugin.py", content)
|
|
464
|
+
|
|
465
|
+
def _generate_hook_plugin(
|
|
466
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
467
|
+
) -> None:
|
|
468
|
+
"""Generate hook plugin template."""
|
|
469
|
+
content = f'''{self._get_header(config)}
|
|
470
|
+
|
|
471
|
+
from __future__ import annotations
|
|
472
|
+
|
|
473
|
+
import logging
|
|
474
|
+
from typing import Any, Callable
|
|
475
|
+
|
|
476
|
+
from truthound.plugins import HookPlugin, PluginInfo, PluginType, HookType
|
|
477
|
+
|
|
478
|
+
logger = logging.getLogger(__name__)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def on_validation_start(
|
|
482
|
+
datasource: Any,
|
|
483
|
+
validators: list[Any],
|
|
484
|
+
**kwargs: Any,
|
|
485
|
+
) -> None:
|
|
486
|
+
"""Called before validation starts.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
datasource: The data source being validated.
|
|
490
|
+
validators: List of validators to run.
|
|
491
|
+
**kwargs: Additional context.
|
|
492
|
+
"""
|
|
493
|
+
logger.info(f"[{config.name}] Starting validation on {{datasource}}")
|
|
494
|
+
logger.debug(f"[{config.name}] Validators: {{[v.name for v in validators]}}")
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def on_validation_complete(
|
|
498
|
+
datasource: Any,
|
|
499
|
+
result: Any,
|
|
500
|
+
issues: list[Any],
|
|
501
|
+
**kwargs: Any,
|
|
502
|
+
) -> None:
|
|
503
|
+
"""Called after validation completes.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
datasource: The data source that was validated.
|
|
507
|
+
result: The validation result.
|
|
508
|
+
issues: List of issues found.
|
|
509
|
+
**kwargs: Additional context.
|
|
510
|
+
"""
|
|
511
|
+
logger.info(
|
|
512
|
+
f"[{config.name}] Validation complete: "
|
|
513
|
+
f"{{len(issues)}} issues found"
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def on_validator_start(
|
|
518
|
+
validator: Any,
|
|
519
|
+
datasource: Any,
|
|
520
|
+
**kwargs: Any,
|
|
521
|
+
) -> None:
|
|
522
|
+
"""Called before each validator runs.
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
validator: The validator about to run.
|
|
526
|
+
datasource: The data source.
|
|
527
|
+
**kwargs: Additional context.
|
|
528
|
+
"""
|
|
529
|
+
logger.debug(f"[{config.name}] Starting validator: {{validator.name}}")
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def on_validator_complete(
|
|
533
|
+
validator: Any,
|
|
534
|
+
issues: list[Any],
|
|
535
|
+
duration: float,
|
|
536
|
+
**kwargs: Any,
|
|
537
|
+
) -> None:
|
|
538
|
+
"""Called after each validator completes.
|
|
539
|
+
|
|
540
|
+
Args:
|
|
541
|
+
validator: The validator that completed.
|
|
542
|
+
issues: Issues found by this validator.
|
|
543
|
+
duration: Execution time in seconds.
|
|
544
|
+
**kwargs: Additional context.
|
|
545
|
+
"""
|
|
546
|
+
logger.debug(
|
|
547
|
+
f"[{config.name}] Validator {{validator.name}} completed in "
|
|
548
|
+
f"{{duration:.3f}}s with {{len(issues)}} issues"
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
class {config.class_name}Plugin(HookPlugin):
|
|
553
|
+
"""Plugin that provides event hooks.
|
|
554
|
+
|
|
555
|
+
Available hooks:
|
|
556
|
+
- BEFORE_VALIDATION: Called before validation starts
|
|
557
|
+
- AFTER_VALIDATION: Called after validation completes
|
|
558
|
+
- BEFORE_VALIDATOR: Called before each validator
|
|
559
|
+
- AFTER_VALIDATOR: Called after each validator
|
|
560
|
+
"""
|
|
561
|
+
|
|
562
|
+
def _get_plugin_name(self) -> str:
|
|
563
|
+
return "{config.name}"
|
|
564
|
+
|
|
565
|
+
def _get_plugin_version(self) -> str:
|
|
566
|
+
return "{config.version}"
|
|
567
|
+
|
|
568
|
+
def _get_description(self) -> str:
|
|
569
|
+
return "{config.description or 'Event hooks for Truthound'}"
|
|
570
|
+
|
|
571
|
+
def _get_author(self) -> str:
|
|
572
|
+
return "{config.author or ''}"
|
|
573
|
+
|
|
574
|
+
def get_hooks(self) -> dict[str, Callable[..., Any]]:
|
|
575
|
+
"""Return hooks to register.
|
|
576
|
+
|
|
577
|
+
Returns:
|
|
578
|
+
Dictionary of hook type to callback function.
|
|
579
|
+
"""
|
|
580
|
+
return {{
|
|
581
|
+
HookType.BEFORE_VALIDATION.value: on_validation_start,
|
|
582
|
+
HookType.AFTER_VALIDATION.value: on_validation_complete,
|
|
583
|
+
HookType.BEFORE_VALIDATOR.value: on_validator_start,
|
|
584
|
+
HookType.AFTER_VALIDATOR.value: on_validator_complete,
|
|
585
|
+
}}
|
|
586
|
+
'''
|
|
587
|
+
result.add_file(f"{pkg_name}/plugin.py", content)
|
|
588
|
+
|
|
589
|
+
def _generate_datasource_plugin(
|
|
590
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
591
|
+
) -> None:
|
|
592
|
+
"""Generate datasource plugin template."""
|
|
593
|
+
content = f'''{self._get_header(config)}
|
|
594
|
+
|
|
595
|
+
from __future__ import annotations
|
|
596
|
+
|
|
597
|
+
from typing import Any
|
|
598
|
+
|
|
599
|
+
import polars as pl
|
|
600
|
+
|
|
601
|
+
from truthound.plugins import DataSourcePlugin, PluginInfo, PluginType
|
|
602
|
+
from truthound.datasources.base import DataSource, DataSourceConfig
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
class {config.class_name}DataSource(DataSource):
|
|
606
|
+
"""Custom data source provided by {config.name} plugin.
|
|
607
|
+
|
|
608
|
+
TODO: Implement connection and data reading logic.
|
|
609
|
+
|
|
610
|
+
Example:
|
|
611
|
+
>>> ds = {config.class_name}DataSource(connection_string="...")
|
|
612
|
+
>>> lf = ds.read()
|
|
613
|
+
"""
|
|
614
|
+
|
|
615
|
+
name = "{config.name}"
|
|
616
|
+
|
|
617
|
+
def __init__(
|
|
618
|
+
self,
|
|
619
|
+
connection_string: str | None = None,
|
|
620
|
+
config: DataSourceConfig | None = None,
|
|
621
|
+
**kwargs: Any,
|
|
622
|
+
) -> None:
|
|
623
|
+
"""Initialize the data source.
|
|
624
|
+
|
|
625
|
+
Args:
|
|
626
|
+
connection_string: Connection string for the data source.
|
|
627
|
+
config: Data source configuration.
|
|
628
|
+
**kwargs: Additional arguments.
|
|
629
|
+
"""
|
|
630
|
+
super().__init__(config, **kwargs)
|
|
631
|
+
self._connection_string = connection_string
|
|
632
|
+
self._connection = None
|
|
633
|
+
|
|
634
|
+
def connect(self) -> None:
|
|
635
|
+
"""Establish connection to the data source."""
|
|
636
|
+
# TODO: Implement connection logic
|
|
637
|
+
pass
|
|
638
|
+
|
|
639
|
+
def disconnect(self) -> None:
|
|
640
|
+
"""Close connection to the data source."""
|
|
641
|
+
# TODO: Implement disconnection logic
|
|
642
|
+
self._connection = None
|
|
643
|
+
|
|
644
|
+
def read(
|
|
645
|
+
self,
|
|
646
|
+
query: str | None = None,
|
|
647
|
+
limit: int | None = None,
|
|
648
|
+
**kwargs: Any,
|
|
649
|
+
) -> pl.LazyFrame:
|
|
650
|
+
"""Read data from the source.
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
query: Optional query or table name.
|
|
654
|
+
limit: Maximum rows to read.
|
|
655
|
+
**kwargs: Additional read options.
|
|
656
|
+
|
|
657
|
+
Returns:
|
|
658
|
+
Polars LazyFrame with the data.
|
|
659
|
+
"""
|
|
660
|
+
# TODO: Implement data reading logic
|
|
661
|
+
# Example:
|
|
662
|
+
# if query:
|
|
663
|
+
# result = self._execute_query(query)
|
|
664
|
+
# else:
|
|
665
|
+
# result = self._read_all()
|
|
666
|
+
#
|
|
667
|
+
# if limit:
|
|
668
|
+
# result = result.head(limit)
|
|
669
|
+
#
|
|
670
|
+
# return result.lazy()
|
|
671
|
+
|
|
672
|
+
raise NotImplementedError("Implement read() method")
|
|
673
|
+
|
|
674
|
+
def get_schema(self) -> dict[str, Any]:
|
|
675
|
+
"""Get schema information from the data source.
|
|
676
|
+
|
|
677
|
+
Returns:
|
|
678
|
+
Dictionary with schema information.
|
|
679
|
+
"""
|
|
680
|
+
# TODO: Implement schema retrieval
|
|
681
|
+
return {{}}
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
class {config.class_name}Plugin(DataSourcePlugin):
|
|
685
|
+
"""Plugin that provides custom data source connectors."""
|
|
686
|
+
|
|
687
|
+
def _get_plugin_name(self) -> str:
|
|
688
|
+
return "{config.name}"
|
|
689
|
+
|
|
690
|
+
def _get_plugin_version(self) -> str:
|
|
691
|
+
return "{config.version}"
|
|
692
|
+
|
|
693
|
+
def _get_description(self) -> str:
|
|
694
|
+
return "{config.description or 'Custom data source for Truthound'}"
|
|
695
|
+
|
|
696
|
+
def _get_author(self) -> str:
|
|
697
|
+
return "{config.author or ''}"
|
|
698
|
+
|
|
699
|
+
def get_datasources(self) -> dict[str, type[DataSource]]:
|
|
700
|
+
"""Return data source classes to register.
|
|
701
|
+
|
|
702
|
+
Returns:
|
|
703
|
+
Dictionary of data source name to class.
|
|
704
|
+
"""
|
|
705
|
+
return {{"{config.name}": {config.class_name}DataSource}}
|
|
706
|
+
'''
|
|
707
|
+
result.add_file(f"{pkg_name}/plugin.py", content)
|
|
708
|
+
|
|
709
|
+
def _generate_action_plugin(
|
|
710
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
711
|
+
) -> None:
|
|
712
|
+
"""Generate checkpoint action plugin template."""
|
|
713
|
+
content = f'''{self._get_header(config)}
|
|
714
|
+
|
|
715
|
+
from __future__ import annotations
|
|
716
|
+
|
|
717
|
+
import logging
|
|
718
|
+
from typing import Any
|
|
719
|
+
|
|
720
|
+
from truthound.plugins import ActionPlugin, PluginInfo, PluginType
|
|
721
|
+
from truthound.checkpoint.actions.base import CheckpointAction, ActionConfig
|
|
722
|
+
from truthound.checkpoint.core import CheckpointResult
|
|
723
|
+
|
|
724
|
+
logger = logging.getLogger(__name__)
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
class {config.class_name}ActionConfig(ActionConfig):
|
|
728
|
+
"""Configuration for {config.class_name} action.
|
|
729
|
+
|
|
730
|
+
Attributes:
|
|
731
|
+
enabled: Whether the action is enabled.
|
|
732
|
+
trigger_on: When to trigger (success, failure, always).
|
|
733
|
+
custom_option: Your custom configuration option.
|
|
734
|
+
"""
|
|
735
|
+
|
|
736
|
+
enabled: bool = True
|
|
737
|
+
trigger_on: str = "always"
|
|
738
|
+
custom_option: str = ""
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
class {config.class_name}Action(CheckpointAction[{config.class_name}ActionConfig]):
|
|
742
|
+
"""Custom checkpoint action provided by {config.name} plugin.
|
|
743
|
+
|
|
744
|
+
This action is triggered after checkpoint validation completes.
|
|
745
|
+
|
|
746
|
+
Example:
|
|
747
|
+
>>> action = {config.class_name}Action(custom_option="value")
|
|
748
|
+
>>> action.execute(checkpoint_result)
|
|
749
|
+
"""
|
|
750
|
+
|
|
751
|
+
name = "{config.name}"
|
|
752
|
+
|
|
753
|
+
def __init__(
|
|
754
|
+
self,
|
|
755
|
+
config: {config.class_name}ActionConfig | None = None,
|
|
756
|
+
**kwargs: Any,
|
|
757
|
+
) -> None:
|
|
758
|
+
"""Initialize the action.
|
|
759
|
+
|
|
760
|
+
Args:
|
|
761
|
+
config: Action configuration.
|
|
762
|
+
**kwargs: Additional arguments passed to config.
|
|
763
|
+
"""
|
|
764
|
+
if config is None:
|
|
765
|
+
config = {config.class_name}ActionConfig(**kwargs)
|
|
766
|
+
super().__init__(config)
|
|
767
|
+
|
|
768
|
+
def execute(self, result: CheckpointResult) -> bool:
|
|
769
|
+
"""Execute the action.
|
|
770
|
+
|
|
771
|
+
Args:
|
|
772
|
+
result: The checkpoint result to act on.
|
|
773
|
+
|
|
774
|
+
Returns:
|
|
775
|
+
True if action succeeded, False otherwise.
|
|
776
|
+
"""
|
|
777
|
+
if not self._should_trigger(result):
|
|
778
|
+
logger.debug(f"[{config.name}] Skipping - trigger condition not met")
|
|
779
|
+
return True
|
|
780
|
+
|
|
781
|
+
try:
|
|
782
|
+
# TODO: Implement your action logic
|
|
783
|
+
logger.info(
|
|
784
|
+
f"[{config.name}] Executing action for checkpoint "
|
|
785
|
+
f"'{{result.checkpoint_name}}'"
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
# Example: Log results
|
|
789
|
+
logger.info(f" Status: {{result.status.value}}")
|
|
790
|
+
logger.info(f" Issues: {{result.issue_count}}")
|
|
791
|
+
|
|
792
|
+
if self._config.custom_option:
|
|
793
|
+
logger.info(f" Custom option: {{self._config.custom_option}}")
|
|
794
|
+
|
|
795
|
+
return True
|
|
796
|
+
|
|
797
|
+
except Exception as e:
|
|
798
|
+
logger.error(f"[{config.name}] Action failed: {{e}}")
|
|
799
|
+
return False
|
|
800
|
+
|
|
801
|
+
def _should_trigger(self, result: CheckpointResult) -> bool:
|
|
802
|
+
"""Check if action should trigger based on configuration.
|
|
803
|
+
|
|
804
|
+
Args:
|
|
805
|
+
result: The checkpoint result.
|
|
806
|
+
|
|
807
|
+
Returns:
|
|
808
|
+
True if should trigger, False otherwise.
|
|
809
|
+
"""
|
|
810
|
+
if not self._config.enabled:
|
|
811
|
+
return False
|
|
812
|
+
|
|
813
|
+
trigger = self._config.trigger_on.lower()
|
|
814
|
+
if trigger == "always":
|
|
815
|
+
return True
|
|
816
|
+
elif trigger == "success":
|
|
817
|
+
return result.status.value == "success"
|
|
818
|
+
elif trigger == "failure":
|
|
819
|
+
return result.status.value in ("failure", "error")
|
|
820
|
+
|
|
821
|
+
return True
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
class {config.class_name}Plugin(ActionPlugin):
|
|
825
|
+
"""Plugin that provides custom checkpoint actions."""
|
|
826
|
+
|
|
827
|
+
def _get_plugin_name(self) -> str:
|
|
828
|
+
return "{config.name}"
|
|
829
|
+
|
|
830
|
+
def _get_plugin_version(self) -> str:
|
|
831
|
+
return "{config.version}"
|
|
832
|
+
|
|
833
|
+
def _get_description(self) -> str:
|
|
834
|
+
return "{config.description or 'Custom checkpoint action for Truthound'}"
|
|
835
|
+
|
|
836
|
+
def _get_author(self) -> str:
|
|
837
|
+
return "{config.author or ''}"
|
|
838
|
+
|
|
839
|
+
def get_actions(self) -> dict[str, type[CheckpointAction]]:
|
|
840
|
+
"""Return action classes to register.
|
|
841
|
+
|
|
842
|
+
Returns:
|
|
843
|
+
Dictionary of action name to class.
|
|
844
|
+
"""
|
|
845
|
+
return {{"{config.name}": {config.class_name}Action}}
|
|
846
|
+
'''
|
|
847
|
+
result.add_file(f"{pkg_name}/plugin.py", content)
|
|
848
|
+
|
|
849
|
+
def _generate_full_plugin(
|
|
850
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
851
|
+
) -> None:
|
|
852
|
+
"""Generate full-featured plugin with multiple components."""
|
|
853
|
+
content = f'''{self._get_header(config)}
|
|
854
|
+
|
|
855
|
+
from __future__ import annotations
|
|
856
|
+
|
|
857
|
+
import logging
|
|
858
|
+
from dataclasses import dataclass
|
|
859
|
+
from typing import Any, Callable, TYPE_CHECKING
|
|
860
|
+
|
|
861
|
+
import polars as pl
|
|
862
|
+
|
|
863
|
+
from truthound.plugins import (
|
|
864
|
+
Plugin,
|
|
865
|
+
PluginInfo,
|
|
866
|
+
PluginType,
|
|
867
|
+
PluginConfig,
|
|
868
|
+
HookType,
|
|
869
|
+
)
|
|
870
|
+
from truthound.validators.base import Validator, ValidatorConfig, ValidationIssue
|
|
871
|
+
from truthound.reporters.base import ValidationReporter, ReporterConfig
|
|
872
|
+
from truthound.types import Severity
|
|
873
|
+
|
|
874
|
+
if TYPE_CHECKING:
|
|
875
|
+
from truthound.plugins import PluginManager
|
|
876
|
+
from truthound.stores.results import ValidationResult
|
|
877
|
+
|
|
878
|
+
logger = logging.getLogger(__name__)
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
# =============================================================================
|
|
882
|
+
# Validators
|
|
883
|
+
# =============================================================================
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
class {config.class_name}Validator(Validator):
|
|
887
|
+
"""Custom validator provided by {config.name} plugin."""
|
|
888
|
+
|
|
889
|
+
name = "{config.name}_validator"
|
|
890
|
+
category = "custom"
|
|
891
|
+
|
|
892
|
+
def validate(self, lf: pl.LazyFrame) -> list[ValidationIssue]:
|
|
893
|
+
issues: list[ValidationIssue] = []
|
|
894
|
+
# TODO: Implement validation logic
|
|
895
|
+
return issues
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
# =============================================================================
|
|
899
|
+
# Reporters
|
|
900
|
+
# =============================================================================
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
@dataclass
|
|
904
|
+
class {config.class_name}ReporterConfig(ReporterConfig):
|
|
905
|
+
"""Configuration for {config.class_name} reporter."""
|
|
906
|
+
include_passed: bool = False
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
class {config.class_name}Reporter(ValidationReporter[{config.class_name}ReporterConfig]):
|
|
910
|
+
"""Custom reporter provided by {config.name} plugin."""
|
|
911
|
+
|
|
912
|
+
name = "{config.name}_reporter"
|
|
913
|
+
file_extension = ".txt"
|
|
914
|
+
content_type = "text/plain"
|
|
915
|
+
|
|
916
|
+
@classmethod
|
|
917
|
+
def _default_config(cls) -> {config.class_name}ReporterConfig:
|
|
918
|
+
return {config.class_name}ReporterConfig()
|
|
919
|
+
|
|
920
|
+
def render(self, data: "ValidationResult") -> str:
|
|
921
|
+
lines = [f"Report: {{data.data_asset}}"]
|
|
922
|
+
for r in data.results:
|
|
923
|
+
if not r.success:
|
|
924
|
+
lines.append(f"- {{r.column}}: {{r.issue_type}}")
|
|
925
|
+
return "\\n".join(lines)
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
# =============================================================================
|
|
929
|
+
# Hooks
|
|
930
|
+
# =============================================================================
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
def on_validation_complete(
|
|
934
|
+
datasource: Any,
|
|
935
|
+
result: Any,
|
|
936
|
+
issues: list[Any],
|
|
937
|
+
**kwargs: Any,
|
|
938
|
+
) -> None:
|
|
939
|
+
"""Hook called after validation completes."""
|
|
940
|
+
logger.info(f"[{config.name}] Validation complete: {{len(issues)}} issues")
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
# =============================================================================
|
|
944
|
+
# Plugin
|
|
945
|
+
# =============================================================================
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
@dataclass
|
|
949
|
+
class {config.class_name}PluginConfig(PluginConfig):
|
|
950
|
+
"""Configuration for {config.class_name} plugin.
|
|
951
|
+
|
|
952
|
+
Attributes:
|
|
953
|
+
enable_validator: Enable the validator component.
|
|
954
|
+
enable_reporter: Enable the reporter component.
|
|
955
|
+
enable_hooks: Enable event hooks.
|
|
956
|
+
"""
|
|
957
|
+
|
|
958
|
+
enable_validator: bool = True
|
|
959
|
+
enable_reporter: bool = True
|
|
960
|
+
enable_hooks: bool = True
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
class {config.class_name}Plugin(Plugin[{config.class_name}PluginConfig]):
|
|
964
|
+
"""Full-featured Truthound plugin.
|
|
965
|
+
|
|
966
|
+
This plugin provides:
|
|
967
|
+
- Custom validators
|
|
968
|
+
- Custom reporters
|
|
969
|
+
- Event hooks
|
|
970
|
+
|
|
971
|
+
Components can be enabled/disabled via configuration.
|
|
972
|
+
"""
|
|
973
|
+
|
|
974
|
+
@property
|
|
975
|
+
def info(self) -> PluginInfo:
|
|
976
|
+
return PluginInfo(
|
|
977
|
+
name="{config.name}",
|
|
978
|
+
version="{config.version}",
|
|
979
|
+
plugin_type=PluginType.CUSTOM,
|
|
980
|
+
description="{config.description or 'Full-featured Truthound plugin'}",
|
|
981
|
+
author="{config.author or ''}",
|
|
982
|
+
tags=["validator", "reporter", "hooks"],
|
|
983
|
+
)
|
|
984
|
+
|
|
985
|
+
def setup(self) -> None:
|
|
986
|
+
"""Initialize the plugin."""
|
|
987
|
+
logger.info(f"[{config.name}] Plugin setup complete")
|
|
988
|
+
|
|
989
|
+
def teardown(self) -> None:
|
|
990
|
+
"""Cleanup plugin resources."""
|
|
991
|
+
logger.info(f"[{config.name}] Plugin teardown complete")
|
|
992
|
+
|
|
993
|
+
def register(self, manager: "PluginManager") -> None:
|
|
994
|
+
"""Register plugin components with the manager.
|
|
995
|
+
|
|
996
|
+
Args:
|
|
997
|
+
manager: The plugin manager.
|
|
998
|
+
"""
|
|
999
|
+
config = self._config or {config.class_name}PluginConfig()
|
|
1000
|
+
|
|
1001
|
+
# Register validators
|
|
1002
|
+
if config.enable_validator:
|
|
1003
|
+
manager.register_validator({config.class_name}Validator)
|
|
1004
|
+
logger.debug(f"[{config.name}] Registered validator")
|
|
1005
|
+
|
|
1006
|
+
# Register reporters
|
|
1007
|
+
if config.enable_reporter:
|
|
1008
|
+
manager.register_reporter("{config.name}", {config.class_name}Reporter)
|
|
1009
|
+
logger.debug(f"[{config.name}] Registered reporter")
|
|
1010
|
+
|
|
1011
|
+
# Register hooks
|
|
1012
|
+
if config.enable_hooks:
|
|
1013
|
+
manager.register_hook(
|
|
1014
|
+
HookType.AFTER_VALIDATION.value,
|
|
1015
|
+
on_validation_complete,
|
|
1016
|
+
)
|
|
1017
|
+
logger.debug(f"[{config.name}] Registered hooks")
|
|
1018
|
+
'''
|
|
1019
|
+
result.add_file(f"{pkg_name}/plugin.py", content)
|
|
1020
|
+
|
|
1021
|
+
def _generate_tests(
|
|
1022
|
+
self, config: ScaffoldConfig, result: ScaffoldResult, pkg_name: str
|
|
1023
|
+
) -> None:
|
|
1024
|
+
"""Generate test files."""
|
|
1025
|
+
# Test __init__.py
|
|
1026
|
+
result.add_file("tests/__init__.py", "")
|
|
1027
|
+
|
|
1028
|
+
# Main test file
|
|
1029
|
+
content = f'''"""Tests for {config.name} plugin."""
|
|
1030
|
+
|
|
1031
|
+
import pytest
|
|
1032
|
+
|
|
1033
|
+
from {pkg_name} import {config.class_name}Plugin
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
class Test{config.class_name}Plugin:
|
|
1037
|
+
"""Test cases for {config.class_name}Plugin."""
|
|
1038
|
+
|
|
1039
|
+
def test_plugin_info(self):
|
|
1040
|
+
"""Test plugin info properties."""
|
|
1041
|
+
plugin = {config.class_name}Plugin()
|
|
1042
|
+
|
|
1043
|
+
assert plugin.name == "{config.name}"
|
|
1044
|
+
assert plugin.version == "{config.version}"
|
|
1045
|
+
|
|
1046
|
+
def test_plugin_setup(self):
|
|
1047
|
+
"""Test plugin setup."""
|
|
1048
|
+
plugin = {config.class_name}Plugin()
|
|
1049
|
+
plugin.setup()
|
|
1050
|
+
# Should not raise
|
|
1051
|
+
|
|
1052
|
+
def test_plugin_teardown(self):
|
|
1053
|
+
"""Test plugin teardown."""
|
|
1054
|
+
plugin = {config.class_name}Plugin()
|
|
1055
|
+
plugin.setup()
|
|
1056
|
+
plugin.teardown()
|
|
1057
|
+
# Should not raise
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
class Test{config.class_name}Components:
|
|
1061
|
+
"""Test plugin components."""
|
|
1062
|
+
|
|
1063
|
+
@pytest.mark.parametrize("variant", ["{config.template_variant}"])
|
|
1064
|
+
def test_component_exists(self, variant):
|
|
1065
|
+
"""Test that expected components exist."""
|
|
1066
|
+
plugin = {config.class_name}Plugin()
|
|
1067
|
+
|
|
1068
|
+
# Plugin should have required methods
|
|
1069
|
+
assert hasattr(plugin, "setup")
|
|
1070
|
+
assert hasattr(plugin, "teardown")
|
|
1071
|
+
'''
|
|
1072
|
+
result.add_file("tests/test_plugin.py", content)
|