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,855 @@
|
|
|
1
|
+
"""i18n Protocol Definitions and Base Abstractions.
|
|
2
|
+
|
|
3
|
+
This module defines the core protocols (interfaces) for the i18n system,
|
|
4
|
+
enabling extensibility and loose coupling between components.
|
|
5
|
+
|
|
6
|
+
Protocols:
|
|
7
|
+
- PluralRuleProvider: CLDR plural rule handling
|
|
8
|
+
- NumberFormatter: Locale-aware number formatting
|
|
9
|
+
- DateFormatter: Locale-aware date/time formatting
|
|
10
|
+
- TextDirectionProvider: RTL/LTR text direction
|
|
11
|
+
- MessageResolver: Message resolution with context
|
|
12
|
+
- CatalogLoader: Dynamic catalog loading
|
|
13
|
+
- TranslationService: External TMS integration
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from datetime import datetime, date, time
|
|
21
|
+
from decimal import Decimal
|
|
22
|
+
from enum import Enum
|
|
23
|
+
from typing import Any, Protocol, TypeVar, runtime_checkable
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ==============================================================================
|
|
27
|
+
# Enums and Type Definitions
|
|
28
|
+
# ==============================================================================
|
|
29
|
+
|
|
30
|
+
class PluralCategory(str, Enum):
|
|
31
|
+
"""CLDR plural categories.
|
|
32
|
+
|
|
33
|
+
Based on Unicode CLDR plural rules:
|
|
34
|
+
https://cldr.unicode.org/index/cldr-spec/plural-rules
|
|
35
|
+
"""
|
|
36
|
+
ZERO = "zero" # 0 items
|
|
37
|
+
ONE = "one" # 1 item (singular)
|
|
38
|
+
TWO = "two" # 2 items (dual)
|
|
39
|
+
FEW = "few" # Small number (e.g., 2-4 in Slavic languages)
|
|
40
|
+
MANY = "many" # Larger number (e.g., 5+ in Slavic languages)
|
|
41
|
+
OTHER = "other" # Default/fallback
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TextDirection(str, Enum):
|
|
45
|
+
"""Text direction for layout."""
|
|
46
|
+
LTR = "ltr" # Left-to-right (English, Korean, etc.)
|
|
47
|
+
RTL = "rtl" # Right-to-left (Arabic, Hebrew, etc.)
|
|
48
|
+
AUTO = "auto" # Automatic detection
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class NumberStyle(str, Enum):
|
|
52
|
+
"""Number formatting style."""
|
|
53
|
+
DECIMAL = "decimal"
|
|
54
|
+
CURRENCY = "currency"
|
|
55
|
+
PERCENT = "percent"
|
|
56
|
+
SCIENTIFIC = "scientific"
|
|
57
|
+
COMPACT = "compact"
|
|
58
|
+
ORDINAL = "ordinal"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class DateStyle(str, Enum):
|
|
62
|
+
"""Date formatting style."""
|
|
63
|
+
SHORT = "short" # 12/31/24
|
|
64
|
+
MEDIUM = "medium" # Dec 31, 2024
|
|
65
|
+
LONG = "long" # December 31, 2024
|
|
66
|
+
FULL = "full" # Tuesday, December 31, 2024
|
|
67
|
+
ISO = "iso" # 2024-12-31
|
|
68
|
+
RELATIVE = "relative" # 2 days ago
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TimeStyle(str, Enum):
|
|
72
|
+
"""Time formatting style."""
|
|
73
|
+
SHORT = "short" # 3:30 PM
|
|
74
|
+
MEDIUM = "medium" # 3:30:00 PM
|
|
75
|
+
LONG = "long" # 3:30:00 PM UTC
|
|
76
|
+
FULL = "full" # 3:30:00 PM Coordinated Universal Time
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class MessageContext(str, Enum):
|
|
80
|
+
"""Context for message selection."""
|
|
81
|
+
FORMAL = "formal"
|
|
82
|
+
INFORMAL = "informal"
|
|
83
|
+
TECHNICAL = "technical"
|
|
84
|
+
LEGAL = "legal"
|
|
85
|
+
MARKETING = "marketing"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# ==============================================================================
|
|
89
|
+
# Data Classes
|
|
90
|
+
# ==============================================================================
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True)
|
|
93
|
+
class LocaleInfo:
|
|
94
|
+
"""Complete locale information.
|
|
95
|
+
|
|
96
|
+
Attributes:
|
|
97
|
+
language: ISO 639-1 language code (e.g., "en", "ko")
|
|
98
|
+
region: ISO 3166-1 region code (e.g., "US", "GB")
|
|
99
|
+
script: ISO 15924 script code (e.g., "Latn", "Hans")
|
|
100
|
+
variant: Locale variant (e.g., "formal", "informal")
|
|
101
|
+
"""
|
|
102
|
+
language: str
|
|
103
|
+
region: str | None = None
|
|
104
|
+
script: str | None = None
|
|
105
|
+
variant: str | None = None
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def tag(self) -> str:
|
|
109
|
+
"""Get BCP 47 language tag."""
|
|
110
|
+
parts = [self.language]
|
|
111
|
+
if self.script:
|
|
112
|
+
parts.append(self.script)
|
|
113
|
+
if self.region:
|
|
114
|
+
parts.append(self.region)
|
|
115
|
+
if self.variant:
|
|
116
|
+
parts.append(self.variant)
|
|
117
|
+
return "-".join(parts)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def direction(self) -> TextDirection:
|
|
121
|
+
"""Get default text direction for this locale."""
|
|
122
|
+
rtl_languages = {"ar", "he", "fa", "ur", "yi", "ps", "sd"}
|
|
123
|
+
if self.language in rtl_languages:
|
|
124
|
+
return TextDirection.RTL
|
|
125
|
+
return TextDirection.LTR
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def parse(cls, tag: str) -> "LocaleInfo":
|
|
129
|
+
"""Parse a locale tag.
|
|
130
|
+
|
|
131
|
+
Supports formats:
|
|
132
|
+
- Simple: "en", "ko"
|
|
133
|
+
- With region: "en-US", "ko-KR", "en_US", "ko_KR"
|
|
134
|
+
- With script: "zh-Hans", "zh-Hant"
|
|
135
|
+
- Full: "zh-Hans-CN", "sr-Latn-RS"
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
tag: Locale tag string
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Parsed LocaleInfo
|
|
142
|
+
"""
|
|
143
|
+
# Normalize separator
|
|
144
|
+
parts = tag.replace("_", "-").split("-")
|
|
145
|
+
|
|
146
|
+
language = parts[0].lower()
|
|
147
|
+
region = None
|
|
148
|
+
script = None
|
|
149
|
+
variant = None
|
|
150
|
+
|
|
151
|
+
for part in parts[1:]:
|
|
152
|
+
if len(part) == 4 and part.isalpha():
|
|
153
|
+
# Script code (4 letters)
|
|
154
|
+
script = part.capitalize()
|
|
155
|
+
elif len(part) == 2 and part.isalpha():
|
|
156
|
+
# Region code (2 letters)
|
|
157
|
+
region = part.upper()
|
|
158
|
+
elif len(part) == 3 and part.isdigit():
|
|
159
|
+
# UN M.49 region code (3 digits)
|
|
160
|
+
region = part
|
|
161
|
+
else:
|
|
162
|
+
# Variant or extension
|
|
163
|
+
variant = part.lower()
|
|
164
|
+
|
|
165
|
+
return cls(
|
|
166
|
+
language=language,
|
|
167
|
+
region=region,
|
|
168
|
+
script=script,
|
|
169
|
+
variant=variant,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def matches(self, other: "LocaleInfo", strict: bool = False) -> bool:
|
|
173
|
+
"""Check if this locale matches another.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
other: Locale to compare with
|
|
177
|
+
strict: If True, require exact match
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
True if locales match
|
|
181
|
+
"""
|
|
182
|
+
if strict:
|
|
183
|
+
return (
|
|
184
|
+
self.language == other.language
|
|
185
|
+
and self.region == other.region
|
|
186
|
+
and self.script == other.script
|
|
187
|
+
and self.variant == other.variant
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Non-strict: language must match, others are optional
|
|
191
|
+
if self.language != other.language:
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
if other.region and self.region != other.region:
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
if other.script and self.script != other.script:
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
return True
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@dataclass
|
|
204
|
+
class FormattedNumber:
|
|
205
|
+
"""Result of number formatting.
|
|
206
|
+
|
|
207
|
+
Attributes:
|
|
208
|
+
value: Original numeric value
|
|
209
|
+
formatted: Formatted string representation
|
|
210
|
+
direction: Text direction
|
|
211
|
+
parts: Component parts (for advanced rendering)
|
|
212
|
+
"""
|
|
213
|
+
value: float | int | Decimal
|
|
214
|
+
formatted: str
|
|
215
|
+
direction: TextDirection = TextDirection.LTR
|
|
216
|
+
parts: dict[str, str] = field(default_factory=dict)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@dataclass
|
|
220
|
+
class FormattedDate:
|
|
221
|
+
"""Result of date/time formatting.
|
|
222
|
+
|
|
223
|
+
Attributes:
|
|
224
|
+
value: Original datetime value
|
|
225
|
+
formatted: Formatted string representation
|
|
226
|
+
direction: Text direction
|
|
227
|
+
calendar: Calendar system used
|
|
228
|
+
"""
|
|
229
|
+
value: datetime | date | time
|
|
230
|
+
formatted: str
|
|
231
|
+
direction: TextDirection = TextDirection.LTR
|
|
232
|
+
calendar: str = "gregorian"
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@dataclass
|
|
236
|
+
class PluralizedMessage:
|
|
237
|
+
"""Result of message pluralization.
|
|
238
|
+
|
|
239
|
+
Attributes:
|
|
240
|
+
message: Formatted message
|
|
241
|
+
count: Original count value
|
|
242
|
+
category: Plural category used
|
|
243
|
+
"""
|
|
244
|
+
message: str
|
|
245
|
+
count: float | int
|
|
246
|
+
category: PluralCategory
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@dataclass
|
|
250
|
+
class ResolvedMessage:
|
|
251
|
+
"""Result of message resolution.
|
|
252
|
+
|
|
253
|
+
Attributes:
|
|
254
|
+
key: Original message key
|
|
255
|
+
message: Resolved and formatted message
|
|
256
|
+
locale: Locale used
|
|
257
|
+
context: Context used
|
|
258
|
+
fallback: Whether fallback was used
|
|
259
|
+
"""
|
|
260
|
+
key: str
|
|
261
|
+
message: str
|
|
262
|
+
locale: LocaleInfo
|
|
263
|
+
context: MessageContext | None = None
|
|
264
|
+
fallback: bool = False
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# ==============================================================================
|
|
268
|
+
# Protocols (Interfaces)
|
|
269
|
+
# ==============================================================================
|
|
270
|
+
|
|
271
|
+
@runtime_checkable
|
|
272
|
+
class PluralRuleProvider(Protocol):
|
|
273
|
+
"""Protocol for plural rule handling.
|
|
274
|
+
|
|
275
|
+
Implementations should follow CLDR plural rules:
|
|
276
|
+
https://cldr.unicode.org/index/cldr-spec/plural-rules
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
def get_category(
|
|
280
|
+
self,
|
|
281
|
+
count: float | int,
|
|
282
|
+
locale: LocaleInfo,
|
|
283
|
+
ordinal: bool = False,
|
|
284
|
+
) -> PluralCategory:
|
|
285
|
+
"""Get the plural category for a number.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
count: The number to categorize
|
|
289
|
+
locale: Target locale
|
|
290
|
+
ordinal: If True, use ordinal rules (1st, 2nd, 3rd...)
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Appropriate plural category
|
|
294
|
+
"""
|
|
295
|
+
...
|
|
296
|
+
|
|
297
|
+
def get_plural_form(
|
|
298
|
+
self,
|
|
299
|
+
count: float | int,
|
|
300
|
+
forms: dict[PluralCategory, str],
|
|
301
|
+
locale: LocaleInfo,
|
|
302
|
+
) -> str:
|
|
303
|
+
"""Select appropriate plural form.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
count: The number to pluralize
|
|
307
|
+
forms: Dictionary of category -> message template
|
|
308
|
+
locale: Target locale
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Selected message template
|
|
312
|
+
"""
|
|
313
|
+
...
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@runtime_checkable
|
|
317
|
+
class NumberFormatter(Protocol):
|
|
318
|
+
"""Protocol for locale-aware number formatting."""
|
|
319
|
+
|
|
320
|
+
def format(
|
|
321
|
+
self,
|
|
322
|
+
value: float | int | Decimal,
|
|
323
|
+
locale: LocaleInfo,
|
|
324
|
+
style: NumberStyle = NumberStyle.DECIMAL,
|
|
325
|
+
**options: Any,
|
|
326
|
+
) -> FormattedNumber:
|
|
327
|
+
"""Format a number according to locale rules.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
value: Number to format
|
|
331
|
+
locale: Target locale
|
|
332
|
+
style: Formatting style
|
|
333
|
+
**options: Additional options (currency, precision, etc.)
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Formatted number result
|
|
337
|
+
"""
|
|
338
|
+
...
|
|
339
|
+
|
|
340
|
+
def parse(
|
|
341
|
+
self,
|
|
342
|
+
text: str,
|
|
343
|
+
locale: LocaleInfo,
|
|
344
|
+
style: NumberStyle = NumberStyle.DECIMAL,
|
|
345
|
+
) -> float | int | Decimal | None:
|
|
346
|
+
"""Parse a localized number string.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
text: Localized number string
|
|
350
|
+
locale: Source locale
|
|
351
|
+
style: Expected format style
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Parsed number or None if invalid
|
|
355
|
+
"""
|
|
356
|
+
...
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@runtime_checkable
|
|
360
|
+
class DateFormatter(Protocol):
|
|
361
|
+
"""Protocol for locale-aware date/time formatting."""
|
|
362
|
+
|
|
363
|
+
def format_date(
|
|
364
|
+
self,
|
|
365
|
+
value: datetime | date,
|
|
366
|
+
locale: LocaleInfo,
|
|
367
|
+
style: DateStyle = DateStyle.MEDIUM,
|
|
368
|
+
**options: Any,
|
|
369
|
+
) -> FormattedDate:
|
|
370
|
+
"""Format a date according to locale rules.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
value: Date to format
|
|
374
|
+
locale: Target locale
|
|
375
|
+
style: Formatting style
|
|
376
|
+
**options: Additional options (timezone, calendar, etc.)
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
Formatted date result
|
|
380
|
+
"""
|
|
381
|
+
...
|
|
382
|
+
|
|
383
|
+
def format_time(
|
|
384
|
+
self,
|
|
385
|
+
value: datetime | time,
|
|
386
|
+
locale: LocaleInfo,
|
|
387
|
+
style: TimeStyle = TimeStyle.MEDIUM,
|
|
388
|
+
**options: Any,
|
|
389
|
+
) -> FormattedDate:
|
|
390
|
+
"""Format a time according to locale rules.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
value: Time to format
|
|
394
|
+
locale: Target locale
|
|
395
|
+
style: Formatting style
|
|
396
|
+
**options: Additional options (timezone, etc.)
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Formatted time result
|
|
400
|
+
"""
|
|
401
|
+
...
|
|
402
|
+
|
|
403
|
+
def format_datetime(
|
|
404
|
+
self,
|
|
405
|
+
value: datetime,
|
|
406
|
+
locale: LocaleInfo,
|
|
407
|
+
date_style: DateStyle = DateStyle.MEDIUM,
|
|
408
|
+
time_style: TimeStyle = TimeStyle.MEDIUM,
|
|
409
|
+
**options: Any,
|
|
410
|
+
) -> FormattedDate:
|
|
411
|
+
"""Format a datetime according to locale rules.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
value: Datetime to format
|
|
415
|
+
locale: Target locale
|
|
416
|
+
date_style: Date formatting style
|
|
417
|
+
time_style: Time formatting style
|
|
418
|
+
**options: Additional options
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
Formatted datetime result
|
|
422
|
+
"""
|
|
423
|
+
...
|
|
424
|
+
|
|
425
|
+
def format_relative(
|
|
426
|
+
self,
|
|
427
|
+
value: datetime | date,
|
|
428
|
+
reference: datetime | date | None = None,
|
|
429
|
+
locale: LocaleInfo | None = None,
|
|
430
|
+
) -> FormattedDate:
|
|
431
|
+
"""Format a relative date (e.g., "2 days ago").
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
value: Date to format
|
|
435
|
+
reference: Reference date (default: now)
|
|
436
|
+
locale: Target locale
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
Formatted relative date
|
|
440
|
+
"""
|
|
441
|
+
...
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@runtime_checkable
|
|
445
|
+
class TextDirectionProvider(Protocol):
|
|
446
|
+
"""Protocol for text direction handling."""
|
|
447
|
+
|
|
448
|
+
def get_direction(self, locale: LocaleInfo) -> TextDirection:
|
|
449
|
+
"""Get text direction for a locale.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
locale: Target locale
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Text direction
|
|
456
|
+
"""
|
|
457
|
+
...
|
|
458
|
+
|
|
459
|
+
def wrap_bidi(
|
|
460
|
+
self,
|
|
461
|
+
text: str,
|
|
462
|
+
direction: TextDirection,
|
|
463
|
+
embed: bool = True,
|
|
464
|
+
) -> str:
|
|
465
|
+
"""Wrap text with bidirectional control characters.
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
text: Text to wrap
|
|
469
|
+
direction: Intended direction
|
|
470
|
+
embed: If True, use embedding; otherwise use override
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
Text with bidi controls
|
|
474
|
+
"""
|
|
475
|
+
...
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
@runtime_checkable
|
|
479
|
+
class MessageResolver(Protocol):
|
|
480
|
+
"""Protocol for context-aware message resolution."""
|
|
481
|
+
|
|
482
|
+
def resolve(
|
|
483
|
+
self,
|
|
484
|
+
key: str,
|
|
485
|
+
locale: LocaleInfo,
|
|
486
|
+
context: MessageContext | None = None,
|
|
487
|
+
**params: Any,
|
|
488
|
+
) -> ResolvedMessage:
|
|
489
|
+
"""Resolve a message with context.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
key: Message key
|
|
493
|
+
locale: Target locale
|
|
494
|
+
context: Message context
|
|
495
|
+
**params: Format parameters
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
Resolved message
|
|
499
|
+
"""
|
|
500
|
+
...
|
|
501
|
+
|
|
502
|
+
def resolve_plural(
|
|
503
|
+
self,
|
|
504
|
+
key: str,
|
|
505
|
+
count: float | int,
|
|
506
|
+
locale: LocaleInfo,
|
|
507
|
+
context: MessageContext | None = None,
|
|
508
|
+
**params: Any,
|
|
509
|
+
) -> PluralizedMessage:
|
|
510
|
+
"""Resolve a pluralized message.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
key: Message key (base, without plural suffix)
|
|
514
|
+
count: Number for pluralization
|
|
515
|
+
locale: Target locale
|
|
516
|
+
context: Message context
|
|
517
|
+
**params: Additional format parameters
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
Pluralized message
|
|
521
|
+
"""
|
|
522
|
+
...
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@runtime_checkable
|
|
526
|
+
class CatalogLoader(Protocol):
|
|
527
|
+
"""Protocol for dynamic message catalog loading."""
|
|
528
|
+
|
|
529
|
+
def load(
|
|
530
|
+
self,
|
|
531
|
+
locale: LocaleInfo,
|
|
532
|
+
namespace: str | None = None,
|
|
533
|
+
) -> dict[str, str]:
|
|
534
|
+
"""Load message catalog for a locale.
|
|
535
|
+
|
|
536
|
+
Args:
|
|
537
|
+
locale: Target locale
|
|
538
|
+
namespace: Optional namespace (e.g., "validators", "errors")
|
|
539
|
+
|
|
540
|
+
Returns:
|
|
541
|
+
Dictionary of message key -> template
|
|
542
|
+
"""
|
|
543
|
+
...
|
|
544
|
+
|
|
545
|
+
def is_loaded(self, locale: LocaleInfo) -> bool:
|
|
546
|
+
"""Check if a locale catalog is loaded.
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
locale: Locale to check
|
|
550
|
+
|
|
551
|
+
Returns:
|
|
552
|
+
True if loaded
|
|
553
|
+
"""
|
|
554
|
+
...
|
|
555
|
+
|
|
556
|
+
def unload(self, locale: LocaleInfo) -> None:
|
|
557
|
+
"""Unload a locale catalog from memory.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
locale: Locale to unload
|
|
561
|
+
"""
|
|
562
|
+
...
|
|
563
|
+
|
|
564
|
+
def get_available_locales(self) -> list[LocaleInfo]:
|
|
565
|
+
"""Get list of available locales.
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
List of available LocaleInfo
|
|
569
|
+
"""
|
|
570
|
+
...
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
@runtime_checkable
|
|
574
|
+
class TranslationService(Protocol):
|
|
575
|
+
"""Protocol for external Translation Management System (TMS) integration."""
|
|
576
|
+
|
|
577
|
+
def sync_catalog(
|
|
578
|
+
self,
|
|
579
|
+
locale: LocaleInfo,
|
|
580
|
+
catalog: dict[str, str],
|
|
581
|
+
) -> dict[str, str]:
|
|
582
|
+
"""Sync local catalog with TMS.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
locale: Target locale
|
|
586
|
+
catalog: Local message catalog
|
|
587
|
+
|
|
588
|
+
Returns:
|
|
589
|
+
Updated catalog with TMS translations
|
|
590
|
+
"""
|
|
591
|
+
...
|
|
592
|
+
|
|
593
|
+
def push_new_keys(
|
|
594
|
+
self,
|
|
595
|
+
keys: list[str],
|
|
596
|
+
source_locale: LocaleInfo,
|
|
597
|
+
source_messages: dict[str, str],
|
|
598
|
+
) -> bool:
|
|
599
|
+
"""Push new message keys to TMS for translation.
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
keys: New message keys
|
|
603
|
+
source_locale: Source locale (usually "en")
|
|
604
|
+
source_messages: Source message templates
|
|
605
|
+
|
|
606
|
+
Returns:
|
|
607
|
+
True if successful
|
|
608
|
+
"""
|
|
609
|
+
...
|
|
610
|
+
|
|
611
|
+
def get_translation_status(
|
|
612
|
+
self,
|
|
613
|
+
locale: LocaleInfo,
|
|
614
|
+
) -> dict[str, float]:
|
|
615
|
+
"""Get translation completion status.
|
|
616
|
+
|
|
617
|
+
Args:
|
|
618
|
+
locale: Target locale
|
|
619
|
+
|
|
620
|
+
Returns:
|
|
621
|
+
Dictionary with completion percentages by namespace
|
|
622
|
+
"""
|
|
623
|
+
...
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
# ==============================================================================
|
|
627
|
+
# Abstract Base Classes
|
|
628
|
+
# ==============================================================================
|
|
629
|
+
|
|
630
|
+
class BasePluralRuleProvider(ABC):
|
|
631
|
+
"""Abstract base class for plural rule providers."""
|
|
632
|
+
|
|
633
|
+
@abstractmethod
|
|
634
|
+
def get_category(
|
|
635
|
+
self,
|
|
636
|
+
count: float | int,
|
|
637
|
+
locale: LocaleInfo,
|
|
638
|
+
ordinal: bool = False,
|
|
639
|
+
) -> PluralCategory:
|
|
640
|
+
"""Get the plural category for a number."""
|
|
641
|
+
pass
|
|
642
|
+
|
|
643
|
+
def get_plural_form(
|
|
644
|
+
self,
|
|
645
|
+
count: float | int,
|
|
646
|
+
forms: dict[PluralCategory, str],
|
|
647
|
+
locale: LocaleInfo,
|
|
648
|
+
) -> str:
|
|
649
|
+
"""Select appropriate plural form."""
|
|
650
|
+
category = self.get_category(count, locale)
|
|
651
|
+
|
|
652
|
+
# Try exact match first
|
|
653
|
+
if category in forms:
|
|
654
|
+
return forms[category]
|
|
655
|
+
|
|
656
|
+
# Fallback to OTHER
|
|
657
|
+
if PluralCategory.OTHER in forms:
|
|
658
|
+
return forms[PluralCategory.OTHER]
|
|
659
|
+
|
|
660
|
+
# Last resort: return first available form
|
|
661
|
+
return next(iter(forms.values()))
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
class BaseNumberFormatter(ABC):
|
|
665
|
+
"""Abstract base class for number formatters."""
|
|
666
|
+
|
|
667
|
+
@abstractmethod
|
|
668
|
+
def format(
|
|
669
|
+
self,
|
|
670
|
+
value: float | int | Decimal,
|
|
671
|
+
locale: LocaleInfo,
|
|
672
|
+
style: NumberStyle = NumberStyle.DECIMAL,
|
|
673
|
+
**options: Any,
|
|
674
|
+
) -> FormattedNumber:
|
|
675
|
+
"""Format a number according to locale rules."""
|
|
676
|
+
pass
|
|
677
|
+
|
|
678
|
+
def parse(
|
|
679
|
+
self,
|
|
680
|
+
text: str,
|
|
681
|
+
locale: LocaleInfo,
|
|
682
|
+
style: NumberStyle = NumberStyle.DECIMAL,
|
|
683
|
+
) -> float | int | Decimal | None:
|
|
684
|
+
"""Parse a localized number string."""
|
|
685
|
+
# Default implementation: try to parse after cleaning
|
|
686
|
+
try:
|
|
687
|
+
# Remove common thousands separators and normalize decimal
|
|
688
|
+
cleaned = text.strip()
|
|
689
|
+
# Basic implementation - subclasses should override
|
|
690
|
+
return float(cleaned.replace(",", "").replace(" ", ""))
|
|
691
|
+
except ValueError:
|
|
692
|
+
return None
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
class BaseDateFormatter(ABC):
|
|
696
|
+
"""Abstract base class for date formatters."""
|
|
697
|
+
|
|
698
|
+
@abstractmethod
|
|
699
|
+
def format_date(
|
|
700
|
+
self,
|
|
701
|
+
value: datetime | date,
|
|
702
|
+
locale: LocaleInfo,
|
|
703
|
+
style: DateStyle = DateStyle.MEDIUM,
|
|
704
|
+
**options: Any,
|
|
705
|
+
) -> FormattedDate:
|
|
706
|
+
"""Format a date according to locale rules."""
|
|
707
|
+
pass
|
|
708
|
+
|
|
709
|
+
@abstractmethod
|
|
710
|
+
def format_time(
|
|
711
|
+
self,
|
|
712
|
+
value: datetime | time,
|
|
713
|
+
locale: LocaleInfo,
|
|
714
|
+
style: TimeStyle = TimeStyle.MEDIUM,
|
|
715
|
+
**options: Any,
|
|
716
|
+
) -> FormattedDate:
|
|
717
|
+
"""Format a time according to locale rules."""
|
|
718
|
+
pass
|
|
719
|
+
|
|
720
|
+
def format_datetime(
|
|
721
|
+
self,
|
|
722
|
+
value: datetime,
|
|
723
|
+
locale: LocaleInfo,
|
|
724
|
+
date_style: DateStyle = DateStyle.MEDIUM,
|
|
725
|
+
time_style: TimeStyle = TimeStyle.MEDIUM,
|
|
726
|
+
**options: Any,
|
|
727
|
+
) -> FormattedDate:
|
|
728
|
+
"""Format a datetime according to locale rules."""
|
|
729
|
+
date_result = self.format_date(value, locale, date_style, **options)
|
|
730
|
+
time_result = self.format_time(value, locale, time_style, **options)
|
|
731
|
+
|
|
732
|
+
# Combine date and time
|
|
733
|
+
combined = f"{date_result.formatted} {time_result.formatted}"
|
|
734
|
+
|
|
735
|
+
return FormattedDate(
|
|
736
|
+
value=value,
|
|
737
|
+
formatted=combined,
|
|
738
|
+
direction=date_result.direction,
|
|
739
|
+
calendar=date_result.calendar,
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
def format_relative(
|
|
743
|
+
self,
|
|
744
|
+
value: datetime | date,
|
|
745
|
+
reference: datetime | date | None = None,
|
|
746
|
+
locale: LocaleInfo | None = None,
|
|
747
|
+
) -> FormattedDate:
|
|
748
|
+
"""Format a relative date (e.g., "2 days ago")."""
|
|
749
|
+
if reference is None:
|
|
750
|
+
reference = datetime.now()
|
|
751
|
+
|
|
752
|
+
if isinstance(value, date) and not isinstance(value, datetime):
|
|
753
|
+
value = datetime.combine(value, time.min)
|
|
754
|
+
if isinstance(reference, date) and not isinstance(reference, datetime):
|
|
755
|
+
reference = datetime.combine(reference, time.min)
|
|
756
|
+
|
|
757
|
+
delta = value - reference
|
|
758
|
+
days = delta.days
|
|
759
|
+
|
|
760
|
+
# Basic English implementation - subclasses should override
|
|
761
|
+
if days == 0:
|
|
762
|
+
return FormattedDate(value=value, formatted="today")
|
|
763
|
+
elif days == 1:
|
|
764
|
+
return FormattedDate(value=value, formatted="tomorrow")
|
|
765
|
+
elif days == -1:
|
|
766
|
+
return FormattedDate(value=value, formatted="yesterday")
|
|
767
|
+
elif days > 0:
|
|
768
|
+
return FormattedDate(value=value, formatted=f"in {days} days")
|
|
769
|
+
else:
|
|
770
|
+
return FormattedDate(value=value, formatted=f"{-days} days ago")
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
class BaseCatalogLoader(ABC):
|
|
774
|
+
"""Abstract base class for catalog loaders."""
|
|
775
|
+
|
|
776
|
+
def __init__(self) -> None:
|
|
777
|
+
self._loaded_catalogs: dict[str, dict[str, str]] = {}
|
|
778
|
+
|
|
779
|
+
@abstractmethod
|
|
780
|
+
def _do_load(
|
|
781
|
+
self,
|
|
782
|
+
locale: LocaleInfo,
|
|
783
|
+
namespace: str | None = None,
|
|
784
|
+
) -> dict[str, str]:
|
|
785
|
+
"""Internal load implementation."""
|
|
786
|
+
pass
|
|
787
|
+
|
|
788
|
+
def load(
|
|
789
|
+
self,
|
|
790
|
+
locale: LocaleInfo,
|
|
791
|
+
namespace: str | None = None,
|
|
792
|
+
) -> dict[str, str]:
|
|
793
|
+
"""Load message catalog for a locale."""
|
|
794
|
+
cache_key = f"{locale.tag}:{namespace or 'default'}"
|
|
795
|
+
|
|
796
|
+
if cache_key not in self._loaded_catalogs:
|
|
797
|
+
self._loaded_catalogs[cache_key] = self._do_load(locale, namespace)
|
|
798
|
+
|
|
799
|
+
return self._loaded_catalogs[cache_key]
|
|
800
|
+
|
|
801
|
+
def is_loaded(self, locale: LocaleInfo) -> bool:
|
|
802
|
+
"""Check if a locale catalog is loaded."""
|
|
803
|
+
return any(
|
|
804
|
+
key.startswith(locale.tag)
|
|
805
|
+
for key in self._loaded_catalogs
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
def unload(self, locale: LocaleInfo) -> None:
|
|
809
|
+
"""Unload a locale catalog from memory."""
|
|
810
|
+
keys_to_remove = [
|
|
811
|
+
key for key in self._loaded_catalogs
|
|
812
|
+
if key.startswith(locale.tag)
|
|
813
|
+
]
|
|
814
|
+
for key in keys_to_remove:
|
|
815
|
+
del self._loaded_catalogs[key]
|
|
816
|
+
|
|
817
|
+
@abstractmethod
|
|
818
|
+
def get_available_locales(self) -> list[LocaleInfo]:
|
|
819
|
+
"""Get list of available locales."""
|
|
820
|
+
pass
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
class BaseTranslationService(ABC):
|
|
824
|
+
"""Abstract base class for TMS integrations."""
|
|
825
|
+
|
|
826
|
+
def __init__(self, api_key: str | None = None, project_id: str | None = None):
|
|
827
|
+
self.api_key = api_key
|
|
828
|
+
self.project_id = project_id
|
|
829
|
+
|
|
830
|
+
@abstractmethod
|
|
831
|
+
def sync_catalog(
|
|
832
|
+
self,
|
|
833
|
+
locale: LocaleInfo,
|
|
834
|
+
catalog: dict[str, str],
|
|
835
|
+
) -> dict[str, str]:
|
|
836
|
+
"""Sync local catalog with TMS."""
|
|
837
|
+
pass
|
|
838
|
+
|
|
839
|
+
@abstractmethod
|
|
840
|
+
def push_new_keys(
|
|
841
|
+
self,
|
|
842
|
+
keys: list[str],
|
|
843
|
+
source_locale: LocaleInfo,
|
|
844
|
+
source_messages: dict[str, str],
|
|
845
|
+
) -> bool:
|
|
846
|
+
"""Push new message keys to TMS for translation."""
|
|
847
|
+
pass
|
|
848
|
+
|
|
849
|
+
@abstractmethod
|
|
850
|
+
def get_translation_status(
|
|
851
|
+
self,
|
|
852
|
+
locale: LocaleInfo,
|
|
853
|
+
) -> dict[str, float]:
|
|
854
|
+
"""Get translation completion status."""
|
|
855
|
+
pass
|