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,842 @@
|
|
|
1
|
+
"""Protocols and Data Classes for Distributed Checkpoint Orchestration.
|
|
2
|
+
|
|
3
|
+
This module defines the core abstractions for distributed checkpoint execution,
|
|
4
|
+
including backend protocols, task states, and configuration options.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from abc import abstractmethod
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime, timedelta
|
|
13
|
+
from enum import Enum, Flag, auto
|
|
14
|
+
from typing import (
|
|
15
|
+
TYPE_CHECKING,
|
|
16
|
+
Any,
|
|
17
|
+
AsyncIterator,
|
|
18
|
+
Callable,
|
|
19
|
+
Generator,
|
|
20
|
+
Generic,
|
|
21
|
+
Iterator,
|
|
22
|
+
Protocol,
|
|
23
|
+
TypeVar,
|
|
24
|
+
runtime_checkable,
|
|
25
|
+
)
|
|
26
|
+
from uuid import uuid4
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from truthound.checkpoint.checkpoint import Checkpoint, CheckpointResult
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# =============================================================================
|
|
36
|
+
# Exceptions
|
|
37
|
+
# =============================================================================
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class DistributedError(Exception):
|
|
41
|
+
"""Base exception for distributed checkpoint errors."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str, details: dict[str, Any] | None = None) -> None:
|
|
44
|
+
super().__init__(message)
|
|
45
|
+
self.message = message
|
|
46
|
+
self.details = details or {}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TaskSubmissionError(DistributedError):
|
|
50
|
+
"""Raised when task submission fails."""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
message: str,
|
|
55
|
+
checkpoint_name: str | None = None,
|
|
56
|
+
reason: str | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
super().__init__(message, {"checkpoint_name": checkpoint_name, "reason": reason})
|
|
59
|
+
self.checkpoint_name = checkpoint_name
|
|
60
|
+
self.reason = reason
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TaskTimeoutError(DistributedError):
|
|
64
|
+
"""Raised when task execution times out."""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
message: str,
|
|
69
|
+
task_id: str,
|
|
70
|
+
timeout_seconds: float,
|
|
71
|
+
) -> None:
|
|
72
|
+
super().__init__(message, {"task_id": task_id, "timeout_seconds": timeout_seconds})
|
|
73
|
+
self.task_id = task_id
|
|
74
|
+
self.timeout_seconds = timeout_seconds
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TaskCancelledError(DistributedError):
|
|
78
|
+
"""Raised when task is cancelled."""
|
|
79
|
+
|
|
80
|
+
def __init__(self, message: str, task_id: str, reason: str | None = None) -> None:
|
|
81
|
+
super().__init__(message, {"task_id": task_id, "reason": reason})
|
|
82
|
+
self.task_id = task_id
|
|
83
|
+
self.reason = reason
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class WorkerNotAvailableError(DistributedError):
|
|
87
|
+
"""Raised when no workers are available."""
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
message: str = "No workers available",
|
|
92
|
+
required_workers: int = 1,
|
|
93
|
+
available_workers: int = 0,
|
|
94
|
+
) -> None:
|
|
95
|
+
super().__init__(
|
|
96
|
+
message,
|
|
97
|
+
{"required_workers": required_workers, "available_workers": available_workers},
|
|
98
|
+
)
|
|
99
|
+
self.required_workers = required_workers
|
|
100
|
+
self.available_workers = available_workers
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class BackendNotAvailableError(DistributedError):
|
|
104
|
+
"""Raised when backend is not available."""
|
|
105
|
+
|
|
106
|
+
def __init__(
|
|
107
|
+
self,
|
|
108
|
+
backend_name: str,
|
|
109
|
+
reason: str | None = None,
|
|
110
|
+
install_hint: str | None = None,
|
|
111
|
+
) -> None:
|
|
112
|
+
message = f"Backend '{backend_name}' is not available"
|
|
113
|
+
if reason:
|
|
114
|
+
message += f": {reason}"
|
|
115
|
+
super().__init__(message, {"backend_name": backend_name, "install_hint": install_hint})
|
|
116
|
+
self.backend_name = backend_name
|
|
117
|
+
self.reason = reason
|
|
118
|
+
self.install_hint = install_hint
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# =============================================================================
|
|
122
|
+
# Enums
|
|
123
|
+
# =============================================================================
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TaskState(str, Enum):
|
|
127
|
+
"""State of a distributed task."""
|
|
128
|
+
|
|
129
|
+
PENDING = "pending" # Task created but not yet submitted
|
|
130
|
+
SUBMITTED = "submitted" # Task submitted to backend
|
|
131
|
+
QUEUED = "queued" # Task in backend queue
|
|
132
|
+
RUNNING = "running" # Task currently executing
|
|
133
|
+
SUCCEEDED = "succeeded" # Task completed successfully
|
|
134
|
+
FAILED = "failed" # Task failed with error
|
|
135
|
+
CANCELLED = "cancelled" # Task was cancelled
|
|
136
|
+
REVOKED = "revoked" # Task was revoked (Celery term)
|
|
137
|
+
RETRYING = "retrying" # Task is being retried
|
|
138
|
+
TIMEOUT = "timeout" # Task timed out
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def is_terminal(self) -> bool:
|
|
142
|
+
"""Check if state is terminal (no further transitions)."""
|
|
143
|
+
return self in {
|
|
144
|
+
TaskState.SUCCEEDED,
|
|
145
|
+
TaskState.FAILED,
|
|
146
|
+
TaskState.CANCELLED,
|
|
147
|
+
TaskState.REVOKED,
|
|
148
|
+
TaskState.TIMEOUT,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def is_active(self) -> bool:
|
|
153
|
+
"""Check if task is actively processing."""
|
|
154
|
+
return self in {TaskState.RUNNING, TaskState.RETRYING}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class TaskPriority(int, Enum):
|
|
158
|
+
"""Priority levels for task scheduling."""
|
|
159
|
+
|
|
160
|
+
LOWEST = 0
|
|
161
|
+
LOW = 3
|
|
162
|
+
NORMAL = 5
|
|
163
|
+
HIGH = 7
|
|
164
|
+
HIGHEST = 9
|
|
165
|
+
CRITICAL = 10
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def from_int(cls, value: int) -> "TaskPriority":
|
|
169
|
+
"""Convert integer to priority."""
|
|
170
|
+
if value <= 0:
|
|
171
|
+
return cls.LOWEST
|
|
172
|
+
elif value <= 3:
|
|
173
|
+
return cls.LOW
|
|
174
|
+
elif value <= 5:
|
|
175
|
+
return cls.NORMAL
|
|
176
|
+
elif value <= 7:
|
|
177
|
+
return cls.HIGH
|
|
178
|
+
elif value <= 9:
|
|
179
|
+
return cls.HIGHEST
|
|
180
|
+
return cls.CRITICAL
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class WorkerState(str, Enum):
|
|
184
|
+
"""State of a worker node."""
|
|
185
|
+
|
|
186
|
+
ONLINE = "online" # Worker is online and accepting tasks
|
|
187
|
+
OFFLINE = "offline" # Worker is offline
|
|
188
|
+
BUSY = "busy" # Worker is at capacity
|
|
189
|
+
DRAINING = "draining" # Worker is draining (no new tasks)
|
|
190
|
+
MAINTENANCE = "maintenance" # Worker is in maintenance mode
|
|
191
|
+
UNKNOWN = "unknown" # Worker state unknown
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class BackendCapability(Flag):
|
|
195
|
+
"""Capabilities supported by a backend."""
|
|
196
|
+
|
|
197
|
+
NONE = 0
|
|
198
|
+
ASYNC_SUBMIT = auto() # Async task submission
|
|
199
|
+
BATCH_SUBMIT = auto() # Batch task submission
|
|
200
|
+
PRIORITY_QUEUE = auto() # Priority-based scheduling
|
|
201
|
+
RETRY_POLICY = auto() # Automatic retry on failure
|
|
202
|
+
RATE_LIMITING = auto() # Task rate limiting
|
|
203
|
+
TASK_ROUTING = auto() # Route tasks to specific workers
|
|
204
|
+
RESULT_BACKEND = auto() # Store task results
|
|
205
|
+
TASK_REVOKE = auto() # Revoke/cancel running tasks
|
|
206
|
+
PROGRESS_TRACKING = auto() # Track task progress
|
|
207
|
+
CHAIN_TASKS = auto() # Chain tasks together
|
|
208
|
+
GROUP_TASKS = auto() # Execute tasks in groups
|
|
209
|
+
BROADCAST = auto() # Broadcast to all workers
|
|
210
|
+
SCHEDULED_TASKS = auto() # Schedule tasks for later execution
|
|
211
|
+
WORKER_SCALING = auto() # Dynamic worker scaling
|
|
212
|
+
HEALTH_CHECK = auto() # Worker health checks
|
|
213
|
+
METRICS = auto() # Collect execution metrics
|
|
214
|
+
|
|
215
|
+
# Common capability sets
|
|
216
|
+
BASIC = ASYNC_SUBMIT | RESULT_BACKEND
|
|
217
|
+
STANDARD = BASIC | BATCH_SUBMIT | RETRY_POLICY | TASK_REVOKE
|
|
218
|
+
ADVANCED = STANDARD | PRIORITY_QUEUE | PROGRESS_TRACKING | CHAIN_TASKS | GROUP_TASKS
|
|
219
|
+
FULL = ADVANCED | RATE_LIMITING | TASK_ROUTING | BROADCAST | SCHEDULED_TASKS | WORKER_SCALING | HEALTH_CHECK | METRICS
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# =============================================================================
|
|
223
|
+
# Data Classes
|
|
224
|
+
# =============================================================================
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@dataclass
|
|
228
|
+
class DistributedConfig:
|
|
229
|
+
"""Configuration for distributed checkpoint orchestration.
|
|
230
|
+
|
|
231
|
+
Attributes:
|
|
232
|
+
backend: Backend type ("celery", "ray", "kubernetes", "local").
|
|
233
|
+
max_workers: Maximum number of concurrent workers.
|
|
234
|
+
max_retries: Maximum retry attempts on failure.
|
|
235
|
+
retry_delay_seconds: Delay between retries.
|
|
236
|
+
task_timeout_seconds: Default task timeout.
|
|
237
|
+
result_ttl_seconds: How long to keep results.
|
|
238
|
+
heartbeat_interval_seconds: Worker heartbeat interval.
|
|
239
|
+
enable_metrics: Enable metrics collection.
|
|
240
|
+
enable_tracing: Enable distributed tracing.
|
|
241
|
+
backend_options: Backend-specific configuration.
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
backend: str = "local"
|
|
245
|
+
max_workers: int = 4
|
|
246
|
+
max_retries: int = 3
|
|
247
|
+
retry_delay_seconds: float = 5.0
|
|
248
|
+
retry_backoff_multiplier: float = 2.0
|
|
249
|
+
retry_max_delay_seconds: float = 300.0
|
|
250
|
+
task_timeout_seconds: float = 3600.0
|
|
251
|
+
result_ttl_seconds: float = 86400.0 # 24 hours
|
|
252
|
+
heartbeat_interval_seconds: float = 30.0
|
|
253
|
+
enable_metrics: bool = True
|
|
254
|
+
enable_tracing: bool = False
|
|
255
|
+
backend_options: dict[str, Any] = field(default_factory=dict)
|
|
256
|
+
|
|
257
|
+
def with_backend(self, backend: str, **options: Any) -> "DistributedConfig":
|
|
258
|
+
"""Create new config with different backend."""
|
|
259
|
+
new_options = {**self.backend_options, **options}
|
|
260
|
+
return DistributedConfig(
|
|
261
|
+
backend=backend,
|
|
262
|
+
max_workers=self.max_workers,
|
|
263
|
+
max_retries=self.max_retries,
|
|
264
|
+
retry_delay_seconds=self.retry_delay_seconds,
|
|
265
|
+
retry_backoff_multiplier=self.retry_backoff_multiplier,
|
|
266
|
+
retry_max_delay_seconds=self.retry_max_delay_seconds,
|
|
267
|
+
task_timeout_seconds=self.task_timeout_seconds,
|
|
268
|
+
result_ttl_seconds=self.result_ttl_seconds,
|
|
269
|
+
heartbeat_interval_seconds=self.heartbeat_interval_seconds,
|
|
270
|
+
enable_metrics=self.enable_metrics,
|
|
271
|
+
enable_tracing=self.enable_tracing,
|
|
272
|
+
backend_options=new_options,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@dataclass
|
|
277
|
+
class WorkerInfo:
|
|
278
|
+
"""Information about a worker node.
|
|
279
|
+
|
|
280
|
+
Attributes:
|
|
281
|
+
worker_id: Unique worker identifier.
|
|
282
|
+
hostname: Worker hostname.
|
|
283
|
+
state: Current worker state.
|
|
284
|
+
current_tasks: Number of tasks currently running.
|
|
285
|
+
max_concurrency: Maximum concurrent tasks.
|
|
286
|
+
registered_at: When worker registered.
|
|
287
|
+
last_heartbeat: Last heartbeat timestamp.
|
|
288
|
+
tags: Worker tags for routing.
|
|
289
|
+
metadata: Additional worker metadata.
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
worker_id: str
|
|
293
|
+
hostname: str
|
|
294
|
+
state: WorkerState = WorkerState.UNKNOWN
|
|
295
|
+
current_tasks: int = 0
|
|
296
|
+
max_concurrency: int = 1
|
|
297
|
+
registered_at: datetime = field(default_factory=datetime.now)
|
|
298
|
+
last_heartbeat: datetime = field(default_factory=datetime.now)
|
|
299
|
+
tags: set[str] = field(default_factory=set)
|
|
300
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def is_available(self) -> bool:
|
|
304
|
+
"""Check if worker can accept new tasks."""
|
|
305
|
+
return (
|
|
306
|
+
self.state == WorkerState.ONLINE
|
|
307
|
+
and self.current_tasks < self.max_concurrency
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def available_slots(self) -> int:
|
|
312
|
+
"""Get number of available task slots."""
|
|
313
|
+
if self.state != WorkerState.ONLINE:
|
|
314
|
+
return 0
|
|
315
|
+
return max(0, self.max_concurrency - self.current_tasks)
|
|
316
|
+
|
|
317
|
+
@property
|
|
318
|
+
def load_factor(self) -> float:
|
|
319
|
+
"""Get worker load factor (0.0 to 1.0)."""
|
|
320
|
+
if self.max_concurrency == 0:
|
|
321
|
+
return 1.0
|
|
322
|
+
return self.current_tasks / self.max_concurrency
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@dataclass
|
|
326
|
+
class ClusterState:
|
|
327
|
+
"""State of the distributed cluster.
|
|
328
|
+
|
|
329
|
+
Attributes:
|
|
330
|
+
workers: List of worker info.
|
|
331
|
+
total_capacity: Total task capacity.
|
|
332
|
+
current_load: Current number of running tasks.
|
|
333
|
+
pending_tasks: Number of pending tasks.
|
|
334
|
+
backend_name: Name of the backend.
|
|
335
|
+
backend_version: Version of the backend.
|
|
336
|
+
is_healthy: Whether cluster is healthy.
|
|
337
|
+
last_updated: When state was last updated.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
workers: list[WorkerInfo] = field(default_factory=list)
|
|
341
|
+
total_capacity: int = 0
|
|
342
|
+
current_load: int = 0
|
|
343
|
+
pending_tasks: int = 0
|
|
344
|
+
backend_name: str = ""
|
|
345
|
+
backend_version: str = ""
|
|
346
|
+
is_healthy: bool = True
|
|
347
|
+
last_updated: datetime = field(default_factory=datetime.now)
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def available_capacity(self) -> int:
|
|
351
|
+
"""Get available capacity."""
|
|
352
|
+
return self.total_capacity - self.current_load
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def online_workers(self) -> list[WorkerInfo]:
|
|
356
|
+
"""Get list of online workers."""
|
|
357
|
+
return [w for w in self.workers if w.state == WorkerState.ONLINE]
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def utilization(self) -> float:
|
|
361
|
+
"""Get cluster utilization (0.0 to 1.0)."""
|
|
362
|
+
if self.total_capacity == 0:
|
|
363
|
+
return 0.0
|
|
364
|
+
return self.current_load / self.total_capacity
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
@dataclass
|
|
368
|
+
class DistributedTaskResult:
|
|
369
|
+
"""Result of a distributed task execution.
|
|
370
|
+
|
|
371
|
+
Attributes:
|
|
372
|
+
task_id: Unique task identifier.
|
|
373
|
+
checkpoint_name: Name of the checkpoint that was run.
|
|
374
|
+
state: Final task state.
|
|
375
|
+
result: The CheckpointResult if successful.
|
|
376
|
+
error: Error message if failed.
|
|
377
|
+
exception: Exception if failed.
|
|
378
|
+
worker_id: ID of worker that executed the task.
|
|
379
|
+
submitted_at: When task was submitted.
|
|
380
|
+
started_at: When task started executing.
|
|
381
|
+
completed_at: When task completed.
|
|
382
|
+
retries: Number of retry attempts.
|
|
383
|
+
metadata: Additional result metadata.
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
task_id: str
|
|
387
|
+
checkpoint_name: str
|
|
388
|
+
state: TaskState
|
|
389
|
+
result: "CheckpointResult | None" = None
|
|
390
|
+
error: str | None = None
|
|
391
|
+
exception: Exception | None = None
|
|
392
|
+
worker_id: str | None = None
|
|
393
|
+
submitted_at: datetime | None = None
|
|
394
|
+
started_at: datetime | None = None
|
|
395
|
+
completed_at: datetime | None = None
|
|
396
|
+
retries: int = 0
|
|
397
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
398
|
+
|
|
399
|
+
@property
|
|
400
|
+
def success(self) -> bool:
|
|
401
|
+
"""Check if task succeeded."""
|
|
402
|
+
return self.state == TaskState.SUCCEEDED
|
|
403
|
+
|
|
404
|
+
@property
|
|
405
|
+
def duration_ms(self) -> float | None:
|
|
406
|
+
"""Get execution duration in milliseconds."""
|
|
407
|
+
if self.started_at and self.completed_at:
|
|
408
|
+
return (self.completed_at - self.started_at).total_seconds() * 1000
|
|
409
|
+
return None
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def queue_time_ms(self) -> float | None:
|
|
413
|
+
"""Get time spent in queue in milliseconds."""
|
|
414
|
+
if self.submitted_at and self.started_at:
|
|
415
|
+
return (self.started_at - self.submitted_at).total_seconds() * 1000
|
|
416
|
+
return None
|
|
417
|
+
|
|
418
|
+
def to_dict(self) -> dict[str, Any]:
|
|
419
|
+
"""Convert to dictionary."""
|
|
420
|
+
return {
|
|
421
|
+
"task_id": self.task_id,
|
|
422
|
+
"checkpoint_name": self.checkpoint_name,
|
|
423
|
+
"state": self.state.value,
|
|
424
|
+
"result": self.result.to_dict() if self.result else None,
|
|
425
|
+
"error": self.error,
|
|
426
|
+
"worker_id": self.worker_id,
|
|
427
|
+
"submitted_at": self.submitted_at.isoformat() if self.submitted_at else None,
|
|
428
|
+
"started_at": self.started_at.isoformat() if self.started_at else None,
|
|
429
|
+
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
|
|
430
|
+
"retries": self.retries,
|
|
431
|
+
"duration_ms": self.duration_ms,
|
|
432
|
+
"queue_time_ms": self.queue_time_ms,
|
|
433
|
+
"metadata": self.metadata,
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
# =============================================================================
|
|
438
|
+
# Protocols
|
|
439
|
+
# =============================================================================
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
T = TypeVar("T")
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@runtime_checkable
|
|
446
|
+
class DistributedTaskProtocol(Protocol[T]):
|
|
447
|
+
"""Protocol for a distributed task handle.
|
|
448
|
+
|
|
449
|
+
This represents a submitted task that can be awaited, cancelled, or queried.
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
@property
|
|
453
|
+
def task_id(self) -> str:
|
|
454
|
+
"""Get unique task identifier."""
|
|
455
|
+
...
|
|
456
|
+
|
|
457
|
+
@property
|
|
458
|
+
def state(self) -> TaskState:
|
|
459
|
+
"""Get current task state."""
|
|
460
|
+
...
|
|
461
|
+
|
|
462
|
+
@property
|
|
463
|
+
def checkpoint_name(self) -> str:
|
|
464
|
+
"""Get name of the checkpoint being executed."""
|
|
465
|
+
...
|
|
466
|
+
|
|
467
|
+
def result(self, timeout: float | None = None) -> T:
|
|
468
|
+
"""Wait for and return the task result.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
timeout: Maximum time to wait in seconds.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
Task result.
|
|
475
|
+
|
|
476
|
+
Raises:
|
|
477
|
+
TaskTimeoutError: If timeout exceeded.
|
|
478
|
+
TaskCancelledError: If task was cancelled.
|
|
479
|
+
DistributedError: If task failed.
|
|
480
|
+
"""
|
|
481
|
+
...
|
|
482
|
+
|
|
483
|
+
async def result_async(self, timeout: float | None = None) -> T:
|
|
484
|
+
"""Async version of result().
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
timeout: Maximum time to wait in seconds.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Task result.
|
|
491
|
+
"""
|
|
492
|
+
...
|
|
493
|
+
|
|
494
|
+
def cancel(self, terminate: bool = False) -> bool:
|
|
495
|
+
"""Cancel the task.
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
terminate: If True, terminate even if running.
|
|
499
|
+
|
|
500
|
+
Returns:
|
|
501
|
+
True if cancelled, False if already completed.
|
|
502
|
+
"""
|
|
503
|
+
...
|
|
504
|
+
|
|
505
|
+
def is_ready(self) -> bool:
|
|
506
|
+
"""Check if task has completed."""
|
|
507
|
+
...
|
|
508
|
+
|
|
509
|
+
def is_successful(self) -> bool:
|
|
510
|
+
"""Check if task completed successfully."""
|
|
511
|
+
...
|
|
512
|
+
|
|
513
|
+
def wait(self, timeout: float | None = None) -> bool:
|
|
514
|
+
"""Wait for task to complete.
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
timeout: Maximum time to wait.
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
True if completed, False if timeout.
|
|
521
|
+
"""
|
|
522
|
+
...
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@runtime_checkable
|
|
526
|
+
class DistributedBackendProtocol(Protocol):
|
|
527
|
+
"""Protocol for distributed execution backends.
|
|
528
|
+
|
|
529
|
+
Implementations must provide methods for task submission, worker
|
|
530
|
+
management, and cluster state queries.
|
|
531
|
+
"""
|
|
532
|
+
|
|
533
|
+
@property
|
|
534
|
+
def name(self) -> str:
|
|
535
|
+
"""Get backend name."""
|
|
536
|
+
...
|
|
537
|
+
|
|
538
|
+
@property
|
|
539
|
+
def capabilities(self) -> BackendCapability:
|
|
540
|
+
"""Get backend capabilities."""
|
|
541
|
+
...
|
|
542
|
+
|
|
543
|
+
@property
|
|
544
|
+
def is_connected(self) -> bool:
|
|
545
|
+
"""Check if backend is connected."""
|
|
546
|
+
...
|
|
547
|
+
|
|
548
|
+
def connect(self, **kwargs: Any) -> None:
|
|
549
|
+
"""Connect to the backend.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
**kwargs: Backend-specific connection options.
|
|
553
|
+
|
|
554
|
+
Raises:
|
|
555
|
+
BackendNotAvailableError: If connection fails.
|
|
556
|
+
"""
|
|
557
|
+
...
|
|
558
|
+
|
|
559
|
+
def disconnect(self) -> None:
|
|
560
|
+
"""Disconnect from the backend."""
|
|
561
|
+
...
|
|
562
|
+
|
|
563
|
+
def submit(
|
|
564
|
+
self,
|
|
565
|
+
checkpoint: "Checkpoint",
|
|
566
|
+
priority: TaskPriority = TaskPriority.NORMAL,
|
|
567
|
+
timeout: float | None = None,
|
|
568
|
+
context: dict[str, Any] | None = None,
|
|
569
|
+
**kwargs: Any,
|
|
570
|
+
) -> DistributedTaskProtocol["CheckpointResult"]:
|
|
571
|
+
"""Submit a checkpoint for execution.
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
checkpoint: Checkpoint to execute.
|
|
575
|
+
priority: Task priority.
|
|
576
|
+
timeout: Task timeout in seconds.
|
|
577
|
+
context: Additional context for the run.
|
|
578
|
+
**kwargs: Backend-specific options.
|
|
579
|
+
|
|
580
|
+
Returns:
|
|
581
|
+
Task handle for tracking execution.
|
|
582
|
+
|
|
583
|
+
Raises:
|
|
584
|
+
TaskSubmissionError: If submission fails.
|
|
585
|
+
"""
|
|
586
|
+
...
|
|
587
|
+
|
|
588
|
+
def submit_batch(
|
|
589
|
+
self,
|
|
590
|
+
checkpoints: list["Checkpoint"],
|
|
591
|
+
priority: TaskPriority = TaskPriority.NORMAL,
|
|
592
|
+
timeout: float | None = None,
|
|
593
|
+
context: dict[str, Any] | None = None,
|
|
594
|
+
**kwargs: Any,
|
|
595
|
+
) -> list[DistributedTaskProtocol["CheckpointResult"]]:
|
|
596
|
+
"""Submit multiple checkpoints for execution.
|
|
597
|
+
|
|
598
|
+
Args:
|
|
599
|
+
checkpoints: List of checkpoints to execute.
|
|
600
|
+
priority: Task priority.
|
|
601
|
+
timeout: Task timeout in seconds.
|
|
602
|
+
context: Additional context for all runs.
|
|
603
|
+
**kwargs: Backend-specific options.
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
List of task handles.
|
|
607
|
+
|
|
608
|
+
Raises:
|
|
609
|
+
TaskSubmissionError: If submission fails.
|
|
610
|
+
"""
|
|
611
|
+
...
|
|
612
|
+
|
|
613
|
+
def get_task(self, task_id: str) -> DistributedTaskProtocol["CheckpointResult"] | None:
|
|
614
|
+
"""Get a task by ID.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
task_id: Task identifier.
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
Task handle if found, None otherwise.
|
|
621
|
+
"""
|
|
622
|
+
...
|
|
623
|
+
|
|
624
|
+
def get_cluster_state(self) -> ClusterState:
|
|
625
|
+
"""Get current cluster state.
|
|
626
|
+
|
|
627
|
+
Returns:
|
|
628
|
+
ClusterState with worker and capacity info.
|
|
629
|
+
"""
|
|
630
|
+
...
|
|
631
|
+
|
|
632
|
+
def get_workers(self) -> list[WorkerInfo]:
|
|
633
|
+
"""Get list of workers.
|
|
634
|
+
|
|
635
|
+
Returns:
|
|
636
|
+
List of worker info.
|
|
637
|
+
"""
|
|
638
|
+
...
|
|
639
|
+
|
|
640
|
+
def scale_workers(self, count: int) -> bool:
|
|
641
|
+
"""Scale the number of workers.
|
|
642
|
+
|
|
643
|
+
Args:
|
|
644
|
+
count: Desired worker count.
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
True if scaling initiated, False if not supported.
|
|
648
|
+
"""
|
|
649
|
+
...
|
|
650
|
+
|
|
651
|
+
def health_check(self) -> bool:
|
|
652
|
+
"""Perform health check.
|
|
653
|
+
|
|
654
|
+
Returns:
|
|
655
|
+
True if healthy, False otherwise.
|
|
656
|
+
"""
|
|
657
|
+
...
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
@dataclass
|
|
661
|
+
class DistributedTask(Generic[T]):
|
|
662
|
+
"""Concrete implementation of a distributed task handle.
|
|
663
|
+
|
|
664
|
+
This provides a standard implementation that backends can use or extend.
|
|
665
|
+
"""
|
|
666
|
+
|
|
667
|
+
task_id: str
|
|
668
|
+
checkpoint_name: str
|
|
669
|
+
_state: TaskState = TaskState.PENDING
|
|
670
|
+
_result: T | None = None
|
|
671
|
+
_error: str | None = None
|
|
672
|
+
_exception: Exception | None = None
|
|
673
|
+
_backend: Any = None # Reference to backend for operations
|
|
674
|
+
_submitted_at: datetime = field(default_factory=datetime.now)
|
|
675
|
+
_started_at: datetime | None = None
|
|
676
|
+
_completed_at: datetime | None = None
|
|
677
|
+
_worker_id: str | None = None
|
|
678
|
+
_retries: int = 0
|
|
679
|
+
_metadata: dict[str, Any] = field(default_factory=dict)
|
|
680
|
+
_callbacks: list[Callable[[T | None, Exception | None], None]] = field(
|
|
681
|
+
default_factory=list
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
@classmethod
|
|
685
|
+
def create(
|
|
686
|
+
cls,
|
|
687
|
+
checkpoint: "Checkpoint",
|
|
688
|
+
backend: Any = None,
|
|
689
|
+
) -> "DistributedTask[T]":
|
|
690
|
+
"""Create a new task for a checkpoint."""
|
|
691
|
+
return cls(
|
|
692
|
+
task_id=f"task-{uuid4().hex[:16]}",
|
|
693
|
+
checkpoint_name=checkpoint.name,
|
|
694
|
+
_state=TaskState.PENDING,
|
|
695
|
+
_backend=backend,
|
|
696
|
+
_submitted_at=datetime.now(),
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
@property
|
|
700
|
+
def state(self) -> TaskState:
|
|
701
|
+
"""Get current task state."""
|
|
702
|
+
return self._state
|
|
703
|
+
|
|
704
|
+
def _set_state(self, state: TaskState) -> None:
|
|
705
|
+
"""Set task state (internal use)."""
|
|
706
|
+
self._state = state
|
|
707
|
+
|
|
708
|
+
def _set_result(self, result: T) -> None:
|
|
709
|
+
"""Set task result (internal use)."""
|
|
710
|
+
self._result = result
|
|
711
|
+
self._state = TaskState.SUCCEEDED
|
|
712
|
+
self._completed_at = datetime.now()
|
|
713
|
+
self._invoke_callbacks()
|
|
714
|
+
|
|
715
|
+
def _set_error(self, error: str, exception: Exception | None = None) -> None:
|
|
716
|
+
"""Set task error (internal use)."""
|
|
717
|
+
self._error = error
|
|
718
|
+
self._exception = exception
|
|
719
|
+
self._state = TaskState.FAILED
|
|
720
|
+
self._completed_at = datetime.now()
|
|
721
|
+
self._invoke_callbacks()
|
|
722
|
+
|
|
723
|
+
def _invoke_callbacks(self) -> None:
|
|
724
|
+
"""Invoke registered callbacks."""
|
|
725
|
+
for callback in self._callbacks:
|
|
726
|
+
try:
|
|
727
|
+
callback(self._result, self._exception)
|
|
728
|
+
except Exception as e:
|
|
729
|
+
logger.error(f"Callback error: {e}")
|
|
730
|
+
|
|
731
|
+
def result(self, timeout: float | None = None) -> T:
|
|
732
|
+
"""Wait for and return the task result."""
|
|
733
|
+
import time
|
|
734
|
+
|
|
735
|
+
start = time.time()
|
|
736
|
+
while not self.is_ready():
|
|
737
|
+
if timeout and (time.time() - start) > timeout:
|
|
738
|
+
raise TaskTimeoutError(
|
|
739
|
+
f"Task {self.task_id} timed out",
|
|
740
|
+
self.task_id,
|
|
741
|
+
timeout,
|
|
742
|
+
)
|
|
743
|
+
time.sleep(0.1)
|
|
744
|
+
|
|
745
|
+
if self._state == TaskState.CANCELLED:
|
|
746
|
+
raise TaskCancelledError(f"Task {self.task_id} was cancelled", self.task_id)
|
|
747
|
+
|
|
748
|
+
if self._state == TaskState.FAILED:
|
|
749
|
+
raise DistributedError(
|
|
750
|
+
f"Task {self.task_id} failed: {self._error}",
|
|
751
|
+
{"exception": self._exception},
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
return self._result # type: ignore
|
|
755
|
+
|
|
756
|
+
async def result_async(self, timeout: float | None = None) -> T:
|
|
757
|
+
"""Async version of result()."""
|
|
758
|
+
import asyncio
|
|
759
|
+
|
|
760
|
+
start = asyncio.get_event_loop().time()
|
|
761
|
+
while not self.is_ready():
|
|
762
|
+
if timeout and (asyncio.get_event_loop().time() - start) > timeout:
|
|
763
|
+
raise TaskTimeoutError(
|
|
764
|
+
f"Task {self.task_id} timed out",
|
|
765
|
+
self.task_id,
|
|
766
|
+
timeout,
|
|
767
|
+
)
|
|
768
|
+
await asyncio.sleep(0.1)
|
|
769
|
+
|
|
770
|
+
if self._state == TaskState.CANCELLED:
|
|
771
|
+
raise TaskCancelledError(f"Task {self.task_id} was cancelled", self.task_id)
|
|
772
|
+
|
|
773
|
+
if self._state == TaskState.FAILED:
|
|
774
|
+
raise DistributedError(
|
|
775
|
+
f"Task {self.task_id} failed: {self._error}",
|
|
776
|
+
{"exception": self._exception},
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
return self._result # type: ignore
|
|
780
|
+
|
|
781
|
+
def cancel(self, terminate: bool = False) -> bool:
|
|
782
|
+
"""Cancel the task."""
|
|
783
|
+
if self.is_ready():
|
|
784
|
+
return False
|
|
785
|
+
|
|
786
|
+
self._state = TaskState.CANCELLED
|
|
787
|
+
self._completed_at = datetime.now()
|
|
788
|
+
|
|
789
|
+
# Notify backend if available
|
|
790
|
+
if self._backend and hasattr(self._backend, "cancel_task"):
|
|
791
|
+
try:
|
|
792
|
+
self._backend.cancel_task(self.task_id, terminate=terminate)
|
|
793
|
+
except Exception as e:
|
|
794
|
+
logger.warning(f"Backend cancel failed: {e}")
|
|
795
|
+
|
|
796
|
+
return True
|
|
797
|
+
|
|
798
|
+
def is_ready(self) -> bool:
|
|
799
|
+
"""Check if task has completed."""
|
|
800
|
+
return self._state.is_terminal
|
|
801
|
+
|
|
802
|
+
def is_successful(self) -> bool:
|
|
803
|
+
"""Check if task completed successfully."""
|
|
804
|
+
return self._state == TaskState.SUCCEEDED
|
|
805
|
+
|
|
806
|
+
def wait(self, timeout: float | None = None) -> bool:
|
|
807
|
+
"""Wait for task to complete."""
|
|
808
|
+
try:
|
|
809
|
+
self.result(timeout=timeout)
|
|
810
|
+
return True
|
|
811
|
+
except TaskTimeoutError:
|
|
812
|
+
return False
|
|
813
|
+
except Exception:
|
|
814
|
+
return True # Task completed (with error)
|
|
815
|
+
|
|
816
|
+
def add_callback(
|
|
817
|
+
self,
|
|
818
|
+
callback: Callable[[T | None, Exception | None], None],
|
|
819
|
+
) -> "DistributedTask[T]":
|
|
820
|
+
"""Add a completion callback."""
|
|
821
|
+
self._callbacks.append(callback)
|
|
822
|
+
# If already complete, invoke immediately
|
|
823
|
+
if self.is_ready():
|
|
824
|
+
callback(self._result, self._exception)
|
|
825
|
+
return self
|
|
826
|
+
|
|
827
|
+
def to_result(self) -> DistributedTaskResult:
|
|
828
|
+
"""Convert to DistributedTaskResult."""
|
|
829
|
+
return DistributedTaskResult(
|
|
830
|
+
task_id=self.task_id,
|
|
831
|
+
checkpoint_name=self.checkpoint_name,
|
|
832
|
+
state=self._state,
|
|
833
|
+
result=self._result, # type: ignore
|
|
834
|
+
error=self._error,
|
|
835
|
+
exception=self._exception,
|
|
836
|
+
worker_id=self._worker_id,
|
|
837
|
+
submitted_at=self._submitted_at,
|
|
838
|
+
started_at=self._started_at,
|
|
839
|
+
completed_at=self._completed_at,
|
|
840
|
+
retries=self._retries,
|
|
841
|
+
metadata=self._metadata,
|
|
842
|
+
)
|