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,982 @@
|
|
|
1
|
+
"""OpenTelemetry adapter implementation.
|
|
2
|
+
|
|
3
|
+
This module provides adapter classes that wrap both Truthound native
|
|
4
|
+
and OpenTelemetry SDK implementations behind a unified interface.
|
|
5
|
+
|
|
6
|
+
The adapter pattern allows:
|
|
7
|
+
- Seamless switching between backends
|
|
8
|
+
- Gradual migration from Truthound to OTEL SDK
|
|
9
|
+
- Using OTEL exporters with Truthound spans
|
|
10
|
+
- Using Truthound spans with OTEL processors
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import threading
|
|
17
|
+
from contextlib import contextmanager
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from enum import Enum, auto
|
|
20
|
+
from typing import Any, Iterator, Mapping, Sequence
|
|
21
|
+
|
|
22
|
+
from truthound.observability.tracing.otel.detection import (
|
|
23
|
+
detect_otel_availability,
|
|
24
|
+
is_otel_sdk_available,
|
|
25
|
+
)
|
|
26
|
+
from truthound.observability.tracing.otel.protocols import (
|
|
27
|
+
SpanContextProtocol,
|
|
28
|
+
SpanProtocol,
|
|
29
|
+
TracerProtocol,
|
|
30
|
+
TracerProviderProtocol,
|
|
31
|
+
SpanProcessorProtocol,
|
|
32
|
+
SpanExporterProtocol,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# =============================================================================
|
|
39
|
+
# Backend Selection
|
|
40
|
+
# =============================================================================
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TracingBackend(str, Enum):
|
|
44
|
+
"""Available tracing backends."""
|
|
45
|
+
|
|
46
|
+
AUTO = "auto" # Auto-detect best available
|
|
47
|
+
TRUTHOUND = "truthound" # Force Truthound native
|
|
48
|
+
OPENTELEMETRY = "opentelemetry" # Force OpenTelemetry SDK
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class AdapterConfig:
|
|
53
|
+
"""Configuration for the tracing adapter.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
backend: Which backend to use.
|
|
57
|
+
service_name: Service name for traces.
|
|
58
|
+
service_version: Service version.
|
|
59
|
+
environment: Deployment environment.
|
|
60
|
+
exporter_type: Type of exporter (console, otlp, jaeger, zipkin).
|
|
61
|
+
exporter_endpoint: Exporter endpoint URL.
|
|
62
|
+
exporter_headers: Additional headers for exporter.
|
|
63
|
+
sampling_ratio: Sampling ratio (0.0 to 1.0).
|
|
64
|
+
batch_export: Use batch span processor.
|
|
65
|
+
propagators: List of propagator types.
|
|
66
|
+
auto_instrument: Enable auto-instrumentation (if OTEL SDK).
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
backend: TracingBackend = TracingBackend.AUTO
|
|
70
|
+
service_name: str = "truthound"
|
|
71
|
+
service_version: str = ""
|
|
72
|
+
environment: str = ""
|
|
73
|
+
exporter_type: str = "console"
|
|
74
|
+
exporter_endpoint: str = ""
|
|
75
|
+
exporter_headers: dict[str, str] = field(default_factory=dict)
|
|
76
|
+
sampling_ratio: float = 1.0
|
|
77
|
+
batch_export: bool = True
|
|
78
|
+
propagators: list[str] = field(default_factory=lambda: ["w3c"])
|
|
79
|
+
auto_instrument: bool = False
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def from_env(cls) -> "AdapterConfig":
|
|
83
|
+
"""Create configuration from environment variables."""
|
|
84
|
+
import os
|
|
85
|
+
|
|
86
|
+
backend_str = os.environ.get("TRUTHOUND_TRACING_BACKEND", "auto").lower()
|
|
87
|
+
backend = TracingBackend(backend_str) if backend_str in [b.value for b in TracingBackend] else TracingBackend.AUTO
|
|
88
|
+
|
|
89
|
+
# Parse headers
|
|
90
|
+
headers = {}
|
|
91
|
+
headers_str = os.environ.get("OTEL_EXPORTER_OTLP_HEADERS", "")
|
|
92
|
+
if headers_str:
|
|
93
|
+
for pair in headers_str.split(","):
|
|
94
|
+
if "=" in pair:
|
|
95
|
+
key, value = pair.split("=", 1)
|
|
96
|
+
headers[key.strip()] = value.strip()
|
|
97
|
+
|
|
98
|
+
# Determine exporter type
|
|
99
|
+
exporter_type = "console"
|
|
100
|
+
endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "")
|
|
101
|
+
if endpoint:
|
|
102
|
+
exporter_type = "otlp"
|
|
103
|
+
|
|
104
|
+
# Parse sampling ratio
|
|
105
|
+
try:
|
|
106
|
+
sampling_ratio = float(os.environ.get("OTEL_TRACES_SAMPLER_ARG", "1.0"))
|
|
107
|
+
except ValueError:
|
|
108
|
+
sampling_ratio = 1.0
|
|
109
|
+
|
|
110
|
+
return cls(
|
|
111
|
+
backend=backend,
|
|
112
|
+
service_name=os.environ.get("OTEL_SERVICE_NAME", "truthound"),
|
|
113
|
+
service_version=os.environ.get("OTEL_SERVICE_VERSION", ""),
|
|
114
|
+
environment=os.environ.get("DEPLOYMENT_ENVIRONMENT", ""),
|
|
115
|
+
exporter_type=exporter_type,
|
|
116
|
+
exporter_endpoint=endpoint,
|
|
117
|
+
exporter_headers=headers,
|
|
118
|
+
sampling_ratio=sampling_ratio,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# =============================================================================
|
|
123
|
+
# Span Context Adapter
|
|
124
|
+
# =============================================================================
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class SpanContextAdapter:
|
|
128
|
+
"""Adapter for span context that works with both backends.
|
|
129
|
+
|
|
130
|
+
Normalizes span context data from either Truthound or OTEL format.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def __init__(
|
|
134
|
+
self,
|
|
135
|
+
trace_id: str | int,
|
|
136
|
+
span_id: str | int,
|
|
137
|
+
trace_flags: int = 1,
|
|
138
|
+
trace_state: str = "",
|
|
139
|
+
is_remote: bool = False,
|
|
140
|
+
_native: Any = None,
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Initialize span context adapter.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
trace_id: Trace ID (hex string or int).
|
|
146
|
+
span_id: Span ID (hex string or int).
|
|
147
|
+
trace_flags: Trace flags (0=not sampled, 1=sampled).
|
|
148
|
+
trace_state: W3C trace state string.
|
|
149
|
+
is_remote: Whether context is from remote parent.
|
|
150
|
+
_native: Native span context object (for unwrapping).
|
|
151
|
+
"""
|
|
152
|
+
# Normalize to hex strings
|
|
153
|
+
if isinstance(trace_id, int):
|
|
154
|
+
self._trace_id = format(trace_id, "032x")
|
|
155
|
+
else:
|
|
156
|
+
self._trace_id = str(trace_id)
|
|
157
|
+
|
|
158
|
+
if isinstance(span_id, int):
|
|
159
|
+
self._span_id = format(span_id, "016x")
|
|
160
|
+
else:
|
|
161
|
+
self._span_id = str(span_id)
|
|
162
|
+
|
|
163
|
+
self._trace_flags = trace_flags
|
|
164
|
+
self._trace_state = trace_state
|
|
165
|
+
self._is_remote = is_remote
|
|
166
|
+
self._native = _native
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def trace_id(self) -> str:
|
|
170
|
+
"""Get trace ID as hex string."""
|
|
171
|
+
return self._trace_id
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def trace_id_int(self) -> int:
|
|
175
|
+
"""Get trace ID as integer."""
|
|
176
|
+
return int(self._trace_id, 16)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def span_id(self) -> str:
|
|
180
|
+
"""Get span ID as hex string."""
|
|
181
|
+
return self._span_id
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def span_id_int(self) -> int:
|
|
185
|
+
"""Get span ID as integer."""
|
|
186
|
+
return int(self._span_id, 16)
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def trace_flags(self) -> int:
|
|
190
|
+
"""Get trace flags."""
|
|
191
|
+
return self._trace_flags
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def trace_state(self) -> str:
|
|
195
|
+
"""Get trace state."""
|
|
196
|
+
return self._trace_state
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def is_valid(self) -> bool:
|
|
200
|
+
"""Check if context is valid."""
|
|
201
|
+
return bool(self._trace_id and self._span_id and
|
|
202
|
+
self._trace_id != "0" * 32 and self._span_id != "0" * 16)
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def is_remote(self) -> bool:
|
|
206
|
+
"""Check if context is from remote parent."""
|
|
207
|
+
return self._is_remote
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def is_sampled(self) -> bool:
|
|
211
|
+
"""Check if span is sampled."""
|
|
212
|
+
return bool(self._trace_flags & 0x01)
|
|
213
|
+
|
|
214
|
+
def to_w3c_traceparent(self) -> str:
|
|
215
|
+
"""Convert to W3C traceparent header."""
|
|
216
|
+
return f"00-{self._trace_id}-{self._span_id}-{self._trace_flags:02x}"
|
|
217
|
+
|
|
218
|
+
@classmethod
|
|
219
|
+
def from_w3c_traceparent(cls, header: str) -> "SpanContextAdapter | None":
|
|
220
|
+
"""Parse from W3C traceparent header."""
|
|
221
|
+
try:
|
|
222
|
+
parts = header.split("-")
|
|
223
|
+
if len(parts) != 4 or parts[0] != "00":
|
|
224
|
+
return None
|
|
225
|
+
return cls(
|
|
226
|
+
trace_id=parts[1],
|
|
227
|
+
span_id=parts[2],
|
|
228
|
+
trace_flags=int(parts[3], 16),
|
|
229
|
+
is_remote=True,
|
|
230
|
+
)
|
|
231
|
+
except (ValueError, IndexError):
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def from_truthound(cls, ctx: Any) -> "SpanContextAdapter":
|
|
236
|
+
"""Create from Truthound SpanContextData."""
|
|
237
|
+
return cls(
|
|
238
|
+
trace_id=ctx.trace_id,
|
|
239
|
+
span_id=ctx.span_id,
|
|
240
|
+
trace_flags=ctx.trace_flags,
|
|
241
|
+
trace_state=getattr(ctx, "trace_state", ""),
|
|
242
|
+
is_remote=getattr(ctx, "is_remote", False),
|
|
243
|
+
_native=ctx,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
@classmethod
|
|
247
|
+
def from_otel(cls, ctx: Any) -> "SpanContextAdapter":
|
|
248
|
+
"""Create from OpenTelemetry SpanContext."""
|
|
249
|
+
return cls(
|
|
250
|
+
trace_id=ctx.trace_id,
|
|
251
|
+
span_id=ctx.span_id,
|
|
252
|
+
trace_flags=ctx.trace_flags,
|
|
253
|
+
trace_state=str(ctx.trace_state) if ctx.trace_state else "",
|
|
254
|
+
is_remote=ctx.is_remote,
|
|
255
|
+
_native=ctx,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def to_truthound(self) -> Any:
|
|
259
|
+
"""Convert to Truthound SpanContextData."""
|
|
260
|
+
from truthound.observability.tracing.span import SpanContextData
|
|
261
|
+
|
|
262
|
+
return SpanContextData(
|
|
263
|
+
trace_id=self._trace_id,
|
|
264
|
+
span_id=self._span_id,
|
|
265
|
+
trace_flags=self._trace_flags,
|
|
266
|
+
trace_state=self._trace_state,
|
|
267
|
+
is_remote=self._is_remote,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def to_otel(self) -> Any:
|
|
271
|
+
"""Convert to OpenTelemetry SpanContext."""
|
|
272
|
+
if not is_otel_sdk_available():
|
|
273
|
+
raise ImportError("OpenTelemetry SDK not available")
|
|
274
|
+
|
|
275
|
+
from opentelemetry.trace import SpanContext, TraceState
|
|
276
|
+
|
|
277
|
+
trace_state = TraceState()
|
|
278
|
+
if self._trace_state:
|
|
279
|
+
for pair in self._trace_state.split(","):
|
|
280
|
+
if "=" in pair:
|
|
281
|
+
key, value = pair.split("=", 1)
|
|
282
|
+
trace_state = trace_state.add(key.strip(), value.strip())
|
|
283
|
+
|
|
284
|
+
return SpanContext(
|
|
285
|
+
trace_id=self.trace_id_int,
|
|
286
|
+
span_id=self.span_id_int,
|
|
287
|
+
is_remote=self._is_remote,
|
|
288
|
+
trace_flags=self._trace_flags,
|
|
289
|
+
trace_state=trace_state,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
def unwrap(self) -> Any:
|
|
293
|
+
"""Get the native span context object."""
|
|
294
|
+
return self._native
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# =============================================================================
|
|
298
|
+
# Span Adapter
|
|
299
|
+
# =============================================================================
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class SpanAdapter:
|
|
303
|
+
"""Adapter for spans that works with both backends.
|
|
304
|
+
|
|
305
|
+
Provides a unified interface regardless of whether the underlying
|
|
306
|
+
span is from Truthound or OpenTelemetry SDK.
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
def __init__(self, span: Any, backend: TracingBackend) -> None:
|
|
310
|
+
"""Initialize span adapter.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
span: Native span object.
|
|
314
|
+
backend: Backend type.
|
|
315
|
+
"""
|
|
316
|
+
self._span = span
|
|
317
|
+
self._backend = backend
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def context(self) -> SpanContextAdapter:
|
|
321
|
+
"""Get span context."""
|
|
322
|
+
if self._backend == TracingBackend.OPENTELEMETRY:
|
|
323
|
+
return SpanContextAdapter.from_otel(self._span.get_span_context())
|
|
324
|
+
else:
|
|
325
|
+
return SpanContextAdapter.from_truthound(self._span.context)
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def name(self) -> str:
|
|
329
|
+
"""Get span name."""
|
|
330
|
+
return self._span.name
|
|
331
|
+
|
|
332
|
+
def get_span_context(self) -> SpanContextAdapter:
|
|
333
|
+
"""Get span context (OTEL API compatible)."""
|
|
334
|
+
return self.context
|
|
335
|
+
|
|
336
|
+
def set_attribute(self, key: str, value: Any) -> "SpanAdapter":
|
|
337
|
+
"""Set a span attribute."""
|
|
338
|
+
self._span.set_attribute(key, value)
|
|
339
|
+
return self
|
|
340
|
+
|
|
341
|
+
def set_attributes(self, attributes: Mapping[str, Any]) -> "SpanAdapter":
|
|
342
|
+
"""Set multiple attributes."""
|
|
343
|
+
if self._backend == TracingBackend.OPENTELEMETRY:
|
|
344
|
+
self._span.set_attributes(dict(attributes))
|
|
345
|
+
else:
|
|
346
|
+
self._span.set_attributes(attributes)
|
|
347
|
+
return self
|
|
348
|
+
|
|
349
|
+
def add_event(
|
|
350
|
+
self,
|
|
351
|
+
name: str,
|
|
352
|
+
attributes: Mapping[str, Any] | None = None,
|
|
353
|
+
timestamp: int | float | None = None,
|
|
354
|
+
) -> "SpanAdapter":
|
|
355
|
+
"""Add an event to the span."""
|
|
356
|
+
self._span.add_event(name, attributes=attributes, timestamp=timestamp)
|
|
357
|
+
return self
|
|
358
|
+
|
|
359
|
+
def set_status(self, status: Any, description: str | None = None) -> "SpanAdapter":
|
|
360
|
+
"""Set span status."""
|
|
361
|
+
if self._backend == TracingBackend.OPENTELEMETRY:
|
|
362
|
+
from opentelemetry.trace import StatusCode
|
|
363
|
+
|
|
364
|
+
if isinstance(status, str):
|
|
365
|
+
status_map = {"ok": StatusCode.OK, "error": StatusCode.ERROR}
|
|
366
|
+
status = status_map.get(status.lower(), StatusCode.UNSET)
|
|
367
|
+
self._span.set_status(status, description)
|
|
368
|
+
else:
|
|
369
|
+
from truthound.observability.tracing.span import StatusCode
|
|
370
|
+
|
|
371
|
+
if isinstance(status, str):
|
|
372
|
+
status_map = {"ok": StatusCode.OK, "error": StatusCode.ERROR}
|
|
373
|
+
status = status_map.get(status.lower(), StatusCode.UNSET)
|
|
374
|
+
self._span.set_status(status, description or "")
|
|
375
|
+
return self
|
|
376
|
+
|
|
377
|
+
def record_exception(
|
|
378
|
+
self,
|
|
379
|
+
exception: BaseException,
|
|
380
|
+
attributes: Mapping[str, Any] | None = None,
|
|
381
|
+
timestamp: int | float | None = None,
|
|
382
|
+
escaped: bool = False,
|
|
383
|
+
) -> "SpanAdapter":
|
|
384
|
+
"""Record an exception."""
|
|
385
|
+
if self._backend == TracingBackend.OPENTELEMETRY:
|
|
386
|
+
self._span.record_exception(exception, attributes=attributes)
|
|
387
|
+
else:
|
|
388
|
+
self._span.record_exception(exception, attributes=attributes, escaped=escaped)
|
|
389
|
+
return self
|
|
390
|
+
|
|
391
|
+
def update_name(self, name: str) -> "SpanAdapter":
|
|
392
|
+
"""Update span name."""
|
|
393
|
+
self._span.update_name(name)
|
|
394
|
+
return self
|
|
395
|
+
|
|
396
|
+
def end(self, end_time: int | float | None = None) -> None:
|
|
397
|
+
"""End the span."""
|
|
398
|
+
self._span.end(end_time)
|
|
399
|
+
|
|
400
|
+
def is_recording(self) -> bool:
|
|
401
|
+
"""Check if span is recording."""
|
|
402
|
+
return self._span.is_recording()
|
|
403
|
+
|
|
404
|
+
def unwrap(self) -> Any:
|
|
405
|
+
"""Get the native span object."""
|
|
406
|
+
return self._span
|
|
407
|
+
|
|
408
|
+
def __enter__(self) -> "SpanAdapter":
|
|
409
|
+
"""Enter context manager."""
|
|
410
|
+
return self
|
|
411
|
+
|
|
412
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
413
|
+
"""Exit context manager."""
|
|
414
|
+
if exc_val is not None:
|
|
415
|
+
self.record_exception(exc_val, escaped=True)
|
|
416
|
+
self.set_status("error", str(exc_val))
|
|
417
|
+
self.end()
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
# =============================================================================
|
|
421
|
+
# Tracer Adapter
|
|
422
|
+
# =============================================================================
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class TracerAdapter:
|
|
426
|
+
"""Adapter for tracers that works with both backends.
|
|
427
|
+
|
|
428
|
+
Provides a unified interface for creating spans regardless of
|
|
429
|
+
the underlying implementation.
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
def __init__(self, tracer: Any, backend: TracingBackend) -> None:
|
|
433
|
+
"""Initialize tracer adapter.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
tracer: Native tracer object.
|
|
437
|
+
backend: Backend type.
|
|
438
|
+
"""
|
|
439
|
+
self._tracer = tracer
|
|
440
|
+
self._backend = backend
|
|
441
|
+
|
|
442
|
+
@property
|
|
443
|
+
def name(self) -> str:
|
|
444
|
+
"""Get tracer name."""
|
|
445
|
+
if hasattr(self._tracer, "name"):
|
|
446
|
+
return self._tracer.name
|
|
447
|
+
if hasattr(self._tracer, "_name"):
|
|
448
|
+
return self._tracer._name
|
|
449
|
+
return "unknown"
|
|
450
|
+
|
|
451
|
+
def start_span(
|
|
452
|
+
self,
|
|
453
|
+
name: str,
|
|
454
|
+
context: SpanContextAdapter | None = None,
|
|
455
|
+
kind: Any = None,
|
|
456
|
+
attributes: Mapping[str, Any] | None = None,
|
|
457
|
+
links: Sequence[Any] | None = None,
|
|
458
|
+
start_time: int | float | None = None,
|
|
459
|
+
record_exception: bool = True,
|
|
460
|
+
set_status_on_exception: bool = True,
|
|
461
|
+
) -> SpanAdapter:
|
|
462
|
+
"""Start a new span.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
name: Span name.
|
|
466
|
+
context: Parent context (optional).
|
|
467
|
+
kind: Span kind.
|
|
468
|
+
attributes: Initial attributes.
|
|
469
|
+
links: Links to other spans.
|
|
470
|
+
start_time: Start time.
|
|
471
|
+
record_exception: Whether to record exceptions.
|
|
472
|
+
set_status_on_exception: Whether to set error status on exception.
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
SpanAdapter wrapping the created span.
|
|
476
|
+
"""
|
|
477
|
+
if self._backend == TracingBackend.OPENTELEMETRY:
|
|
478
|
+
return self._start_span_otel(
|
|
479
|
+
name, context, kind, attributes, links, start_time
|
|
480
|
+
)
|
|
481
|
+
else:
|
|
482
|
+
return self._start_span_truthound(
|
|
483
|
+
name, context, kind, attributes, links, start_time
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
def _start_span_truthound(
|
|
487
|
+
self,
|
|
488
|
+
name: str,
|
|
489
|
+
context: SpanContextAdapter | None,
|
|
490
|
+
kind: Any,
|
|
491
|
+
attributes: Mapping[str, Any] | None,
|
|
492
|
+
links: Sequence[Any] | None,
|
|
493
|
+
start_time: int | float | None,
|
|
494
|
+
) -> SpanAdapter:
|
|
495
|
+
"""Start span using Truthound backend."""
|
|
496
|
+
from truthound.observability.tracing.span import SpanKind
|
|
497
|
+
|
|
498
|
+
# Convert kind
|
|
499
|
+
span_kind = SpanKind.INTERNAL
|
|
500
|
+
if kind is not None:
|
|
501
|
+
if isinstance(kind, SpanKind):
|
|
502
|
+
span_kind = kind
|
|
503
|
+
elif hasattr(kind, "name"):
|
|
504
|
+
kind_map = {
|
|
505
|
+
"INTERNAL": SpanKind.INTERNAL,
|
|
506
|
+
"SERVER": SpanKind.SERVER,
|
|
507
|
+
"CLIENT": SpanKind.CLIENT,
|
|
508
|
+
"PRODUCER": SpanKind.PRODUCER,
|
|
509
|
+
"CONSUMER": SpanKind.CONSUMER,
|
|
510
|
+
}
|
|
511
|
+
span_kind = kind_map.get(kind.name, SpanKind.INTERNAL)
|
|
512
|
+
|
|
513
|
+
# Convert parent context
|
|
514
|
+
parent = context.to_truthound() if context else None
|
|
515
|
+
|
|
516
|
+
span = self._tracer.start_span(
|
|
517
|
+
name=name,
|
|
518
|
+
kind=span_kind,
|
|
519
|
+
attributes=attributes,
|
|
520
|
+
links=links,
|
|
521
|
+
start_time=start_time,
|
|
522
|
+
parent=parent,
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
return SpanAdapter(span, self._backend)
|
|
526
|
+
|
|
527
|
+
def _start_span_otel(
|
|
528
|
+
self,
|
|
529
|
+
name: str,
|
|
530
|
+
context: SpanContextAdapter | None,
|
|
531
|
+
kind: Any,
|
|
532
|
+
attributes: Mapping[str, Any] | None,
|
|
533
|
+
links: Sequence[Any] | None,
|
|
534
|
+
start_time: int | float | None,
|
|
535
|
+
) -> SpanAdapter:
|
|
536
|
+
"""Start span using OpenTelemetry backend."""
|
|
537
|
+
from opentelemetry.trace import SpanKind
|
|
538
|
+
|
|
539
|
+
# Convert kind
|
|
540
|
+
span_kind = SpanKind.INTERNAL
|
|
541
|
+
if kind is not None:
|
|
542
|
+
if isinstance(kind, SpanKind):
|
|
543
|
+
span_kind = kind
|
|
544
|
+
elif hasattr(kind, "name"):
|
|
545
|
+
kind_map = {
|
|
546
|
+
"INTERNAL": SpanKind.INTERNAL,
|
|
547
|
+
"SERVER": SpanKind.SERVER,
|
|
548
|
+
"CLIENT": SpanKind.CLIENT,
|
|
549
|
+
"PRODUCER": SpanKind.PRODUCER,
|
|
550
|
+
"CONSUMER": SpanKind.CONSUMER,
|
|
551
|
+
}
|
|
552
|
+
span_kind = kind_map.get(kind.name, SpanKind.INTERNAL)
|
|
553
|
+
|
|
554
|
+
# Start span (context handling differs in OTEL)
|
|
555
|
+
span = self._tracer.start_span(
|
|
556
|
+
name=name,
|
|
557
|
+
kind=span_kind,
|
|
558
|
+
attributes=dict(attributes) if attributes else None,
|
|
559
|
+
start_time=start_time,
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
return SpanAdapter(span, self._backend)
|
|
563
|
+
|
|
564
|
+
@contextmanager
|
|
565
|
+
def start_as_current_span(
|
|
566
|
+
self,
|
|
567
|
+
name: str,
|
|
568
|
+
context: SpanContextAdapter | None = None,
|
|
569
|
+
kind: Any = None,
|
|
570
|
+
attributes: Mapping[str, Any] | None = None,
|
|
571
|
+
links: Sequence[Any] | None = None,
|
|
572
|
+
start_time: int | float | None = None,
|
|
573
|
+
record_exception: bool = True,
|
|
574
|
+
set_status_on_exception: bool = True,
|
|
575
|
+
end_on_exit: bool = True,
|
|
576
|
+
) -> Iterator[SpanAdapter]:
|
|
577
|
+
"""Start a span and set it as current.
|
|
578
|
+
|
|
579
|
+
Context manager that creates a span, makes it the current span,
|
|
580
|
+
and ends it when the context exits.
|
|
581
|
+
|
|
582
|
+
Yields:
|
|
583
|
+
SpanAdapter for the created span.
|
|
584
|
+
"""
|
|
585
|
+
if self._backend == TracingBackend.OPENTELEMETRY:
|
|
586
|
+
yield from self._start_as_current_otel(
|
|
587
|
+
name, kind, attributes, links, start_time,
|
|
588
|
+
record_exception, set_status_on_exception, end_on_exit
|
|
589
|
+
)
|
|
590
|
+
else:
|
|
591
|
+
yield from self._start_as_current_truthound(
|
|
592
|
+
name, context, kind, attributes, links, start_time,
|
|
593
|
+
record_exception, set_status_on_exception, end_on_exit
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
def _start_as_current_truthound(
|
|
597
|
+
self,
|
|
598
|
+
name: str,
|
|
599
|
+
context: SpanContextAdapter | None,
|
|
600
|
+
kind: Any,
|
|
601
|
+
attributes: Mapping[str, Any] | None,
|
|
602
|
+
links: Sequence[Any] | None,
|
|
603
|
+
start_time: int | float | None,
|
|
604
|
+
record_exception: bool,
|
|
605
|
+
set_status_on_exception: bool,
|
|
606
|
+
end_on_exit: bool,
|
|
607
|
+
) -> Iterator[SpanAdapter]:
|
|
608
|
+
"""Start as current using Truthound backend."""
|
|
609
|
+
with self._tracer.start_as_current_span(
|
|
610
|
+
name=name,
|
|
611
|
+
kind=kind,
|
|
612
|
+
attributes=attributes,
|
|
613
|
+
links=links,
|
|
614
|
+
start_time=start_time,
|
|
615
|
+
record_exception=record_exception,
|
|
616
|
+
set_status_on_exception=set_status_on_exception,
|
|
617
|
+
end_on_exit=end_on_exit,
|
|
618
|
+
) as span:
|
|
619
|
+
yield SpanAdapter(span, self._backend)
|
|
620
|
+
|
|
621
|
+
def _start_as_current_otel(
|
|
622
|
+
self,
|
|
623
|
+
name: str,
|
|
624
|
+
kind: Any,
|
|
625
|
+
attributes: Mapping[str, Any] | None,
|
|
626
|
+
links: Sequence[Any] | None,
|
|
627
|
+
start_time: int | float | None,
|
|
628
|
+
record_exception: bool,
|
|
629
|
+
set_status_on_exception: bool,
|
|
630
|
+
end_on_exit: bool,
|
|
631
|
+
) -> Iterator[SpanAdapter]:
|
|
632
|
+
"""Start as current using OpenTelemetry backend."""
|
|
633
|
+
from opentelemetry.trace import SpanKind
|
|
634
|
+
|
|
635
|
+
span_kind = SpanKind.INTERNAL
|
|
636
|
+
if kind is not None:
|
|
637
|
+
if hasattr(kind, "name"):
|
|
638
|
+
kind_map = {
|
|
639
|
+
"INTERNAL": SpanKind.INTERNAL,
|
|
640
|
+
"SERVER": SpanKind.SERVER,
|
|
641
|
+
"CLIENT": SpanKind.CLIENT,
|
|
642
|
+
"PRODUCER": SpanKind.PRODUCER,
|
|
643
|
+
"CONSUMER": SpanKind.CONSUMER,
|
|
644
|
+
}
|
|
645
|
+
span_kind = kind_map.get(kind.name, SpanKind.INTERNAL)
|
|
646
|
+
|
|
647
|
+
with self._tracer.start_as_current_span(
|
|
648
|
+
name=name,
|
|
649
|
+
kind=span_kind,
|
|
650
|
+
attributes=dict(attributes) if attributes else None,
|
|
651
|
+
start_time=start_time,
|
|
652
|
+
record_exception=record_exception,
|
|
653
|
+
set_status_on_exception=set_status_on_exception,
|
|
654
|
+
end_on_exit=end_on_exit,
|
|
655
|
+
) as span:
|
|
656
|
+
yield SpanAdapter(span, self._backend)
|
|
657
|
+
|
|
658
|
+
def unwrap(self) -> Any:
|
|
659
|
+
"""Get the native tracer object."""
|
|
660
|
+
return self._tracer
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
# =============================================================================
|
|
664
|
+
# TracerProvider Adapter
|
|
665
|
+
# =============================================================================
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
class TracerProviderAdapter:
|
|
669
|
+
"""Adapter for tracer providers that works with both backends.
|
|
670
|
+
|
|
671
|
+
Provides a unified interface for obtaining tracers regardless of
|
|
672
|
+
the underlying implementation.
|
|
673
|
+
"""
|
|
674
|
+
|
|
675
|
+
def __init__(self, provider: Any, backend: TracingBackend) -> None:
|
|
676
|
+
"""Initialize tracer provider adapter.
|
|
677
|
+
|
|
678
|
+
Args:
|
|
679
|
+
provider: Native tracer provider object.
|
|
680
|
+
backend: Backend type.
|
|
681
|
+
"""
|
|
682
|
+
self._provider = provider
|
|
683
|
+
self._backend = backend
|
|
684
|
+
self._tracers: dict[tuple[str, str], TracerAdapter] = {}
|
|
685
|
+
self._lock = threading.Lock()
|
|
686
|
+
|
|
687
|
+
@property
|
|
688
|
+
def backend(self) -> TracingBackend:
|
|
689
|
+
"""Get the backend type."""
|
|
690
|
+
return self._backend
|
|
691
|
+
|
|
692
|
+
def get_tracer(
|
|
693
|
+
self,
|
|
694
|
+
name: str,
|
|
695
|
+
version: str = "",
|
|
696
|
+
schema_url: str = "",
|
|
697
|
+
) -> TracerAdapter:
|
|
698
|
+
"""Get a tracer.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
name: Instrumentation library name.
|
|
702
|
+
version: Instrumentation library version.
|
|
703
|
+
schema_url: Schema URL.
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
TracerAdapter wrapping the tracer.
|
|
707
|
+
"""
|
|
708
|
+
key = (name, version)
|
|
709
|
+
|
|
710
|
+
with self._lock:
|
|
711
|
+
if key not in self._tracers:
|
|
712
|
+
if self._backend == TracingBackend.OPENTELEMETRY:
|
|
713
|
+
tracer = self._provider.get_tracer(name, version, schema_url)
|
|
714
|
+
else:
|
|
715
|
+
tracer = self._provider.get_tracer(name, version, schema_url)
|
|
716
|
+
self._tracers[key] = TracerAdapter(tracer, self._backend)
|
|
717
|
+
return self._tracers[key]
|
|
718
|
+
|
|
719
|
+
def add_span_processor(self, processor: Any) -> None:
|
|
720
|
+
"""Add a span processor.
|
|
721
|
+
|
|
722
|
+
Args:
|
|
723
|
+
processor: Span processor to add.
|
|
724
|
+
"""
|
|
725
|
+
if hasattr(self._provider, "add_span_processor"):
|
|
726
|
+
self._provider.add_span_processor(processor)
|
|
727
|
+
elif hasattr(self._provider, "add_processor"):
|
|
728
|
+
self._provider.add_processor(processor)
|
|
729
|
+
|
|
730
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
731
|
+
"""Force flush all processors.
|
|
732
|
+
|
|
733
|
+
Args:
|
|
734
|
+
timeout_millis: Timeout in milliseconds.
|
|
735
|
+
|
|
736
|
+
Returns:
|
|
737
|
+
True if successful.
|
|
738
|
+
"""
|
|
739
|
+
return self._provider.force_flush(timeout_millis)
|
|
740
|
+
|
|
741
|
+
def shutdown(self) -> bool:
|
|
742
|
+
"""Shutdown the provider.
|
|
743
|
+
|
|
744
|
+
Returns:
|
|
745
|
+
True if successful.
|
|
746
|
+
"""
|
|
747
|
+
return self._provider.shutdown()
|
|
748
|
+
|
|
749
|
+
def unwrap(self) -> Any:
|
|
750
|
+
"""Get the native provider object."""
|
|
751
|
+
return self._provider
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
# =============================================================================
|
|
755
|
+
# Global State
|
|
756
|
+
# =============================================================================
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
_global_adapter: TracerProviderAdapter | None = None
|
|
760
|
+
_adapter_lock = threading.Lock()
|
|
761
|
+
_current_config: AdapterConfig | None = None
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def _create_truthound_provider(config: AdapterConfig) -> TracerProviderAdapter:
|
|
765
|
+
"""Create a Truthound-based provider."""
|
|
766
|
+
from truthound.observability.tracing.config import configure_tracing, TracingConfig
|
|
767
|
+
|
|
768
|
+
tracing_config = TracingConfig(
|
|
769
|
+
service_name=config.service_name,
|
|
770
|
+
service_version=config.service_version,
|
|
771
|
+
environment=config.environment,
|
|
772
|
+
exporter=config.exporter_type,
|
|
773
|
+
endpoint=config.exporter_endpoint,
|
|
774
|
+
headers=config.exporter_headers,
|
|
775
|
+
sampling_ratio=config.sampling_ratio,
|
|
776
|
+
batch_export=config.batch_export,
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
provider = configure_tracing(tracing_config, set_global=False)
|
|
780
|
+
return TracerProviderAdapter(provider, TracingBackend.TRUTHOUND)
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
def _create_otel_provider(config: AdapterConfig) -> TracerProviderAdapter:
|
|
784
|
+
"""Create an OpenTelemetry SDK-based provider."""
|
|
785
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
786
|
+
from opentelemetry.sdk.resources import Resource, SERVICE_NAME, SERVICE_VERSION
|
|
787
|
+
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBased
|
|
788
|
+
|
|
789
|
+
# Create resource
|
|
790
|
+
resource_attrs = {SERVICE_NAME: config.service_name}
|
|
791
|
+
if config.service_version:
|
|
792
|
+
resource_attrs[SERVICE_VERSION] = config.service_version
|
|
793
|
+
if config.environment:
|
|
794
|
+
resource_attrs["deployment.environment"] = config.environment
|
|
795
|
+
|
|
796
|
+
resource = Resource.create(resource_attrs)
|
|
797
|
+
|
|
798
|
+
# Create sampler
|
|
799
|
+
sampler = ParentBased(root=TraceIdRatioBased(config.sampling_ratio))
|
|
800
|
+
|
|
801
|
+
# Create provider
|
|
802
|
+
provider = TracerProvider(resource=resource, sampler=sampler)
|
|
803
|
+
|
|
804
|
+
# Add exporter
|
|
805
|
+
if config.exporter_type == "otlp":
|
|
806
|
+
try:
|
|
807
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
808
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
809
|
+
|
|
810
|
+
exporter = OTLPSpanExporter(
|
|
811
|
+
endpoint=config.exporter_endpoint or "http://localhost:4317",
|
|
812
|
+
)
|
|
813
|
+
processor = BatchSpanProcessor(exporter)
|
|
814
|
+
provider.add_span_processor(processor)
|
|
815
|
+
except ImportError:
|
|
816
|
+
logger.warning("OTLP exporter not available, using console")
|
|
817
|
+
_add_console_processor(provider)
|
|
818
|
+
|
|
819
|
+
elif config.exporter_type == "jaeger":
|
|
820
|
+
try:
|
|
821
|
+
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
|
|
822
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
823
|
+
|
|
824
|
+
exporter = JaegerExporter()
|
|
825
|
+
processor = BatchSpanProcessor(exporter)
|
|
826
|
+
provider.add_span_processor(processor)
|
|
827
|
+
except ImportError:
|
|
828
|
+
logger.warning("Jaeger exporter not available, using console")
|
|
829
|
+
_add_console_processor(provider)
|
|
830
|
+
|
|
831
|
+
elif config.exporter_type == "zipkin":
|
|
832
|
+
try:
|
|
833
|
+
from opentelemetry.exporter.zipkin.json import ZipkinExporter
|
|
834
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
835
|
+
|
|
836
|
+
exporter = ZipkinExporter(endpoint=config.exporter_endpoint)
|
|
837
|
+
processor = BatchSpanProcessor(exporter)
|
|
838
|
+
provider.add_span_processor(processor)
|
|
839
|
+
except ImportError:
|
|
840
|
+
logger.warning("Zipkin exporter not available, using console")
|
|
841
|
+
_add_console_processor(provider)
|
|
842
|
+
|
|
843
|
+
else:
|
|
844
|
+
_add_console_processor(provider)
|
|
845
|
+
|
|
846
|
+
return TracerProviderAdapter(provider, TracingBackend.OPENTELEMETRY)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def _add_console_processor(provider: Any) -> None:
|
|
850
|
+
"""Add console span processor to provider."""
|
|
851
|
+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor, ConsoleSpanExporter
|
|
852
|
+
|
|
853
|
+
provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def configure(
|
|
857
|
+
config: AdapterConfig | None = None,
|
|
858
|
+
*,
|
|
859
|
+
backend: TracingBackend | str | None = None,
|
|
860
|
+
service_name: str | None = None,
|
|
861
|
+
exporter_type: str | None = None,
|
|
862
|
+
exporter_endpoint: str | None = None,
|
|
863
|
+
sampling_ratio: float | None = None,
|
|
864
|
+
) -> TracerProviderAdapter:
|
|
865
|
+
"""Configure the global tracing adapter.
|
|
866
|
+
|
|
867
|
+
This is the main entry point for setting up tracing with automatic
|
|
868
|
+
backend selection.
|
|
869
|
+
|
|
870
|
+
Args:
|
|
871
|
+
config: Full configuration object.
|
|
872
|
+
backend: Backend to use (overrides config).
|
|
873
|
+
service_name: Service name (overrides config).
|
|
874
|
+
exporter_type: Exporter type (overrides config).
|
|
875
|
+
exporter_endpoint: Exporter endpoint (overrides config).
|
|
876
|
+
sampling_ratio: Sampling ratio (overrides config).
|
|
877
|
+
|
|
878
|
+
Returns:
|
|
879
|
+
Configured TracerProviderAdapter.
|
|
880
|
+
|
|
881
|
+
Example:
|
|
882
|
+
>>> # Auto-detect backend
|
|
883
|
+
>>> provider = configure(service_name="my-service")
|
|
884
|
+
>>> tracer = provider.get_tracer("my-component")
|
|
885
|
+
|
|
886
|
+
>>> # Force specific backend
|
|
887
|
+
>>> provider = configure(backend="truthound")
|
|
888
|
+
"""
|
|
889
|
+
global _global_adapter, _current_config
|
|
890
|
+
|
|
891
|
+
with _adapter_lock:
|
|
892
|
+
# Start with config or defaults
|
|
893
|
+
if config is None:
|
|
894
|
+
config = AdapterConfig()
|
|
895
|
+
|
|
896
|
+
# Apply overrides
|
|
897
|
+
if backend is not None:
|
|
898
|
+
if isinstance(backend, str):
|
|
899
|
+
backend = TracingBackend(backend)
|
|
900
|
+
config.backend = backend
|
|
901
|
+
if service_name:
|
|
902
|
+
config.service_name = service_name
|
|
903
|
+
if exporter_type:
|
|
904
|
+
config.exporter_type = exporter_type
|
|
905
|
+
if exporter_endpoint:
|
|
906
|
+
config.exporter_endpoint = exporter_endpoint
|
|
907
|
+
if sampling_ratio is not None:
|
|
908
|
+
config.sampling_ratio = sampling_ratio
|
|
909
|
+
|
|
910
|
+
# Determine actual backend
|
|
911
|
+
actual_backend = config.backend
|
|
912
|
+
if actual_backend == TracingBackend.AUTO:
|
|
913
|
+
if is_otel_sdk_available():
|
|
914
|
+
logger.info("OpenTelemetry SDK detected, using OTEL backend")
|
|
915
|
+
actual_backend = TracingBackend.OPENTELEMETRY
|
|
916
|
+
else:
|
|
917
|
+
logger.info("OpenTelemetry SDK not found, using Truthound backend")
|
|
918
|
+
actual_backend = TracingBackend.TRUTHOUND
|
|
919
|
+
|
|
920
|
+
# Create provider
|
|
921
|
+
if actual_backend == TracingBackend.OPENTELEMETRY:
|
|
922
|
+
_global_adapter = _create_otel_provider(config)
|
|
923
|
+
else:
|
|
924
|
+
_global_adapter = _create_truthound_provider(config)
|
|
925
|
+
|
|
926
|
+
_current_config = config
|
|
927
|
+
return _global_adapter
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
def get_tracer_provider() -> TracerProviderAdapter:
|
|
931
|
+
"""Get the global tracer provider adapter.
|
|
932
|
+
|
|
933
|
+
Automatically configures with defaults if not already configured.
|
|
934
|
+
|
|
935
|
+
Returns:
|
|
936
|
+
Global TracerProviderAdapter.
|
|
937
|
+
"""
|
|
938
|
+
global _global_adapter
|
|
939
|
+
|
|
940
|
+
with _adapter_lock:
|
|
941
|
+
if _global_adapter is None:
|
|
942
|
+
return configure()
|
|
943
|
+
return _global_adapter
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
def get_tracer(name: str, version: str = "") -> TracerAdapter:
|
|
947
|
+
"""Get a tracer from the global provider.
|
|
948
|
+
|
|
949
|
+
Convenience function for quick access.
|
|
950
|
+
|
|
951
|
+
Args:
|
|
952
|
+
name: Instrumentation name.
|
|
953
|
+
version: Instrumentation version.
|
|
954
|
+
|
|
955
|
+
Returns:
|
|
956
|
+
TracerAdapter.
|
|
957
|
+
"""
|
|
958
|
+
return get_tracer_provider().get_tracer(name, version)
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def get_current_backend() -> TracingBackend:
|
|
962
|
+
"""Get the currently active backend.
|
|
963
|
+
|
|
964
|
+
Returns:
|
|
965
|
+
Current TracingBackend.
|
|
966
|
+
"""
|
|
967
|
+
provider = get_tracer_provider()
|
|
968
|
+
return provider.backend
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
def reset_global_adapter() -> None:
|
|
972
|
+
"""Reset the global adapter (mainly for testing)."""
|
|
973
|
+
global _global_adapter, _current_config
|
|
974
|
+
|
|
975
|
+
with _adapter_lock:
|
|
976
|
+
if _global_adapter is not None:
|
|
977
|
+
try:
|
|
978
|
+
_global_adapter.shutdown()
|
|
979
|
+
except Exception:
|
|
980
|
+
pass
|
|
981
|
+
_global_adapter = None
|
|
982
|
+
_current_config = None
|