pycharter 0.0.22__py3-none-any.whl → 0.0.24__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.
- api/main.py +27 -1
- api/models/docs.py +68 -0
- api/models/evolution.py +117 -0
- api/models/tracking.py +111 -0
- api/models/validation.py +46 -6
- api/routes/v1/__init__.py +14 -1
- api/routes/v1/docs.py +187 -0
- api/routes/v1/evolution.py +337 -0
- api/routes/v1/templates.py +211 -27
- api/routes/v1/tracking.py +301 -0
- api/routes/v1/validation.py +68 -31
- pycharter/__init__.py +268 -58
- pycharter/data/templates/contract/template_coercion_rules.yaml +57 -0
- pycharter/data/templates/contract/template_contract.yaml +122 -0
- pycharter/data/templates/contract/template_metadata.yaml +68 -0
- pycharter/data/templates/contract/template_schema.yaml +100 -0
- pycharter/data/templates/contract/template_validation_rules.yaml +75 -0
- pycharter/data/templates/etl/README.md +224 -0
- pycharter/data/templates/etl/extract_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/extract_cloud_gcs.yaml +25 -0
- pycharter/data/templates/etl/extract_cloud_s3.yaml +30 -0
- pycharter/data/templates/etl/extract_database.yaml +34 -0
- pycharter/data/templates/etl/extract_database_ssh.yaml +40 -0
- pycharter/data/templates/etl/extract_file_csv.yaml +21 -0
- pycharter/data/templates/etl/extract_file_glob.yaml +25 -0
- pycharter/data/templates/etl/extract_file_json.yaml +24 -0
- pycharter/data/templates/etl/extract_file_parquet.yaml +20 -0
- pycharter/data/templates/etl/extract_http_paginated.yaml +79 -0
- pycharter/data/templates/etl/extract_http_path_params.yaml +38 -0
- pycharter/data/templates/etl/extract_http_simple.yaml +62 -0
- pycharter/data/templates/etl/load_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/load_cloud_gcs.yaml +22 -0
- pycharter/data/templates/etl/load_cloud_s3.yaml +27 -0
- pycharter/data/templates/etl/load_file.yaml +34 -0
- pycharter/data/templates/etl/load_insert.yaml +18 -0
- pycharter/data/templates/etl/load_postgresql.yaml +39 -0
- pycharter/data/templates/etl/load_sqlite.yaml +21 -0
- pycharter/data/templates/etl/load_truncate_and_load.yaml +20 -0
- pycharter/data/templates/etl/load_upsert.yaml +25 -0
- pycharter/data/templates/etl/load_with_dlq.yaml +34 -0
- pycharter/data/templates/etl/load_with_ssh_tunnel.yaml +35 -0
- pycharter/data/templates/etl/pipeline_http_to_db.yaml +75 -0
- pycharter/data/templates/etl/transform_combined.yaml +48 -0
- pycharter/data/templates/etl/transform_custom_function.yaml +58 -0
- pycharter/data/templates/etl/transform_jsonata.yaml +51 -0
- pycharter/data/templates/etl/transform_simple.yaml +59 -0
- pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +160 -0
- pycharter/docs_generator/__init__.py +43 -0
- pycharter/docs_generator/generator.py +465 -0
- pycharter/docs_generator/renderers.py +247 -0
- pycharter/etl_generator/__init__.py +168 -80
- pycharter/etl_generator/builder.py +121 -0
- pycharter/etl_generator/config_loader.py +394 -0
- pycharter/etl_generator/config_validator.py +418 -0
- pycharter/etl_generator/context.py +132 -0
- pycharter/etl_generator/expression.py +499 -0
- pycharter/etl_generator/extractors/__init__.py +30 -0
- pycharter/etl_generator/extractors/base.py +70 -0
- pycharter/etl_generator/extractors/cloud_storage.py +530 -0
- pycharter/etl_generator/extractors/database.py +221 -0
- pycharter/etl_generator/extractors/factory.py +185 -0
- pycharter/etl_generator/extractors/file.py +475 -0
- pycharter/etl_generator/extractors/http.py +895 -0
- pycharter/etl_generator/extractors/streaming.py +57 -0
- pycharter/etl_generator/loaders/__init__.py +41 -0
- pycharter/etl_generator/loaders/base.py +35 -0
- pycharter/etl_generator/loaders/cloud.py +87 -0
- pycharter/etl_generator/loaders/cloud_storage_loader.py +275 -0
- pycharter/etl_generator/loaders/database.py +274 -0
- pycharter/etl_generator/loaders/factory.py +180 -0
- pycharter/etl_generator/loaders/file.py +72 -0
- pycharter/etl_generator/loaders/file_loader.py +130 -0
- pycharter/etl_generator/pipeline.py +743 -0
- pycharter/etl_generator/protocols.py +54 -0
- pycharter/etl_generator/result.py +63 -0
- pycharter/etl_generator/schemas/__init__.py +49 -0
- pycharter/etl_generator/transformers/__init__.py +49 -0
- pycharter/etl_generator/transformers/base.py +63 -0
- pycharter/etl_generator/transformers/config.py +45 -0
- pycharter/etl_generator/transformers/custom_function.py +101 -0
- pycharter/etl_generator/transformers/jsonata_transformer.py +56 -0
- pycharter/etl_generator/transformers/operations.py +218 -0
- pycharter/etl_generator/transformers/pipeline.py +54 -0
- pycharter/etl_generator/transformers/simple_operations.py +131 -0
- pycharter/quality/__init__.py +25 -0
- pycharter/quality/tracking/__init__.py +64 -0
- pycharter/quality/tracking/collector.py +318 -0
- pycharter/quality/tracking/exporters.py +238 -0
- pycharter/quality/tracking/models.py +194 -0
- pycharter/quality/tracking/store.py +385 -0
- pycharter/runtime_validator/__init__.py +20 -7
- pycharter/runtime_validator/builder.py +328 -0
- pycharter/runtime_validator/validator.py +311 -7
- pycharter/runtime_validator/validator_core.py +61 -0
- pycharter/schema_evolution/__init__.py +61 -0
- pycharter/schema_evolution/compatibility.py +270 -0
- pycharter/schema_evolution/diff.py +496 -0
- pycharter/schema_evolution/models.py +201 -0
- pycharter/shared/__init__.py +56 -0
- pycharter/shared/errors.py +296 -0
- pycharter/shared/protocols.py +234 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/METADATA +146 -26
- pycharter-0.0.24.dist-info/RECORD +543 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/WHEEL +1 -1
- ui/static/404/index.html +1 -1
- ui/static/404.html +1 -1
- ui/static/__next.__PAGE__.txt +1 -1
- ui/static/__next._full.txt +1 -1
- ui/static/__next._head.txt +1 -1
- ui/static/__next._index.txt +1 -1
- ui/static/__next._tree.txt +1 -1
- ui/static/_next/static/chunks/26dfc590f7714c03.js +1 -0
- ui/static/_next/static/chunks/34d289e6db2ef551.js +1 -0
- ui/static/_next/static/chunks/99508d9d5869cc27.js +1 -0
- ui/static/_next/static/chunks/b313c35a6ba76574.js +1 -0
- ui/static/_not-found/__next._full.txt +1 -1
- ui/static/_not-found/__next._head.txt +1 -1
- ui/static/_not-found/__next._index.txt +1 -1
- ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/_not-found/__next._not-found.txt +1 -1
- ui/static/_not-found/__next._tree.txt +1 -1
- ui/static/_not-found/index.html +1 -1
- ui/static/_not-found/index.txt +1 -1
- ui/static/contracts/__next._full.txt +2 -2
- ui/static/contracts/__next._head.txt +1 -1
- ui/static/contracts/__next._index.txt +1 -1
- ui/static/contracts/__next._tree.txt +1 -1
- ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/contracts/__next.contracts.txt +1 -1
- ui/static/contracts/index.html +1 -1
- ui/static/contracts/index.txt +2 -2
- ui/static/documentation/__next._full.txt +1 -1
- ui/static/documentation/__next._head.txt +1 -1
- ui/static/documentation/__next._index.txt +1 -1
- ui/static/documentation/__next._tree.txt +1 -1
- ui/static/documentation/__next.documentation.__PAGE__.txt +1 -1
- ui/static/documentation/__next.documentation.txt +1 -1
- ui/static/documentation/index.html +2 -2
- ui/static/documentation/index.txt +1 -1
- ui/static/index.html +1 -1
- ui/static/index.txt +1 -1
- ui/static/metadata/__next._full.txt +1 -1
- ui/static/metadata/__next._head.txt +1 -1
- ui/static/metadata/__next._index.txt +1 -1
- ui/static/metadata/__next._tree.txt +1 -1
- ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/metadata/__next.metadata.txt +1 -1
- ui/static/metadata/index.html +1 -1
- ui/static/metadata/index.txt +1 -1
- ui/static/quality/__next._full.txt +2 -2
- ui/static/quality/__next._head.txt +1 -1
- ui/static/quality/__next._index.txt +1 -1
- ui/static/quality/__next._tree.txt +1 -1
- ui/static/quality/__next.quality.__PAGE__.txt +2 -2
- ui/static/quality/__next.quality.txt +1 -1
- ui/static/quality/index.html +2 -2
- ui/static/quality/index.txt +2 -2
- ui/static/rules/__next._full.txt +1 -1
- ui/static/rules/__next._head.txt +1 -1
- ui/static/rules/__next._index.txt +1 -1
- ui/static/rules/__next._tree.txt +1 -1
- ui/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/rules/__next.rules.txt +1 -1
- ui/static/rules/index.html +1 -1
- ui/static/rules/index.txt +1 -1
- ui/static/schemas/__next._full.txt +1 -1
- ui/static/schemas/__next._head.txt +1 -1
- ui/static/schemas/__next._index.txt +1 -1
- ui/static/schemas/__next._tree.txt +1 -1
- ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/schemas/__next.schemas.txt +1 -1
- ui/static/schemas/index.html +1 -1
- ui/static/schemas/index.txt +1 -1
- ui/static/settings/__next._full.txt +1 -1
- ui/static/settings/__next._head.txt +1 -1
- ui/static/settings/__next._index.txt +1 -1
- ui/static/settings/__next._tree.txt +1 -1
- ui/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/settings/__next.settings.txt +1 -1
- ui/static/settings/index.html +1 -1
- ui/static/settings/index.txt +1 -1
- ui/static/static/404/index.html +1 -1
- ui/static/static/404.html +1 -1
- ui/static/static/__next.__PAGE__.txt +1 -1
- ui/static/static/__next._full.txt +2 -2
- ui/static/static/__next._head.txt +1 -1
- ui/static/static/__next._index.txt +2 -2
- ui/static/static/__next._tree.txt +2 -2
- ui/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
- ui/static/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
- ui/static/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
- ui/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- ui/static/static/_not-found/__next._full.txt +2 -2
- ui/static/static/_not-found/__next._head.txt +1 -1
- ui/static/static/_not-found/__next._index.txt +2 -2
- ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/static/_not-found/__next._not-found.txt +1 -1
- ui/static/static/_not-found/__next._tree.txt +2 -2
- ui/static/static/_not-found/index.html +1 -1
- ui/static/static/_not-found/index.txt +2 -2
- ui/static/static/contracts/__next._full.txt +3 -3
- ui/static/static/contracts/__next._head.txt +1 -1
- ui/static/static/contracts/__next._index.txt +2 -2
- ui/static/static/contracts/__next._tree.txt +2 -2
- ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/static/contracts/__next.contracts.txt +1 -1
- ui/static/static/contracts/index.html +1 -1
- ui/static/static/contracts/index.txt +3 -3
- ui/static/static/documentation/__next._full.txt +3 -3
- ui/static/static/documentation/__next._head.txt +1 -1
- ui/static/static/documentation/__next._index.txt +2 -2
- ui/static/static/documentation/__next._tree.txt +2 -2
- ui/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
- ui/static/static/documentation/__next.documentation.txt +1 -1
- ui/static/static/documentation/index.html +2 -2
- ui/static/static/documentation/index.txt +3 -3
- ui/static/static/index.html +1 -1
- ui/static/static/index.txt +2 -2
- ui/static/static/metadata/__next._full.txt +2 -2
- ui/static/static/metadata/__next._head.txt +1 -1
- ui/static/static/metadata/__next._index.txt +2 -2
- ui/static/static/metadata/__next._tree.txt +2 -2
- ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/static/metadata/__next.metadata.txt +1 -1
- ui/static/static/metadata/index.html +1 -1
- ui/static/static/metadata/index.txt +2 -2
- ui/static/static/quality/__next._full.txt +2 -2
- ui/static/static/quality/__next._head.txt +1 -1
- ui/static/static/quality/__next._index.txt +2 -2
- ui/static/static/quality/__next._tree.txt +2 -2
- ui/static/static/quality/__next.quality.__PAGE__.txt +1 -1
- ui/static/static/quality/__next.quality.txt +1 -1
- ui/static/static/quality/index.html +2 -2
- ui/static/static/quality/index.txt +2 -2
- ui/static/static/rules/__next._full.txt +2 -2
- ui/static/static/rules/__next._head.txt +1 -1
- ui/static/static/rules/__next._index.txt +2 -2
- ui/static/static/rules/__next._tree.txt +2 -2
- ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/static/rules/__next.rules.txt +1 -1
- ui/static/static/rules/index.html +1 -1
- ui/static/static/rules/index.txt +2 -2
- ui/static/static/schemas/__next._full.txt +2 -2
- ui/static/static/schemas/__next._head.txt +1 -1
- ui/static/static/schemas/__next._index.txt +2 -2
- ui/static/static/schemas/__next._tree.txt +2 -2
- ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/static/schemas/__next.schemas.txt +1 -1
- ui/static/static/schemas/index.html +1 -1
- ui/static/static/schemas/index.txt +2 -2
- ui/static/static/settings/__next._full.txt +2 -2
- ui/static/static/settings/__next._head.txt +1 -1
- ui/static/static/settings/__next._index.txt +2 -2
- ui/static/static/settings/__next._tree.txt +2 -2
- ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/static/settings/__next.settings.txt +1 -1
- ui/static/static/settings/index.html +1 -1
- ui/static/static/settings/index.txt +2 -2
- ui/static/static/static/.gitkeep +0 -0
- ui/static/static/static/404/index.html +1 -0
- ui/static/static/static/404.html +1 -0
- ui/static/static/static/__next.__PAGE__.txt +10 -0
- ui/static/static/static/__next._full.txt +30 -0
- ui/static/static/static/__next._head.txt +7 -0
- ui/static/static/static/__next._index.txt +9 -0
- ui/static/static/static/__next._tree.txt +2 -0
- ui/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
- ui/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
- ui/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
- ui/static/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
- ui/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
- ui/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
- ui/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
- ui/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
- ui/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
- ui/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- ui/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
- ui/static/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
- ui/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
- ui/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
- ui/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
- ui/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
- ui/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
- ui/static/static/static/_not-found/__next._full.txt +17 -0
- ui/static/static/static/_not-found/__next._head.txt +7 -0
- ui/static/static/static/_not-found/__next._index.txt +9 -0
- ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- ui/static/static/static/_not-found/__next._not-found.txt +4 -0
- ui/static/static/static/_not-found/__next._tree.txt +2 -0
- ui/static/static/static/_not-found/index.html +1 -0
- ui/static/static/static/_not-found/index.txt +17 -0
- ui/static/static/static/contracts/__next._full.txt +21 -0
- ui/static/static/static/contracts/__next._head.txt +7 -0
- ui/static/static/static/contracts/__next._index.txt +9 -0
- ui/static/static/static/contracts/__next._tree.txt +2 -0
- ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- ui/static/static/static/contracts/__next.contracts.txt +4 -0
- ui/static/static/static/contracts/index.html +1 -0
- ui/static/static/static/contracts/index.txt +21 -0
- ui/static/static/static/documentation/__next._full.txt +21 -0
- ui/static/static/static/documentation/__next._head.txt +7 -0
- ui/static/static/static/documentation/__next._index.txt +9 -0
- ui/static/static/static/documentation/__next._tree.txt +2 -0
- ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- ui/static/static/static/documentation/__next.documentation.txt +4 -0
- ui/static/static/static/documentation/index.html +93 -0
- ui/static/static/static/documentation/index.txt +21 -0
- ui/static/static/static/index.html +1 -0
- ui/static/static/static/index.txt +30 -0
- ui/static/static/static/metadata/__next._full.txt +21 -0
- ui/static/static/static/metadata/__next._head.txt +7 -0
- ui/static/static/static/metadata/__next._index.txt +9 -0
- ui/static/static/static/metadata/__next._tree.txt +2 -0
- ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- ui/static/static/static/metadata/__next.metadata.txt +4 -0
- ui/static/static/static/metadata/index.html +1 -0
- ui/static/static/static/metadata/index.txt +21 -0
- ui/static/static/static/quality/__next._full.txt +21 -0
- ui/static/static/static/quality/__next._head.txt +7 -0
- ui/static/static/static/quality/__next._index.txt +9 -0
- ui/static/static/static/quality/__next._tree.txt +2 -0
- ui/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
- ui/static/static/static/quality/__next.quality.txt +4 -0
- ui/static/static/static/quality/index.html +2 -0
- ui/static/static/static/quality/index.txt +21 -0
- ui/static/static/static/rules/__next._full.txt +21 -0
- ui/static/static/static/rules/__next._head.txt +7 -0
- ui/static/static/static/rules/__next._index.txt +9 -0
- ui/static/static/static/rules/__next._tree.txt +2 -0
- ui/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
- ui/static/static/static/rules/__next.rules.txt +4 -0
- ui/static/static/static/rules/index.html +1 -0
- ui/static/static/static/rules/index.txt +21 -0
- ui/static/static/static/schemas/__next._full.txt +21 -0
- ui/static/static/static/schemas/__next._head.txt +7 -0
- ui/static/static/static/schemas/__next._index.txt +9 -0
- ui/static/static/static/schemas/__next._tree.txt +2 -0
- ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- ui/static/static/static/schemas/__next.schemas.txt +4 -0
- ui/static/static/static/schemas/index.html +1 -0
- ui/static/static/static/schemas/index.txt +21 -0
- ui/static/static/static/settings/__next._full.txt +21 -0
- ui/static/static/static/settings/__next._head.txt +7 -0
- ui/static/static/static/settings/__next._index.txt +9 -0
- ui/static/static/static/settings/__next._tree.txt +2 -0
- ui/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
- ui/static/static/static/settings/__next.settings.txt +4 -0
- ui/static/static/static/settings/index.html +1 -0
- ui/static/static/static/settings/index.txt +21 -0
- ui/static/static/static/validation/__next._full.txt +21 -0
- ui/static/static/static/validation/__next._head.txt +7 -0
- ui/static/static/static/validation/__next._index.txt +9 -0
- ui/static/static/static/validation/__next._tree.txt +2 -0
- ui/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
- ui/static/static/static/validation/__next.validation.txt +4 -0
- ui/static/static/static/validation/index.html +1 -0
- ui/static/static/static/validation/index.txt +21 -0
- ui/static/static/validation/__next._full.txt +2 -2
- ui/static/static/validation/__next._head.txt +1 -1
- ui/static/static/validation/__next._index.txt +2 -2
- ui/static/static/validation/__next._tree.txt +2 -2
- ui/static/static/validation/__next.validation.__PAGE__.txt +1 -1
- ui/static/static/validation/__next.validation.txt +1 -1
- ui/static/static/validation/index.html +1 -1
- ui/static/static/validation/index.txt +2 -2
- ui/static/validation/__next._full.txt +2 -2
- ui/static/validation/__next._head.txt +1 -1
- ui/static/validation/__next._index.txt +1 -1
- ui/static/validation/__next._tree.txt +1 -1
- ui/static/validation/__next.validation.__PAGE__.txt +2 -2
- ui/static/validation/__next.validation.txt +1 -1
- ui/static/validation/index.html +1 -1
- ui/static/validation/index.txt +2 -2
- pycharter/data/templates/template_coercion_rules.yaml +0 -15
- pycharter/data/templates/template_contract.yaml +0 -587
- pycharter/data/templates/template_metadata.yaml +0 -38
- pycharter/data/templates/template_schema.yaml +0 -22
- pycharter/data/templates/template_transform_advanced.yaml +0 -50
- pycharter/data/templates/template_transform_simple.yaml +0 -59
- pycharter/data/templates/template_validation_rules.yaml +0 -29
- pycharter/etl_generator/extraction.py +0 -916
- pycharter/etl_generator/factory.py +0 -174
- pycharter/etl_generator/orchestrator.py +0 -1650
- pycharter/integrations/__init__.py +0 -19
- pycharter/integrations/kafka.py +0 -178
- pycharter/integrations/streaming.py +0 -100
- pycharter-0.0.22.dist-info/RECORD +0 -358
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/licenses/LICENSE +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/top_level.txt +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- /ui/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
pycharter/shared/__init__.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Shared utilities used across all services.
|
|
3
3
|
|
|
4
4
|
Includes:
|
|
5
|
+
- Protocols (interface definitions for extensible components)
|
|
5
6
|
- Coercions (pre-validation transformations)
|
|
6
7
|
- Validations (post-validation checks)
|
|
7
8
|
- JSON Schema support utilities
|
|
@@ -20,6 +21,19 @@ from pycharter.shared.json_schema_validator import (
|
|
|
20
21
|
is_valid_json_schema,
|
|
21
22
|
validate_json_schema,
|
|
22
23
|
)
|
|
24
|
+
from pycharter.shared.protocols import (
|
|
25
|
+
MetadataStore,
|
|
26
|
+
CoercionFunc,
|
|
27
|
+
CoercionRegistry,
|
|
28
|
+
ValidatorFunc,
|
|
29
|
+
ValidationFactory,
|
|
30
|
+
ValidationRegistry,
|
|
31
|
+
ContractParser,
|
|
32
|
+
ContractMetadataProtocol,
|
|
33
|
+
ModelGenerator,
|
|
34
|
+
DataValidator,
|
|
35
|
+
ValidationResultProtocol,
|
|
36
|
+
)
|
|
23
37
|
from pycharter.shared.schema_parser import (
|
|
24
38
|
get_schema_type,
|
|
25
39
|
is_required,
|
|
@@ -33,8 +47,35 @@ from pycharter.shared.name_validator import (
|
|
|
33
47
|
validate_name,
|
|
34
48
|
)
|
|
35
49
|
from pycharter.shared.validations import get_validation, register_validation
|
|
50
|
+
from pycharter.shared.errors import (
|
|
51
|
+
PyCharterError,
|
|
52
|
+
ConfigError,
|
|
53
|
+
ConfigValidationError,
|
|
54
|
+
ConfigLoadError,
|
|
55
|
+
ExpressionError,
|
|
56
|
+
ErrorMode,
|
|
57
|
+
ErrorContext,
|
|
58
|
+
get_error_context,
|
|
59
|
+
set_error_mode,
|
|
60
|
+
handle_error,
|
|
61
|
+
handle_warning,
|
|
62
|
+
StrictMode,
|
|
63
|
+
LenientMode,
|
|
64
|
+
)
|
|
36
65
|
|
|
37
66
|
__all__ = [
|
|
67
|
+
# Protocols
|
|
68
|
+
"MetadataStore",
|
|
69
|
+
"CoercionFunc",
|
|
70
|
+
"CoercionRegistry",
|
|
71
|
+
"ValidatorFunc",
|
|
72
|
+
"ValidationFactory",
|
|
73
|
+
"ValidationRegistry",
|
|
74
|
+
"ContractParser",
|
|
75
|
+
"ContractMetadataProtocol",
|
|
76
|
+
"ModelGenerator",
|
|
77
|
+
"DataValidator",
|
|
78
|
+
"ValidationResultProtocol",
|
|
38
79
|
# Coercions
|
|
39
80
|
"get_coercion",
|
|
40
81
|
"register_coercion",
|
|
@@ -62,4 +103,19 @@ __all__ = [
|
|
|
62
103
|
"validate_name",
|
|
63
104
|
"is_valid_name",
|
|
64
105
|
"normalize_name",
|
|
106
|
+
# Exception hierarchy
|
|
107
|
+
"PyCharterError",
|
|
108
|
+
"ConfigError",
|
|
109
|
+
"ConfigValidationError",
|
|
110
|
+
"ConfigLoadError",
|
|
111
|
+
"ExpressionError",
|
|
112
|
+
# Error handling
|
|
113
|
+
"ErrorMode",
|
|
114
|
+
"ErrorContext",
|
|
115
|
+
"get_error_context",
|
|
116
|
+
"set_error_mode",
|
|
117
|
+
"handle_error",
|
|
118
|
+
"handle_warning",
|
|
119
|
+
"StrictMode",
|
|
120
|
+
"LenientMode",
|
|
65
121
|
]
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Standardized error handling for pycharter.
|
|
3
|
+
|
|
4
|
+
Exception hierarchy:
|
|
5
|
+
- PyCharterError: Base for all pycharter exceptions (catch this for any pycharter error)
|
|
6
|
+
- ConfigError: Config loading/parsing failures (e.g. missing file, invalid YAML)
|
|
7
|
+
- ConfigValidationError: Schema validation failures (e.g. missing required 'type' field)
|
|
8
|
+
- ExpressionError: Expression evaluation failures (e.g. invalid syntax in add field)
|
|
9
|
+
|
|
10
|
+
Error handling modes:
|
|
11
|
+
- STRICT: Raise exceptions immediately on errors
|
|
12
|
+
- LENIENT: Log warnings and continue (best effort)
|
|
13
|
+
- COLLECT: Collect errors and return them with results
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
import warnings
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from enum import Enum
|
|
20
|
+
from typing import Any, List, Optional
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# =============================================================================
|
|
26
|
+
# EXCEPTION HIERARCHY
|
|
27
|
+
# =============================================================================
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PyCharterError(Exception):
|
|
31
|
+
"""
|
|
32
|
+
Base exception for all pycharter errors.
|
|
33
|
+
|
|
34
|
+
Catch this to handle any pycharter-related failure without depending on
|
|
35
|
+
specific exception types. Subclasses provide more specific handling.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
try:
|
|
39
|
+
pipeline = Pipeline.from_config_files(extract="e.yaml", load="l.yaml")
|
|
40
|
+
except PyCharterError as e:
|
|
41
|
+
logger.error("Pipeline config failed: %s", e)
|
|
42
|
+
"""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ConfigError(PyCharterError):
|
|
47
|
+
"""
|
|
48
|
+
Raised when configuration cannot be loaded or parsed.
|
|
49
|
+
|
|
50
|
+
Use for: missing files, invalid YAML/JSON, I/O errors during config load.
|
|
51
|
+
"""
|
|
52
|
+
def __init__(self, message: str, path: Optional[str] = None):
|
|
53
|
+
self.path = path
|
|
54
|
+
super().__init__(message + (f" (path: {path})" if path else ""))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ConfigValidationError(PyCharterError):
|
|
58
|
+
"""
|
|
59
|
+
Raised when configuration fails schema validation.
|
|
60
|
+
|
|
61
|
+
Use for: missing required fields (e.g. 'type'), invalid structure,
|
|
62
|
+
schema constraint violations. May include a list of validation errors.
|
|
63
|
+
"""
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
message: str,
|
|
67
|
+
errors: Optional[List[dict]] = None,
|
|
68
|
+
config_type: Optional[str] = None,
|
|
69
|
+
config_path: Optional[str] = None,
|
|
70
|
+
):
|
|
71
|
+
self.message = message
|
|
72
|
+
self.errors = errors or []
|
|
73
|
+
self.config_type = config_type
|
|
74
|
+
self.config_path = config_path
|
|
75
|
+
super().__init__(self._format_message())
|
|
76
|
+
|
|
77
|
+
def _format_message(self) -> str:
|
|
78
|
+
parts = [self.message]
|
|
79
|
+
if self.config_path:
|
|
80
|
+
parts.append(f" File: {self.config_path}")
|
|
81
|
+
if self.errors:
|
|
82
|
+
parts.append("\nValidation errors:")
|
|
83
|
+
for i, error in enumerate(self.errors[:10], 1):
|
|
84
|
+
path = error.get("path", "")
|
|
85
|
+
msg = error.get("message", "Unknown error")
|
|
86
|
+
parts.append(f" {i}. {path}: {msg}" if path else f" {i}. {msg}")
|
|
87
|
+
if len(self.errors) > 10:
|
|
88
|
+
parts.append(f" ... and {len(self.errors) - 10} more errors")
|
|
89
|
+
return "\n".join(parts)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ConfigLoadError(ConfigError):
|
|
93
|
+
"""
|
|
94
|
+
Raised when pipeline config cannot be loaded (missing file, invalid YAML, I/O).
|
|
95
|
+
|
|
96
|
+
Subclass of ConfigError for backward compatibility and ETL-specific usage.
|
|
97
|
+
"""
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ExpressionError(PyCharterError):
|
|
102
|
+
"""
|
|
103
|
+
Raised when an expression cannot be evaluated (e.g. in add field, defaults).
|
|
104
|
+
|
|
105
|
+
Use for: invalid expression syntax, missing variables, type errors in evaluation.
|
|
106
|
+
"""
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ErrorMode(Enum):
|
|
111
|
+
"""Error handling mode for pycharter operations."""
|
|
112
|
+
|
|
113
|
+
STRICT = "strict"
|
|
114
|
+
"""Raise exceptions immediately on any error."""
|
|
115
|
+
|
|
116
|
+
LENIENT = "lenient"
|
|
117
|
+
"""Log warnings and continue with best effort."""
|
|
118
|
+
|
|
119
|
+
COLLECT = "collect"
|
|
120
|
+
"""Collect errors and return them with results."""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass
|
|
124
|
+
class ErrorContext:
|
|
125
|
+
"""
|
|
126
|
+
Context for tracking errors during operations.
|
|
127
|
+
|
|
128
|
+
Use with ErrorMode.COLLECT to gather errors without stopping.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
mode: ErrorMode = ErrorMode.LENIENT
|
|
132
|
+
errors: List[str] = field(default_factory=list)
|
|
133
|
+
warnings: List[str] = field(default_factory=list)
|
|
134
|
+
|
|
135
|
+
def handle_error(
|
|
136
|
+
self,
|
|
137
|
+
message: str,
|
|
138
|
+
exception: Optional[Exception] = None,
|
|
139
|
+
category: str = "error",
|
|
140
|
+
) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Handle an error according to the current mode.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
message: Error message
|
|
146
|
+
exception: Optional exception that caused the error
|
|
147
|
+
category: Error category for logging
|
|
148
|
+
"""
|
|
149
|
+
full_message = f"{category}: {message}"
|
|
150
|
+
if exception:
|
|
151
|
+
full_message += f" ({type(exception).__name__}: {exception})"
|
|
152
|
+
|
|
153
|
+
if self.mode == ErrorMode.STRICT:
|
|
154
|
+
if exception:
|
|
155
|
+
raise type(exception)(full_message) from exception
|
|
156
|
+
raise ValueError(full_message)
|
|
157
|
+
|
|
158
|
+
elif self.mode == ErrorMode.LENIENT:
|
|
159
|
+
warnings.warn(full_message)
|
|
160
|
+
logger.warning(full_message)
|
|
161
|
+
self.warnings.append(full_message)
|
|
162
|
+
|
|
163
|
+
elif self.mode == ErrorMode.COLLECT:
|
|
164
|
+
self.errors.append(full_message)
|
|
165
|
+
logger.warning(full_message)
|
|
166
|
+
|
|
167
|
+
def handle_warning(self, message: str, category: str = "warning") -> None:
|
|
168
|
+
"""
|
|
169
|
+
Handle a warning (non-fatal issue).
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
message: Warning message
|
|
173
|
+
category: Warning category
|
|
174
|
+
"""
|
|
175
|
+
full_message = f"{category}: {message}"
|
|
176
|
+
warnings.warn(full_message)
|
|
177
|
+
logger.warning(full_message)
|
|
178
|
+
self.warnings.append(full_message)
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def has_errors(self) -> bool:
|
|
182
|
+
"""Whether any errors were collected."""
|
|
183
|
+
return len(self.errors) > 0
|
|
184
|
+
|
|
185
|
+
def raise_if_errors(self) -> None:
|
|
186
|
+
"""Raise ValueError if any errors were collected."""
|
|
187
|
+
if self.errors:
|
|
188
|
+
raise ValueError(f"Errors occurred: {'; '.join(self.errors)}")
|
|
189
|
+
|
|
190
|
+
def clear(self) -> None:
|
|
191
|
+
"""Clear all errors and warnings."""
|
|
192
|
+
self.errors.clear()
|
|
193
|
+
self.warnings.clear()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# Default error context (lenient by default)
|
|
197
|
+
_default_context = ErrorContext(mode=ErrorMode.LENIENT)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_error_context() -> ErrorContext:
|
|
201
|
+
"""Get the default error context."""
|
|
202
|
+
return _default_context
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def set_error_mode(mode: ErrorMode) -> None:
|
|
206
|
+
"""
|
|
207
|
+
Set the default error handling mode.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
mode: Error mode to use
|
|
211
|
+
"""
|
|
212
|
+
_default_context.mode = mode
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def handle_error(
|
|
216
|
+
message: str,
|
|
217
|
+
exception: Optional[Exception] = None,
|
|
218
|
+
category: str = "error",
|
|
219
|
+
context: Optional[ErrorContext] = None,
|
|
220
|
+
) -> None:
|
|
221
|
+
"""
|
|
222
|
+
Handle an error using the specified or default context.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
message: Error message
|
|
226
|
+
exception: Optional exception that caused the error
|
|
227
|
+
category: Error category
|
|
228
|
+
context: Optional error context (uses default if not provided)
|
|
229
|
+
"""
|
|
230
|
+
ctx = context or _default_context
|
|
231
|
+
ctx.handle_error(message, exception, category)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def handle_warning(
|
|
235
|
+
message: str,
|
|
236
|
+
category: str = "warning",
|
|
237
|
+
context: Optional[ErrorContext] = None,
|
|
238
|
+
) -> None:
|
|
239
|
+
"""
|
|
240
|
+
Handle a warning using the specified or default context.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
message: Warning message
|
|
244
|
+
category: Warning category
|
|
245
|
+
context: Optional error context (uses default if not provided)
|
|
246
|
+
"""
|
|
247
|
+
ctx = context or _default_context
|
|
248
|
+
ctx.handle_warning(message, category)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class StrictMode:
|
|
252
|
+
"""
|
|
253
|
+
Context manager for temporarily enabling strict mode.
|
|
254
|
+
|
|
255
|
+
Example:
|
|
256
|
+
>>> with StrictMode():
|
|
257
|
+
... # Errors will raise exceptions
|
|
258
|
+
... validator.validate(data)
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
def __init__(self):
|
|
262
|
+
self._previous_mode: Optional[ErrorMode] = None
|
|
263
|
+
|
|
264
|
+
def __enter__(self):
|
|
265
|
+
self._previous_mode = _default_context.mode
|
|
266
|
+
_default_context.mode = ErrorMode.STRICT
|
|
267
|
+
return self
|
|
268
|
+
|
|
269
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
270
|
+
if self._previous_mode is not None:
|
|
271
|
+
_default_context.mode = self._previous_mode
|
|
272
|
+
return False
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class LenientMode:
|
|
276
|
+
"""
|
|
277
|
+
Context manager for temporarily enabling lenient mode.
|
|
278
|
+
|
|
279
|
+
Example:
|
|
280
|
+
>>> with LenientMode():
|
|
281
|
+
... # Errors will be logged as warnings
|
|
282
|
+
... validator.validate(data)
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
def __init__(self):
|
|
286
|
+
self._previous_mode: Optional[ErrorMode] = None
|
|
287
|
+
|
|
288
|
+
def __enter__(self):
|
|
289
|
+
self._previous_mode = _default_context.mode
|
|
290
|
+
_default_context.mode = ErrorMode.LENIENT
|
|
291
|
+
return self
|
|
292
|
+
|
|
293
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
294
|
+
if self._previous_mode is not None:
|
|
295
|
+
_default_context.mode = self._previous_mode
|
|
296
|
+
return False
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Protocol definitions for pycharter interfaces.
|
|
3
|
+
|
|
4
|
+
These protocols define the expected interfaces for extensible components,
|
|
5
|
+
enabling type checking and clear contracts for custom implementations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Callable, Dict, List, Optional, Protocol, Type, runtime_checkable
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, ValidationInfo
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# =============================================================================
|
|
14
|
+
# Metadata Store Protocols
|
|
15
|
+
# =============================================================================
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class MetadataStore(Protocol):
|
|
19
|
+
"""
|
|
20
|
+
Protocol for metadata store implementations.
|
|
21
|
+
|
|
22
|
+
All metadata stores (SQLite, Postgres, MongoDB, Redis, InMemory) must
|
|
23
|
+
implement this interface for storing and retrieving data contracts.
|
|
24
|
+
|
|
25
|
+
Example implementation:
|
|
26
|
+
>>> class MyCustomStore:
|
|
27
|
+
... def get_schema(self, schema_id: str, version: str = None) -> dict | None:
|
|
28
|
+
... # Custom implementation
|
|
29
|
+
... return {"type": "object", "properties": {...}}
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def get_schema(
|
|
33
|
+
self, schema_id: str, version: Optional[str] = None
|
|
34
|
+
) -> Optional[Dict[str, Any]]:
|
|
35
|
+
"""Get a schema by ID and optional version."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
def get_complete_schema(
|
|
39
|
+
self, schema_id: str, version: Optional[str] = None
|
|
40
|
+
) -> Optional[Dict[str, Any]]:
|
|
41
|
+
"""Get a complete schema with coercion and validation rules merged."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
def store_schema(
|
|
45
|
+
self,
|
|
46
|
+
schema_id: str,
|
|
47
|
+
schema: Dict[str, Any],
|
|
48
|
+
version: Optional[str] = None,
|
|
49
|
+
) -> str:
|
|
50
|
+
"""Store a schema and return its ID."""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
def list_schemas(self) -> List[Dict[str, Any]]:
|
|
54
|
+
"""List all available schemas."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# =============================================================================
|
|
59
|
+
# Coercion Protocols
|
|
60
|
+
# =============================================================================
|
|
61
|
+
|
|
62
|
+
# Type alias for coercion functions
|
|
63
|
+
CoercionFunc = Callable[[Any], Any]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@runtime_checkable
|
|
67
|
+
class CoercionRegistry(Protocol):
|
|
68
|
+
"""
|
|
69
|
+
Protocol for coercion function registries.
|
|
70
|
+
|
|
71
|
+
Coercions are pre-validation transformations applied to data
|
|
72
|
+
before Pydantic validation (mode='before').
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def get(self, name: str) -> CoercionFunc:
|
|
76
|
+
"""Get a coercion function by name."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
def register(self, name: str, func: CoercionFunc) -> None:
|
|
80
|
+
"""Register a custom coercion function."""
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
def list_available(self) -> List[str]:
|
|
84
|
+
"""List all available coercion names."""
|
|
85
|
+
...
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# =============================================================================
|
|
89
|
+
# Validation Protocols
|
|
90
|
+
# =============================================================================
|
|
91
|
+
|
|
92
|
+
# Type alias for validation functions (factory pattern)
|
|
93
|
+
# A validation factory returns a validator function that takes (value, info) -> value
|
|
94
|
+
ValidatorFunc = Callable[[Any, ValidationInfo], Any]
|
|
95
|
+
ValidationFactory = Callable[..., ValidatorFunc]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@runtime_checkable
|
|
99
|
+
class ValidationRegistry(Protocol):
|
|
100
|
+
"""
|
|
101
|
+
Protocol for validation function registries.
|
|
102
|
+
|
|
103
|
+
Validations are post-validation checks applied to data
|
|
104
|
+
after Pydantic validation (mode='after').
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def get(self, name: str) -> ValidationFactory:
|
|
108
|
+
"""Get a validation factory by name."""
|
|
109
|
+
...
|
|
110
|
+
|
|
111
|
+
def register(self, name: str, factory: ValidationFactory) -> None:
|
|
112
|
+
"""Register a custom validation factory."""
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
def list_available(self) -> List[str]:
|
|
116
|
+
"""List all available validation names."""
|
|
117
|
+
...
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# =============================================================================
|
|
121
|
+
# Contract Parser Protocols
|
|
122
|
+
# =============================================================================
|
|
123
|
+
|
|
124
|
+
@runtime_checkable
|
|
125
|
+
class ContractParser(Protocol):
|
|
126
|
+
"""
|
|
127
|
+
Protocol for contract parsers.
|
|
128
|
+
|
|
129
|
+
Contract parsers decompose data contract files/dicts into
|
|
130
|
+
their constituent components (schema, coercion_rules, validation_rules, metadata).
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def parse(self, contract_data: Dict[str, Any]) -> "ContractMetadataProtocol":
|
|
134
|
+
"""Parse contract data into metadata components."""
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
def parse_file(self, file_path: str) -> "ContractMetadataProtocol":
|
|
138
|
+
"""Parse contract from file."""
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@runtime_checkable
|
|
143
|
+
class ContractMetadataProtocol(Protocol):
|
|
144
|
+
"""Protocol for contract metadata containers."""
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def schema(self) -> Dict[str, Any]:
|
|
148
|
+
"""Get the JSON Schema definition."""
|
|
149
|
+
...
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def coercion_rules(self) -> Dict[str, Any]:
|
|
153
|
+
"""Get coercion rules."""
|
|
154
|
+
...
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def validation_rules(self) -> Dict[str, Any]:
|
|
158
|
+
"""Get validation rules."""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def metadata(self) -> Dict[str, Any]:
|
|
163
|
+
"""Get additional metadata."""
|
|
164
|
+
...
|
|
165
|
+
|
|
166
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
167
|
+
"""Convert to dictionary."""
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# =============================================================================
|
|
172
|
+
# Model Generator Protocols
|
|
173
|
+
# =============================================================================
|
|
174
|
+
|
|
175
|
+
@runtime_checkable
|
|
176
|
+
class ModelGenerator(Protocol):
|
|
177
|
+
"""
|
|
178
|
+
Protocol for Pydantic model generators.
|
|
179
|
+
|
|
180
|
+
Model generators take JSON Schemas and produce Pydantic model classes.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
def generate(
|
|
184
|
+
self, schema: Dict[str, Any], model_name: str = "DynamicModel"
|
|
185
|
+
) -> Type[BaseModel]:
|
|
186
|
+
"""Generate a Pydantic model from JSON Schema."""
|
|
187
|
+
...
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# =============================================================================
|
|
191
|
+
# Validator Protocols
|
|
192
|
+
# =============================================================================
|
|
193
|
+
|
|
194
|
+
@runtime_checkable
|
|
195
|
+
class DataValidator(Protocol):
|
|
196
|
+
"""
|
|
197
|
+
Protocol for data validators.
|
|
198
|
+
|
|
199
|
+
Validators validate data against contracts/schemas and return results.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
def validate(self, data: Dict[str, Any]) -> "ValidationResultProtocol":
|
|
203
|
+
"""Validate a single data record."""
|
|
204
|
+
...
|
|
205
|
+
|
|
206
|
+
def validate_batch(
|
|
207
|
+
self, data_list: List[Dict[str, Any]]
|
|
208
|
+
) -> List["ValidationResultProtocol"]:
|
|
209
|
+
"""Validate a batch of data records."""
|
|
210
|
+
...
|
|
211
|
+
|
|
212
|
+
def get_model(self) -> Type[BaseModel]:
|
|
213
|
+
"""Get the underlying Pydantic model."""
|
|
214
|
+
...
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@runtime_checkable
|
|
218
|
+
class ValidationResultProtocol(Protocol):
|
|
219
|
+
"""Protocol for validation results."""
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def is_valid(self) -> bool:
|
|
223
|
+
"""Whether validation passed."""
|
|
224
|
+
...
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def data(self) -> Optional[Any]:
|
|
228
|
+
"""Validated/coerced data (if valid)."""
|
|
229
|
+
...
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def errors(self) -> List[Dict[str, Any]]:
|
|
233
|
+
"""Validation errors (if any)."""
|
|
234
|
+
...
|