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,615 @@
|
|
|
1
|
+
"""Testcontainers integration for streaming tests.
|
|
2
|
+
|
|
3
|
+
Provides managed containers for integration testing:
|
|
4
|
+
- KafkaTestContainer: Apache Kafka with Zookeeper
|
|
5
|
+
- RedisTestContainer: Redis for state management
|
|
6
|
+
- LocalStackTestContainer: AWS Kinesis emulation
|
|
7
|
+
- StreamTestEnvironment: Multi-container orchestration
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, AsyncIterator
|
|
15
|
+
import asyncio
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# Container Protocols
|
|
25
|
+
# =============================================================================
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ITestContainer(ABC):
|
|
29
|
+
"""Protocol for test containers."""
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
async def start(self) -> None:
|
|
33
|
+
"""Start the container."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def stop(self) -> None:
|
|
38
|
+
"""Stop the container."""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def get_connection_url(self) -> str:
|
|
43
|
+
"""Get connection URL for the service."""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def is_running(self) -> bool:
|
|
49
|
+
"""Check if container is running."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# =============================================================================
|
|
54
|
+
# Kafka Test Container
|
|
55
|
+
# =============================================================================
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class KafkaTestContainerConfig:
|
|
60
|
+
"""Configuration for Kafka test container.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
image: Docker image to use
|
|
64
|
+
kafka_port: Kafka broker port
|
|
65
|
+
zookeeper_port: Zookeeper port
|
|
66
|
+
startup_timeout: Container startup timeout in seconds
|
|
67
|
+
auto_create_topics: Whether to auto-create topics
|
|
68
|
+
num_partitions: Default number of partitions for topics
|
|
69
|
+
replication_factor: Default replication factor
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
image: str = "confluentinc/cp-kafka:7.5.0"
|
|
73
|
+
kafka_port: int = 9092
|
|
74
|
+
zookeeper_port: int = 2181
|
|
75
|
+
startup_timeout: int = 60
|
|
76
|
+
auto_create_topics: bool = True
|
|
77
|
+
num_partitions: int = 3
|
|
78
|
+
replication_factor: int = 1
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class KafkaTestContainer(ITestContainer):
|
|
82
|
+
"""Kafka test container using testcontainers-python.
|
|
83
|
+
|
|
84
|
+
Provides a real Kafka broker for integration testing.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
>>> async with KafkaTestContainer() as kafka:
|
|
88
|
+
... url = kafka.get_connection_url()
|
|
89
|
+
... # Use with KafkaAdapter
|
|
90
|
+
... adapter = KafkaAdapter(KafkaAdapterConfig(
|
|
91
|
+
... bootstrap_servers=url,
|
|
92
|
+
... topic="test-topic",
|
|
93
|
+
... ))
|
|
94
|
+
|
|
95
|
+
Requires:
|
|
96
|
+
pip install testcontainers[kafka]
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(self, config: KafkaTestContainerConfig | None = None):
|
|
100
|
+
self._config = config or KafkaTestContainerConfig()
|
|
101
|
+
self._container: Any = None
|
|
102
|
+
self._running = False
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def is_running(self) -> bool:
|
|
106
|
+
return self._running
|
|
107
|
+
|
|
108
|
+
async def start(self) -> None:
|
|
109
|
+
"""Start Kafka container."""
|
|
110
|
+
if self._running:
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
from testcontainers.kafka import KafkaContainer
|
|
115
|
+
except ImportError:
|
|
116
|
+
raise ImportError(
|
|
117
|
+
"testcontainers is required for integration testing. "
|
|
118
|
+
"Install with: pip install testcontainers[kafka]"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def _start_sync():
|
|
122
|
+
self._container = KafkaContainer(self._config.image)
|
|
123
|
+
self._container.start()
|
|
124
|
+
return self._container
|
|
125
|
+
|
|
126
|
+
# Run in thread pool to avoid blocking
|
|
127
|
+
loop = asyncio.get_event_loop()
|
|
128
|
+
await loop.run_in_executor(None, _start_sync)
|
|
129
|
+
self._running = True
|
|
130
|
+
|
|
131
|
+
logger.info(f"Kafka container started: {self.get_connection_url()}")
|
|
132
|
+
|
|
133
|
+
async def stop(self) -> None:
|
|
134
|
+
"""Stop Kafka container."""
|
|
135
|
+
if not self._running or not self._container:
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
def _stop_sync():
|
|
139
|
+
self._container.stop()
|
|
140
|
+
|
|
141
|
+
loop = asyncio.get_event_loop()
|
|
142
|
+
await loop.run_in_executor(None, _stop_sync)
|
|
143
|
+
self._running = False
|
|
144
|
+
|
|
145
|
+
logger.info("Kafka container stopped")
|
|
146
|
+
|
|
147
|
+
def get_connection_url(self) -> str:
|
|
148
|
+
"""Get Kafka bootstrap servers URL."""
|
|
149
|
+
if not self._container:
|
|
150
|
+
raise RuntimeError("Container not started")
|
|
151
|
+
return self._container.get_bootstrap_server()
|
|
152
|
+
|
|
153
|
+
async def create_topic(
|
|
154
|
+
self,
|
|
155
|
+
topic: str,
|
|
156
|
+
num_partitions: int | None = None,
|
|
157
|
+
replication_factor: int | None = None,
|
|
158
|
+
) -> None:
|
|
159
|
+
"""Create a Kafka topic.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
topic: Topic name
|
|
163
|
+
num_partitions: Number of partitions
|
|
164
|
+
replication_factor: Replication factor
|
|
165
|
+
"""
|
|
166
|
+
if not self._running:
|
|
167
|
+
raise RuntimeError("Container not running")
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
from kafka.admin import KafkaAdminClient, NewTopic
|
|
171
|
+
except ImportError:
|
|
172
|
+
raise ImportError(
|
|
173
|
+
"kafka-python is required for topic management. "
|
|
174
|
+
"Install with: pip install kafka-python"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def _create_topic_sync():
|
|
178
|
+
admin = KafkaAdminClient(
|
|
179
|
+
bootstrap_servers=self.get_connection_url(),
|
|
180
|
+
)
|
|
181
|
+
topic_obj = NewTopic(
|
|
182
|
+
name=topic,
|
|
183
|
+
num_partitions=num_partitions or self._config.num_partitions,
|
|
184
|
+
replication_factor=replication_factor or self._config.replication_factor,
|
|
185
|
+
)
|
|
186
|
+
admin.create_topics([topic_obj])
|
|
187
|
+
admin.close()
|
|
188
|
+
|
|
189
|
+
loop = asyncio.get_event_loop()
|
|
190
|
+
await loop.run_in_executor(None, _create_topic_sync)
|
|
191
|
+
|
|
192
|
+
async def __aenter__(self) -> "KafkaTestContainer":
|
|
193
|
+
await self.start()
|
|
194
|
+
return self
|
|
195
|
+
|
|
196
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
197
|
+
await self.stop()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# =============================================================================
|
|
201
|
+
# Redis Test Container
|
|
202
|
+
# =============================================================================
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@dataclass
|
|
206
|
+
class RedisTestContainerConfig:
|
|
207
|
+
"""Configuration for Redis test container.
|
|
208
|
+
|
|
209
|
+
Attributes:
|
|
210
|
+
image: Docker image to use
|
|
211
|
+
port: Redis port
|
|
212
|
+
startup_timeout: Container startup timeout in seconds
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
image: str = "redis:7-alpine"
|
|
216
|
+
port: int = 6379
|
|
217
|
+
startup_timeout: int = 30
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class RedisTestContainer(ITestContainer):
|
|
221
|
+
"""Redis test container for state management testing.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
>>> async with RedisTestContainer() as redis:
|
|
225
|
+
... url = redis.get_connection_url()
|
|
226
|
+
... # Use with RedisStateBackend
|
|
227
|
+
|
|
228
|
+
Requires:
|
|
229
|
+
pip install testcontainers[redis]
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
def __init__(self, config: RedisTestContainerConfig | None = None):
|
|
233
|
+
self._config = config or RedisTestContainerConfig()
|
|
234
|
+
self._container: Any = None
|
|
235
|
+
self._running = False
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def is_running(self) -> bool:
|
|
239
|
+
return self._running
|
|
240
|
+
|
|
241
|
+
async def start(self) -> None:
|
|
242
|
+
"""Start Redis container."""
|
|
243
|
+
if self._running:
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
from testcontainers.redis import RedisContainer
|
|
248
|
+
except ImportError:
|
|
249
|
+
raise ImportError(
|
|
250
|
+
"testcontainers is required for integration testing. "
|
|
251
|
+
"Install with: pip install testcontainers[redis]"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def _start_sync():
|
|
255
|
+
self._container = RedisContainer(self._config.image)
|
|
256
|
+
self._container.start()
|
|
257
|
+
return self._container
|
|
258
|
+
|
|
259
|
+
loop = asyncio.get_event_loop()
|
|
260
|
+
await loop.run_in_executor(None, _start_sync)
|
|
261
|
+
self._running = True
|
|
262
|
+
|
|
263
|
+
logger.info(f"Redis container started: {self.get_connection_url()}")
|
|
264
|
+
|
|
265
|
+
async def stop(self) -> None:
|
|
266
|
+
"""Stop Redis container."""
|
|
267
|
+
if not self._running or not self._container:
|
|
268
|
+
return
|
|
269
|
+
|
|
270
|
+
def _stop_sync():
|
|
271
|
+
self._container.stop()
|
|
272
|
+
|
|
273
|
+
loop = asyncio.get_event_loop()
|
|
274
|
+
await loop.run_in_executor(None, _stop_sync)
|
|
275
|
+
self._running = False
|
|
276
|
+
|
|
277
|
+
logger.info("Redis container stopped")
|
|
278
|
+
|
|
279
|
+
def get_connection_url(self) -> str:
|
|
280
|
+
"""Get Redis connection URL."""
|
|
281
|
+
if not self._container:
|
|
282
|
+
raise RuntimeError("Container not started")
|
|
283
|
+
return self._container.get_connection_url()
|
|
284
|
+
|
|
285
|
+
def get_host(self) -> str:
|
|
286
|
+
"""Get Redis host."""
|
|
287
|
+
if not self._container:
|
|
288
|
+
raise RuntimeError("Container not started")
|
|
289
|
+
return self._container.get_container_host_ip()
|
|
290
|
+
|
|
291
|
+
def get_port(self) -> int:
|
|
292
|
+
"""Get Redis port."""
|
|
293
|
+
if not self._container:
|
|
294
|
+
raise RuntimeError("Container not started")
|
|
295
|
+
return int(self._container.get_exposed_port(self._config.port))
|
|
296
|
+
|
|
297
|
+
async def __aenter__(self) -> "RedisTestContainer":
|
|
298
|
+
await self.start()
|
|
299
|
+
return self
|
|
300
|
+
|
|
301
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
302
|
+
await self.stop()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# =============================================================================
|
|
306
|
+
# LocalStack Test Container (for AWS Kinesis)
|
|
307
|
+
# =============================================================================
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@dataclass
|
|
311
|
+
class LocalStackTestContainerConfig:
|
|
312
|
+
"""Configuration for LocalStack test container.
|
|
313
|
+
|
|
314
|
+
Attributes:
|
|
315
|
+
image: Docker image to use
|
|
316
|
+
services: AWS services to enable
|
|
317
|
+
port: LocalStack port
|
|
318
|
+
startup_timeout: Container startup timeout in seconds
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
image: str = "localstack/localstack:3"
|
|
322
|
+
services: list[str] = field(default_factory=lambda: ["kinesis"])
|
|
323
|
+
port: int = 4566
|
|
324
|
+
startup_timeout: int = 60
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class LocalStackTestContainer(ITestContainer):
|
|
328
|
+
"""LocalStack test container for AWS service emulation.
|
|
329
|
+
|
|
330
|
+
Provides local AWS Kinesis for integration testing.
|
|
331
|
+
|
|
332
|
+
Example:
|
|
333
|
+
>>> async with LocalStackTestContainer() as localstack:
|
|
334
|
+
... endpoint = localstack.get_connection_url()
|
|
335
|
+
... # Use with KinesisAdapter
|
|
336
|
+
... adapter = KinesisAdapter(KinesisAdapterConfig(
|
|
337
|
+
... stream_name="test-stream",
|
|
338
|
+
... endpoint_url=endpoint,
|
|
339
|
+
... ))
|
|
340
|
+
|
|
341
|
+
Requires:
|
|
342
|
+
pip install testcontainers[localstack]
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def __init__(self, config: LocalStackTestContainerConfig | None = None):
|
|
346
|
+
self._config = config or LocalStackTestContainerConfig()
|
|
347
|
+
self._container: Any = None
|
|
348
|
+
self._running = False
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def is_running(self) -> bool:
|
|
352
|
+
return self._running
|
|
353
|
+
|
|
354
|
+
async def start(self) -> None:
|
|
355
|
+
"""Start LocalStack container."""
|
|
356
|
+
if self._running:
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
from testcontainers.localstack import LocalStackContainer
|
|
361
|
+
except ImportError:
|
|
362
|
+
raise ImportError(
|
|
363
|
+
"testcontainers is required for integration testing. "
|
|
364
|
+
"Install with: pip install testcontainers[localstack]"
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def _start_sync():
|
|
368
|
+
self._container = LocalStackContainer(self._config.image)
|
|
369
|
+
for service in self._config.services:
|
|
370
|
+
self._container.with_services(service)
|
|
371
|
+
self._container.start()
|
|
372
|
+
return self._container
|
|
373
|
+
|
|
374
|
+
loop = asyncio.get_event_loop()
|
|
375
|
+
await loop.run_in_executor(None, _start_sync)
|
|
376
|
+
self._running = True
|
|
377
|
+
|
|
378
|
+
logger.info(f"LocalStack container started: {self.get_connection_url()}")
|
|
379
|
+
|
|
380
|
+
async def stop(self) -> None:
|
|
381
|
+
"""Stop LocalStack container."""
|
|
382
|
+
if not self._running or not self._container:
|
|
383
|
+
return
|
|
384
|
+
|
|
385
|
+
def _stop_sync():
|
|
386
|
+
self._container.stop()
|
|
387
|
+
|
|
388
|
+
loop = asyncio.get_event_loop()
|
|
389
|
+
await loop.run_in_executor(None, _stop_sync)
|
|
390
|
+
self._running = False
|
|
391
|
+
|
|
392
|
+
logger.info("LocalStack container stopped")
|
|
393
|
+
|
|
394
|
+
def get_connection_url(self) -> str:
|
|
395
|
+
"""Get LocalStack endpoint URL."""
|
|
396
|
+
if not self._container:
|
|
397
|
+
raise RuntimeError("Container not started")
|
|
398
|
+
host = self._container.get_container_host_ip()
|
|
399
|
+
port = self._container.get_exposed_port(self._config.port)
|
|
400
|
+
return f"http://{host}:{port}"
|
|
401
|
+
|
|
402
|
+
async def create_kinesis_stream(
|
|
403
|
+
self,
|
|
404
|
+
stream_name: str,
|
|
405
|
+
shard_count: int = 1,
|
|
406
|
+
) -> None:
|
|
407
|
+
"""Create a Kinesis stream.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
stream_name: Stream name
|
|
411
|
+
shard_count: Number of shards
|
|
412
|
+
"""
|
|
413
|
+
if not self._running:
|
|
414
|
+
raise RuntimeError("Container not running")
|
|
415
|
+
|
|
416
|
+
try:
|
|
417
|
+
import boto3
|
|
418
|
+
except ImportError:
|
|
419
|
+
raise ImportError(
|
|
420
|
+
"boto3 is required for Kinesis operations. "
|
|
421
|
+
"Install with: pip install boto3"
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
def _create_stream_sync():
|
|
425
|
+
client = boto3.client(
|
|
426
|
+
"kinesis",
|
|
427
|
+
endpoint_url=self.get_connection_url(),
|
|
428
|
+
region_name="us-east-1",
|
|
429
|
+
aws_access_key_id="test",
|
|
430
|
+
aws_secret_access_key="test",
|
|
431
|
+
)
|
|
432
|
+
client.create_stream(
|
|
433
|
+
StreamName=stream_name,
|
|
434
|
+
ShardCount=shard_count,
|
|
435
|
+
)
|
|
436
|
+
# Wait for stream to become active
|
|
437
|
+
waiter = client.get_waiter("stream_exists")
|
|
438
|
+
waiter.wait(StreamName=stream_name)
|
|
439
|
+
|
|
440
|
+
loop = asyncio.get_event_loop()
|
|
441
|
+
await loop.run_in_executor(None, _create_stream_sync)
|
|
442
|
+
|
|
443
|
+
async def __aenter__(self) -> "LocalStackTestContainer":
|
|
444
|
+
await self.start()
|
|
445
|
+
return self
|
|
446
|
+
|
|
447
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
448
|
+
await self.stop()
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
# =============================================================================
|
|
452
|
+
# Stream Test Environment
|
|
453
|
+
# =============================================================================
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class StreamTestEnvironment:
|
|
457
|
+
"""Multi-container test environment for streaming tests.
|
|
458
|
+
|
|
459
|
+
Orchestrates multiple test containers for comprehensive
|
|
460
|
+
integration testing.
|
|
461
|
+
|
|
462
|
+
Example:
|
|
463
|
+
>>> async with StreamTestEnvironment(["kafka", "redis"]) as env:
|
|
464
|
+
... kafka_url = env.get_service_url("kafka")
|
|
465
|
+
... redis_url = env.get_service_url("redis")
|
|
466
|
+
... # Run integration tests
|
|
467
|
+
"""
|
|
468
|
+
|
|
469
|
+
_container_types: dict[str, type[ITestContainer]] = {
|
|
470
|
+
"kafka": KafkaTestContainer,
|
|
471
|
+
"redis": RedisTestContainer,
|
|
472
|
+
"localstack": LocalStackTestContainer,
|
|
473
|
+
"kinesis": LocalStackTestContainer, # Alias for Kinesis via LocalStack
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
def __init__(
|
|
477
|
+
self,
|
|
478
|
+
services: list[str],
|
|
479
|
+
configs: dict[str, Any] | None = None,
|
|
480
|
+
):
|
|
481
|
+
"""Initialize test environment.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
services: List of services to start (kafka, redis, localstack, kinesis)
|
|
485
|
+
configs: Optional configurations for each service
|
|
486
|
+
"""
|
|
487
|
+
self._services = services
|
|
488
|
+
self._configs = configs or {}
|
|
489
|
+
self._containers: dict[str, ITestContainer] = {}
|
|
490
|
+
|
|
491
|
+
async def start(self) -> None:
|
|
492
|
+
"""Start all containers."""
|
|
493
|
+
tasks = []
|
|
494
|
+
|
|
495
|
+
for service in self._services:
|
|
496
|
+
if service not in self._container_types:
|
|
497
|
+
raise ValueError(f"Unknown service: {service}")
|
|
498
|
+
|
|
499
|
+
container_cls = self._container_types[service]
|
|
500
|
+
config = self._configs.get(service)
|
|
501
|
+
container = container_cls(config) if config else container_cls()
|
|
502
|
+
self._containers[service] = container
|
|
503
|
+
tasks.append(container.start())
|
|
504
|
+
|
|
505
|
+
# Start all containers in parallel
|
|
506
|
+
await asyncio.gather(*tasks)
|
|
507
|
+
|
|
508
|
+
logger.info(f"Test environment started with services: {self._services}")
|
|
509
|
+
|
|
510
|
+
async def stop(self) -> None:
|
|
511
|
+
"""Stop all containers."""
|
|
512
|
+
tasks = [c.stop() for c in self._containers.values()]
|
|
513
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
514
|
+
self._containers.clear()
|
|
515
|
+
|
|
516
|
+
logger.info("Test environment stopped")
|
|
517
|
+
|
|
518
|
+
def get_container(self, service: str) -> ITestContainer:
|
|
519
|
+
"""Get container by service name.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
service: Service name
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
Container instance
|
|
526
|
+
"""
|
|
527
|
+
if service not in self._containers:
|
|
528
|
+
raise KeyError(f"Service not found: {service}")
|
|
529
|
+
return self._containers[service]
|
|
530
|
+
|
|
531
|
+
def get_service_url(self, service: str) -> str:
|
|
532
|
+
"""Get connection URL for a service.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
service: Service name
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
Connection URL
|
|
539
|
+
"""
|
|
540
|
+
return self.get_container(service).get_connection_url()
|
|
541
|
+
|
|
542
|
+
async def create_kafka_topic(
|
|
543
|
+
self,
|
|
544
|
+
topic: str,
|
|
545
|
+
num_partitions: int = 3,
|
|
546
|
+
) -> None:
|
|
547
|
+
"""Create a Kafka topic.
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
topic: Topic name
|
|
551
|
+
num_partitions: Number of partitions
|
|
552
|
+
"""
|
|
553
|
+
kafka = self._containers.get("kafka")
|
|
554
|
+
if not kafka or not isinstance(kafka, KafkaTestContainer):
|
|
555
|
+
raise RuntimeError("Kafka not available in environment")
|
|
556
|
+
await kafka.create_topic(topic, num_partitions)
|
|
557
|
+
|
|
558
|
+
async def create_kinesis_stream(
|
|
559
|
+
self,
|
|
560
|
+
stream_name: str,
|
|
561
|
+
shard_count: int = 1,
|
|
562
|
+
) -> None:
|
|
563
|
+
"""Create a Kinesis stream.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
stream_name: Stream name
|
|
567
|
+
shard_count: Number of shards
|
|
568
|
+
"""
|
|
569
|
+
localstack = self._containers.get("localstack") or self._containers.get("kinesis")
|
|
570
|
+
if not localstack or not isinstance(localstack, LocalStackTestContainer):
|
|
571
|
+
raise RuntimeError("LocalStack/Kinesis not available in environment")
|
|
572
|
+
await localstack.create_kinesis_stream(stream_name, shard_count)
|
|
573
|
+
|
|
574
|
+
async def __aenter__(self) -> "StreamTestEnvironment":
|
|
575
|
+
await self.start()
|
|
576
|
+
return self
|
|
577
|
+
|
|
578
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
579
|
+
await self.stop()
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
# =============================================================================
|
|
583
|
+
# Utility Functions
|
|
584
|
+
# =============================================================================
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def skip_if_no_docker() -> bool:
|
|
588
|
+
"""Check if Docker is available for testing.
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
True if Docker is available
|
|
592
|
+
"""
|
|
593
|
+
import shutil
|
|
594
|
+
|
|
595
|
+
return shutil.which("docker") is not None
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def require_testcontainers():
|
|
599
|
+
"""Decorator to skip tests if testcontainers is not available."""
|
|
600
|
+
import functools
|
|
601
|
+
|
|
602
|
+
def decorator(func):
|
|
603
|
+
@functools.wraps(func)
|
|
604
|
+
async def wrapper(*args, **kwargs):
|
|
605
|
+
try:
|
|
606
|
+
import testcontainers
|
|
607
|
+
except ImportError:
|
|
608
|
+
import pytest
|
|
609
|
+
|
|
610
|
+
pytest.skip("testcontainers not installed")
|
|
611
|
+
return await func(*args, **kwargs)
|
|
612
|
+
|
|
613
|
+
return wrapper
|
|
614
|
+
|
|
615
|
+
return decorator
|