pycharter 0.0.23__py3-none-any.whl → 0.0.25__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.
- pycharter/__init__.py +268 -58
- pycharter/api/README.md +340 -0
- {api → pycharter/api}/__init__.py +1 -1
- {api → pycharter/api}/dependencies/__init__.py +2 -2
- {api → pycharter/api}/main.py +28 -2
- {api → pycharter/api}/models/__init__.py +4 -4
- pycharter/api/models/docs.py +68 -0
- pycharter/api/models/evolution.py +117 -0
- pycharter/api/models/tracking.py +111 -0
- {api → pycharter/api}/models/validation.py +46 -6
- {api → pycharter/api}/routes/v1/__init__.py +14 -1
- {api → pycharter/api}/routes/v1/contracts.py +4 -4
- pycharter/api/routes/v1/docs.py +187 -0
- pycharter/api/routes/v1/evolution.py +337 -0
- {api → pycharter/api}/routes/v1/metadata.py +5 -5
- {api → pycharter/api}/routes/v1/quality.py +3 -3
- {api → pycharter/api}/routes/v1/schemas.py +1 -1
- {api → pycharter/api}/routes/v1/settings.py +1 -1
- {api → pycharter/api}/routes/v1/templates.py +168 -3
- pycharter/api/routes/v1/tracking.py +301 -0
- {api → pycharter/api}/routes/v1/validation.py +70 -33
- {api → pycharter/api}/routes/v1/validation_jobs.py +3 -3
- pycharter/cli.py +8 -10
- pycharter/data/templates/contract/README.md +144 -0
- pycharter/data/templates/contract/template_coercion_rules.yaml +54 -12
- pycharter/data/templates/contract/template_contract.yaml +87 -552
- pycharter/data/templates/contract/template_metadata.yaml +51 -21
- pycharter/data/templates/contract/template_schema.yaml +83 -5
- pycharter/data/templates/contract/template_validation_rules.yaml +68 -22
- pycharter/data/templates/etl/README.md +197 -64
- pycharter/data/templates/etl/extract_cloud_azure.yaml +14 -13
- pycharter/data/templates/etl/extract_cloud_gcs.yaml +17 -14
- pycharter/data/templates/etl/extract_cloud_s3.yaml +21 -15
- pycharter/data/templates/etl/extract_database.yaml +24 -18
- pycharter/data/templates/etl/extract_database_ssh.yaml +36 -23
- pycharter/data/templates/etl/extract_file_csv.yaml +15 -11
- pycharter/data/templates/etl/extract_file_glob.yaml +20 -12
- pycharter/data/templates/etl/extract_file_json.yaml +18 -8
- pycharter/data/templates/etl/extract_file_parquet.yaml +15 -8
- pycharter/data/templates/etl/extract_http_paginated.yaml +47 -43
- pycharter/data/templates/etl/extract_http_path_params.yaml +20 -27
- pycharter/data/templates/etl/extract_http_simple.yaml +30 -20
- 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 +11 -10
- pycharter/data/templates/etl/load_postgresql.yaml +31 -9
- pycharter/data/templates/etl/load_sqlite.yaml +12 -7
- pycharter/data/templates/etl/load_truncate_and_load.yaml +13 -11
- pycharter/data/templates/etl/load_upsert.yaml +17 -20
- pycharter/data/templates/etl/load_with_dlq.yaml +21 -11
- pycharter/data/templates/etl/load_with_ssh_tunnel.yaml +28 -21
- pycharter/data/templates/etl/pipeline_http_to_db.yaml +62 -25
- pycharter/data/templates/etl/transform_combined.yaml +26 -16
- pycharter/data/templates/etl/transform_custom_function.yaml +54 -14
- pycharter/data/templates/etl/transform_jsonata.yaml +44 -13
- pycharter/data/templates/etl/transform_simple.yaml +35 -17
- pycharter/db/README.md +179 -0
- pycharter/db/schemas/README.md +96 -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/ASYNC_AND_EXECUTION.md +91 -0
- pycharter/etl_generator/INTERFACES.md +143 -0
- pycharter/etl_generator/README.md +271 -0
- pycharter/etl_generator/TRANSFORMATION_GUIDE.md +452 -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 +4 -0
- pycharter/etl_generator/extractors/cloud_storage.py +77 -1
- pycharter/etl_generator/extractors/database.py +71 -1
- pycharter/etl_generator/extractors/factory.py +112 -68
- pycharter/etl_generator/extractors/file.py +58 -1
- pycharter/etl_generator/extractors/http.py +80 -1
- 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/schemas/extract.json +234 -0
- pycharter/etl_generator/schemas/load.json +202 -0
- pycharter/etl_generator/schemas/pipeline.json +94 -0
- pycharter/etl_generator/schemas/transform.json +171 -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/metadata_store/README.md +229 -0
- pycharter/quality/README.md +235 -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/ui/.eslintrc.json +4 -0
- pycharter/ui/README.md +186 -0
- {ui → pycharter/ui}/__init__.py +3 -3
- pycharter/ui/components.json +17 -0
- pycharter/ui/package-lock.json +6608 -0
- pycharter/ui/package.json +36 -0
- {ui → pycharter/ui}/server.py +7 -8
- pycharter/ui/static/_next/static/2gKjNv6YvE6BcIdFthBLs/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/_next/static/0rYA78L88aUyD2Uh38hhX/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/tsconfig.json +42 -0
- pycharter/worker/README.md +187 -0
- pycharter/worker/backends/__init__.py +8 -0
- pycharter/worker/backends/base.py +46 -0
- pycharter/worker/backends/spark.py +233 -0
- {worker → pycharter/worker}/cli.py +1 -1
- {worker → pycharter/worker}/processor.py +2 -2
- pycharter/worker/queue/__init__.py +8 -0
- pycharter/worker/queue/redis_queue.py +147 -0
- {pycharter-0.0.23.dist-info → pycharter-0.0.25.dist-info}/METADATA +141 -26
- pycharter-0.0.25.dist-info/RECORD +572 -0
- pycharter-0.0.25.dist-info/top_level.txt +1 -0
- pycharter/etl_generator/extraction.py +0 -701
- 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.23.dist-info/RECORD +0 -498
- pycharter-0.0.23.dist-info/top_level.txt +0 -4
- {api → pycharter/api}/dependencies/database.py +0 -0
- {api → pycharter/api}/dependencies/store.py +0 -0
- {api → pycharter/api}/models/contracts.py +0 -0
- {api → pycharter/api}/models/metadata.py +0 -0
- {api → pycharter/api}/models/metadata_entities.py +0 -0
- {api → pycharter/api}/models/quality.py +0 -0
- {api → pycharter/api}/models/schemas.py +0 -0
- {api → pycharter/api}/routes/__init__.py +0 -0
- {api → pycharter/api}/utils.py +0 -0
- {ui → pycharter/ui}/build.py +0 -0
- {ui → pycharter/ui}/dev.py +0 -0
- {ui → pycharter/ui}/static/.gitkeep +0 -0
- {ui → pycharter/ui}/static/404/index.html +0 -0
- {ui → pycharter/ui}/static/404.html +0 -0
- {ui → pycharter/ui}/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/_next/static/2gKjNv6YvE6BcIdFthBLs/_buildManifest.js +0 -0
- {ui → pycharter/ui}/static/_next/static/2gKjNv6YvE6BcIdFthBLs/_ssgManifest.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/26dfc590f7714c03.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/34d289e6db2ef551.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/99508d9d5869cc27.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/b313c35a6ba76574.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui}/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui}/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/_not-found/index.html +0 -0
- {ui → pycharter/ui}/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui}/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui}/static/contracts/index.html +0 -0
- {ui → pycharter/ui}/static/contracts/index.txt +0 -0
- {ui → pycharter/ui}/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui}/static/documentation/index.html +0 -0
- {ui → pycharter/ui}/static/documentation/index.txt +0 -0
- {ui → pycharter/ui}/static/index.html +0 -0
- {ui → pycharter/ui}/static/index.txt +0 -0
- {ui → pycharter/ui}/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui}/static/metadata/index.html +0 -0
- {ui → pycharter/ui}/static/metadata/index.txt +0 -0
- {ui → pycharter/ui}/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui}/static/quality/index.html +0 -0
- {ui → pycharter/ui}/static/quality/index.txt +0 -0
- {ui → pycharter/ui}/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui}/static/rules/index.html +0 -0
- {ui → pycharter/ui}/static/rules/index.txt +0 -0
- {ui → pycharter/ui}/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui}/static/schemas/index.html +0 -0
- {ui → pycharter/ui}/static/schemas/index.txt +0 -0
- {ui → pycharter/ui}/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui}/static/settings/index.html +0 -0
- {ui → pycharter/ui}/static/settings/index.txt +0 -0
- {ui → pycharter/ui}/static/static/.gitkeep +0 -0
- {ui → pycharter/ui}/static/static/404/index.html +0 -0
- {ui → pycharter/ui}/static/static/404.html +0 -0
- {ui → pycharter/ui}/static/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/_next/static/0rYA78L88aUyD2Uh38hhX/_buildManifest.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/0rYA78L88aUyD2Uh38hhX/_ssgManifest.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/c4fa4f4114b7c352.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui}/static/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui}/static/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/_not-found/index.html +0 -0
- {ui → pycharter/ui}/static/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui}/static/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui}/static/static/contracts/index.html +0 -0
- {ui → pycharter/ui}/static/static/contracts/index.txt +0 -0
- {ui → pycharter/ui}/static/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui}/static/static/documentation/index.html +0 -0
- {ui → pycharter/ui}/static/static/documentation/index.txt +0 -0
- {ui → pycharter/ui}/static/static/index.html +0 -0
- {ui → pycharter/ui}/static/static/index.txt +0 -0
- {ui → pycharter/ui}/static/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui}/static/static/metadata/index.html +0 -0
- {ui → pycharter/ui}/static/static/metadata/index.txt +0 -0
- {ui → pycharter/ui}/static/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui}/static/static/quality/index.html +0 -0
- {ui → pycharter/ui}/static/static/quality/index.txt +0 -0
- {ui → pycharter/ui}/static/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui}/static/static/rules/index.html +0 -0
- {ui → pycharter/ui}/static/static/rules/index.txt +0 -0
- {ui → pycharter/ui}/static/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui}/static/static/schemas/index.html +0 -0
- {ui → pycharter/ui}/static/static/schemas/index.txt +0 -0
- {ui → pycharter/ui}/static/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui}/static/static/settings/index.html +0 -0
- {ui → pycharter/ui}/static/static/settings/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/.gitkeep +0 -0
- {ui → pycharter/ui}/static/static/static/404/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/404.html +0 -0
- {ui → pycharter/ui}/static/static/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/4e310fe5005770a3.css +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/5fc14c00a2779dc5.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/b584574fdc8ab13e.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/d5989c94d3614b3a.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/contracts/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/documentation/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/metadata/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui}/static/static/static/quality/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/quality/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui}/static/static/static/rules/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/rules/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/schemas/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui}/static/static/static/settings/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/settings/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui}/static/static/static/validation/index.html +0 -0
- {ui → pycharter/ui}/static/static/static/validation/index.txt +0 -0
- {ui → pycharter/ui}/static/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui}/static/static/validation/index.html +0 -0
- {ui → pycharter/ui}/static/static/validation/index.txt +0 -0
- {ui → pycharter/ui}/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui}/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui}/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui}/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui}/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui}/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui}/static/validation/index.html +0 -0
- {ui → pycharter/ui}/static/validation/index.txt +0 -0
- {worker → pycharter/worker}/__init__.py +0 -0
- {worker → pycharter/worker}/models.py +0 -0
- {pycharter-0.0.23.dist-info → pycharter-0.0.25.dist-info}/WHEEL +0 -0
- {pycharter-0.0.23.dist-info → pycharter-0.0.25.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.23.dist-info → pycharter-0.0.25.dist-info}/licenses/LICENSE +0 -0
pycharter/__init__.py
CHANGED
|
@@ -1,22 +1,97 @@
|
|
|
1
1
|
"""
|
|
2
|
-
PyCharter - Data Contract Management and Validation
|
|
2
|
+
PyCharter - Data Contract Management, ETL Pipelines, and Validation
|
|
3
3
|
|
|
4
4
|
Core Services:
|
|
5
|
-
1.
|
|
6
|
-
2. Contract
|
|
7
|
-
3.
|
|
8
|
-
4.
|
|
9
|
-
5.
|
|
10
|
-
6.
|
|
11
|
-
7.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
1. ETL Pipelines - Build and run ETL pipelines with | operator
|
|
6
|
+
2. Contract Parser - Reads and decomposes data contract files
|
|
7
|
+
3. Contract Builder - Constructs consolidated contracts from separate artifacts
|
|
8
|
+
4. Metadata Store - Database operations for metadata storage
|
|
9
|
+
5. Pydantic Generator - Generates Pydantic models from JSON Schema
|
|
10
|
+
6. JSON Schema Converter - Converts Pydantic models to JSON Schema
|
|
11
|
+
7. Runtime Validator - Validation utilities
|
|
12
|
+
8. Quality Assurance - Data quality checking and monitoring
|
|
13
|
+
|
|
14
|
+
ETL API:
|
|
15
|
+
>>> from pycharter import Pipeline, HTTPExtractor, PostgresLoader, Rename, AddField
|
|
16
|
+
>>>
|
|
17
|
+
>>> # Programmatic pipeline with | operator
|
|
18
|
+
>>> pipeline = (
|
|
19
|
+
... Pipeline(HTTPExtractor(url="https://api.example.com/data"))
|
|
20
|
+
... | Rename({"old": "new"})
|
|
21
|
+
... | AddField("processed_at", "now()")
|
|
22
|
+
... | PostgresLoader(connection_string="...", table="users")
|
|
23
|
+
... )
|
|
24
|
+
>>> result = await pipeline.run() # run() is async; use asyncio.run() from scripts
|
|
25
|
+
>>>
|
|
26
|
+
>>> # Config-driven (explicit files)
|
|
27
|
+
>>> pipeline = Pipeline.from_config_files(
|
|
28
|
+
... extract="configs/extract.yaml",
|
|
29
|
+
... load="configs/load.yaml",
|
|
30
|
+
... variables={"API_KEY": "secret"}
|
|
31
|
+
... )
|
|
32
|
+
>>>
|
|
33
|
+
>>> # Config-driven (directory with extract.yaml, transform.yaml, load.yaml)
|
|
34
|
+
>>> pipeline = Pipeline.from_config_dir("pipelines/users/")
|
|
35
|
+
>>>
|
|
36
|
+
>>> # Config-driven (single file)
|
|
37
|
+
>>> pipeline = Pipeline.from_config_file("pipelines/users/pipeline.yaml")
|
|
38
|
+
|
|
39
|
+
Validator API:
|
|
40
|
+
>>> from pycharter import Validator
|
|
41
|
+
>>>
|
|
42
|
+
>>> # From explicit files
|
|
43
|
+
>>> validator = Validator.from_files(schema="schema.yaml")
|
|
44
|
+
>>>
|
|
45
|
+
>>> # From directory
|
|
46
|
+
>>> validator = Validator.from_dir("contracts/users/")
|
|
47
|
+
>>>
|
|
48
|
+
>>> result = validator.validate({"name": "Alice", "age": 30})
|
|
17
49
|
"""
|
|
18
50
|
|
|
19
|
-
__version__ = "0.0
|
|
51
|
+
__version__ = "0.1.0"
|
|
52
|
+
|
|
53
|
+
# ============================================================================
|
|
54
|
+
# ETL PIPELINES
|
|
55
|
+
# ============================================================================
|
|
56
|
+
|
|
57
|
+
from pycharter.etl_generator import (
|
|
58
|
+
# Core
|
|
59
|
+
Pipeline,
|
|
60
|
+
PipelineBuilder,
|
|
61
|
+
PipelineContext,
|
|
62
|
+
PipelineResult,
|
|
63
|
+
BatchResult,
|
|
64
|
+
LoadResult,
|
|
65
|
+
# Protocols
|
|
66
|
+
Extractor,
|
|
67
|
+
Transformer,
|
|
68
|
+
Loader,
|
|
69
|
+
# Extractors
|
|
70
|
+
BaseExtractor,
|
|
71
|
+
HTTPExtractor,
|
|
72
|
+
FileExtractor,
|
|
73
|
+
DatabaseExtractor,
|
|
74
|
+
CloudStorageExtractor,
|
|
75
|
+
# Transformers
|
|
76
|
+
BaseTransformer,
|
|
77
|
+
TransformerChain,
|
|
78
|
+
Rename,
|
|
79
|
+
AddField,
|
|
80
|
+
Drop,
|
|
81
|
+
Select,
|
|
82
|
+
Filter,
|
|
83
|
+
Convert,
|
|
84
|
+
Default,
|
|
85
|
+
Map,
|
|
86
|
+
FlatMap,
|
|
87
|
+
CustomFunction,
|
|
88
|
+
# Loaders
|
|
89
|
+
BaseLoader,
|
|
90
|
+
PostgresLoader,
|
|
91
|
+
DatabaseLoader,
|
|
92
|
+
FileLoader,
|
|
93
|
+
CloudStorageLoader,
|
|
94
|
+
)
|
|
20
95
|
|
|
21
96
|
# ============================================================================
|
|
22
97
|
# TIER 1: PRIMARY INTERFACES (Classes - Use these for best performance)
|
|
@@ -25,7 +100,9 @@ __version__ = "0.0.22"
|
|
|
25
100
|
# Runtime Validator (PRIMARY INTERFACE)
|
|
26
101
|
from pycharter.runtime_validator import (
|
|
27
102
|
Validator, # ⭐ PRIMARY: Use this for validation
|
|
103
|
+
ValidatorBuilder, # Fluent API for building validators
|
|
28
104
|
create_validator,
|
|
105
|
+
QualityMetrics as ValidationQualityMetrics, # Quality metrics from validation
|
|
29
106
|
)
|
|
30
107
|
|
|
31
108
|
# Quality Assurance (PRIMARY INTERFACE)
|
|
@@ -140,22 +217,145 @@ from pycharter.quality import (
|
|
|
140
217
|
ViolationTracker,
|
|
141
218
|
ViolationRecord,
|
|
142
219
|
DataProfiler,
|
|
220
|
+
# Tracking submodule
|
|
221
|
+
MetricsCollector,
|
|
222
|
+
ValidationMetric,
|
|
223
|
+
MetricsSummary,
|
|
224
|
+
InMemoryMetricsStore,
|
|
225
|
+
SQLiteMetricsStore,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# ============================================================================
|
|
229
|
+
# DOCUMENTATION GENERATION
|
|
230
|
+
# ============================================================================
|
|
231
|
+
|
|
232
|
+
from pycharter.docs_generator import (
|
|
233
|
+
DocsGenerator,
|
|
234
|
+
generate_docs,
|
|
235
|
+
MarkdownRenderer,
|
|
236
|
+
HTMLRenderer,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# ============================================================================
|
|
240
|
+
# SCHEMA EVOLUTION
|
|
241
|
+
# ============================================================================
|
|
242
|
+
|
|
243
|
+
from pycharter.schema_evolution import (
|
|
244
|
+
check_compatibility,
|
|
245
|
+
compute_diff,
|
|
246
|
+
CompatibilityResult,
|
|
247
|
+
SchemaDiff,
|
|
248
|
+
SchemaChange,
|
|
249
|
+
ChangeType,
|
|
250
|
+
CompatibilityMode,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# ============================================================================
|
|
254
|
+
# PROTOCOLS AND ERROR HANDLING
|
|
255
|
+
# ============================================================================
|
|
256
|
+
|
|
257
|
+
from pycharter.shared import (
|
|
258
|
+
# Protocols for extensibility
|
|
259
|
+
MetadataStore,
|
|
260
|
+
CoercionRegistry,
|
|
261
|
+
ValidationRegistry,
|
|
262
|
+
DataValidator,
|
|
263
|
+
# Exception hierarchy (catch PyCharterError for any pycharter failure)
|
|
264
|
+
PyCharterError,
|
|
265
|
+
ConfigError,
|
|
266
|
+
ConfigValidationError,
|
|
267
|
+
ConfigLoadError,
|
|
268
|
+
ExpressionError,
|
|
269
|
+
# Error handling
|
|
270
|
+
ErrorMode,
|
|
271
|
+
ErrorContext,
|
|
272
|
+
StrictMode,
|
|
273
|
+
LenientMode,
|
|
274
|
+
set_error_mode,
|
|
143
275
|
)
|
|
144
276
|
|
|
145
277
|
__all__ = [
|
|
146
278
|
# ========================================================================
|
|
147
|
-
#
|
|
279
|
+
# ETL PIPELINES
|
|
280
|
+
# ========================================================================
|
|
281
|
+
# Core
|
|
282
|
+
"Pipeline",
|
|
283
|
+
"PipelineBuilder",
|
|
284
|
+
"PipelineContext",
|
|
285
|
+
"PipelineResult",
|
|
286
|
+
"BatchResult",
|
|
287
|
+
"LoadResult",
|
|
288
|
+
# Protocols
|
|
289
|
+
"Extractor",
|
|
290
|
+
"Transformer",
|
|
291
|
+
"Loader",
|
|
292
|
+
# Extractors
|
|
293
|
+
"BaseExtractor",
|
|
294
|
+
"HTTPExtractor",
|
|
295
|
+
"FileExtractor",
|
|
296
|
+
"DatabaseExtractor",
|
|
297
|
+
"CloudStorageExtractor",
|
|
298
|
+
# Transformers
|
|
299
|
+
"BaseTransformer",
|
|
300
|
+
"TransformerChain",
|
|
301
|
+
"Rename",
|
|
302
|
+
"AddField",
|
|
303
|
+
"Drop",
|
|
304
|
+
"Select",
|
|
305
|
+
"Filter",
|
|
306
|
+
"Convert",
|
|
307
|
+
"Default",
|
|
308
|
+
"Map",
|
|
309
|
+
"FlatMap",
|
|
310
|
+
"CustomFunction",
|
|
311
|
+
# Loaders
|
|
312
|
+
"BaseLoader",
|
|
313
|
+
"PostgresLoader",
|
|
314
|
+
"DatabaseLoader",
|
|
315
|
+
"FileLoader",
|
|
316
|
+
"CloudStorageLoader",
|
|
317
|
+
# Exceptions and error handling
|
|
318
|
+
"PyCharterError",
|
|
319
|
+
"ConfigError",
|
|
320
|
+
"ConfigValidationError",
|
|
321
|
+
"ConfigLoadError",
|
|
322
|
+
"ExpressionError",
|
|
323
|
+
"ErrorMode",
|
|
324
|
+
"ErrorContext",
|
|
325
|
+
"StrictMode",
|
|
326
|
+
"LenientMode",
|
|
327
|
+
"set_error_mode",
|
|
148
328
|
# ========================================================================
|
|
149
|
-
|
|
329
|
+
# DATA CONTRACT SERVICES
|
|
330
|
+
# ========================================================================
|
|
331
|
+
# Validation
|
|
332
|
+
"Validator",
|
|
333
|
+
"ValidatorBuilder",
|
|
150
334
|
"create_validator",
|
|
151
|
-
"
|
|
335
|
+
"ValidationResult",
|
|
336
|
+
"ValidationQualityMetrics",
|
|
337
|
+
"validate_with_store",
|
|
338
|
+
"validate_batch_with_store",
|
|
339
|
+
"validate_with_contract",
|
|
340
|
+
"validate_batch_with_contract",
|
|
341
|
+
"get_model_from_store",
|
|
342
|
+
"get_model_from_contract",
|
|
343
|
+
"validate",
|
|
344
|
+
"validate_batch",
|
|
345
|
+
"validate_input",
|
|
346
|
+
"validate_output",
|
|
347
|
+
"validate_with_contract_decorator",
|
|
348
|
+
# Quality
|
|
349
|
+
"QualityCheck",
|
|
152
350
|
"QualityCheckOptions",
|
|
153
351
|
"QualityReport",
|
|
154
352
|
"QualityThresholds",
|
|
155
|
-
"
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
353
|
+
"QualityMetrics",
|
|
354
|
+
"QualityScore",
|
|
355
|
+
"FieldQualityMetrics",
|
|
356
|
+
"ViolationTracker",
|
|
357
|
+
"ViolationRecord",
|
|
358
|
+
"DataProfiler",
|
|
159
359
|
# Contract Management
|
|
160
360
|
"parse_contract",
|
|
161
361
|
"parse_contract_file",
|
|
@@ -163,49 +363,59 @@ __all__ = [
|
|
|
163
363
|
"build_contract",
|
|
164
364
|
"build_contract_from_store",
|
|
165
365
|
"ContractArtifacts",
|
|
166
|
-
# Pydantic Generator
|
|
167
|
-
"from_dict",
|
|
168
|
-
"from_file",
|
|
169
|
-
"from_json",
|
|
170
|
-
"from_url",
|
|
171
|
-
"generate_model",
|
|
366
|
+
# Pydantic Generator
|
|
367
|
+
"from_dict",
|
|
368
|
+
"from_file",
|
|
369
|
+
"from_json",
|
|
370
|
+
"from_url",
|
|
371
|
+
"generate_model",
|
|
172
372
|
"generate_model_file",
|
|
173
|
-
# JSON Schema Converter
|
|
174
|
-
"to_dict",
|
|
175
|
-
"to_file",
|
|
176
|
-
"to_json",
|
|
177
|
-
"model_to_schema",
|
|
178
|
-
#
|
|
179
|
-
"
|
|
180
|
-
"validate_with_store", # Quick: store → validate
|
|
181
|
-
"validate_batch_with_store", # Quick: batch validate with store
|
|
182
|
-
"validate_with_contract", # Quick: contract → validate
|
|
183
|
-
"validate_batch_with_contract", # Quick: batch validate with contract
|
|
184
|
-
"get_model_from_store", # Quick: store → model
|
|
185
|
-
"get_model_from_contract", # Quick: contract → model
|
|
186
|
-
"validate_input", # Decorator
|
|
187
|
-
"validate_output", # Decorator
|
|
188
|
-
"validate_with_contract_decorator",
|
|
189
|
-
# ========================================================================
|
|
190
|
-
# TIER 3: LOW-LEVEL UTILITIES
|
|
191
|
-
# ========================================================================
|
|
192
|
-
"validate", # Low-level: model → validate
|
|
193
|
-
"validate_batch", # Low-level: model → batch validate
|
|
194
|
-
# ========================================================================
|
|
195
|
-
# METADATA STORE IMPLEMENTATIONS
|
|
196
|
-
# ========================================================================
|
|
373
|
+
# JSON Schema Converter
|
|
374
|
+
"to_dict",
|
|
375
|
+
"to_file",
|
|
376
|
+
"to_json",
|
|
377
|
+
"model_to_schema",
|
|
378
|
+
# Metadata Store
|
|
379
|
+
"MetadataStoreClient",
|
|
197
380
|
"InMemoryMetadataStore",
|
|
198
381
|
"MongoDBMetadataStore",
|
|
199
382
|
"PostgresMetadataStore",
|
|
200
383
|
"RedisMetadataStore",
|
|
201
384
|
"SQLiteMetadataStore",
|
|
385
|
+
# Protocols
|
|
386
|
+
"MetadataStore",
|
|
387
|
+
"CoercionRegistry",
|
|
388
|
+
"ValidationRegistry",
|
|
389
|
+
"DataValidator",
|
|
390
|
+
# Error handling
|
|
391
|
+
"ErrorMode",
|
|
392
|
+
"ErrorContext",
|
|
393
|
+
"StrictMode",
|
|
394
|
+
"LenientMode",
|
|
395
|
+
"set_error_mode",
|
|
202
396
|
# ========================================================================
|
|
203
|
-
# QUALITY
|
|
397
|
+
# QUALITY TRACKING
|
|
204
398
|
# ========================================================================
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
"
|
|
210
|
-
|
|
399
|
+
"MetricsCollector",
|
|
400
|
+
"ValidationMetric",
|
|
401
|
+
"MetricsSummary",
|
|
402
|
+
"InMemoryMetricsStore",
|
|
403
|
+
"SQLiteMetricsStore",
|
|
404
|
+
# ========================================================================
|
|
405
|
+
# DOCUMENTATION GENERATION
|
|
406
|
+
# ========================================================================
|
|
407
|
+
"DocsGenerator",
|
|
408
|
+
"generate_docs",
|
|
409
|
+
"MarkdownRenderer",
|
|
410
|
+
"HTMLRenderer",
|
|
411
|
+
# ========================================================================
|
|
412
|
+
# SCHEMA EVOLUTION
|
|
413
|
+
# ========================================================================
|
|
414
|
+
"check_compatibility",
|
|
415
|
+
"compute_diff",
|
|
416
|
+
"CompatibilityResult",
|
|
417
|
+
"SchemaDiff",
|
|
418
|
+
"SchemaChange",
|
|
419
|
+
"ChangeType",
|
|
420
|
+
"CompatibilityMode",
|
|
211
421
|
]
|
pycharter/api/README.md
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# PyCharter API
|
|
2
|
+
|
|
3
|
+
REST API wrapper for PyCharter services using FastAPI.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The PyCharter API is located at the root level of the repository (`api/`) and provides HTTP endpoints for all core PyCharter services:
|
|
8
|
+
- **Contract Parsing**: Parse data contract files into components
|
|
9
|
+
- **Contract Building**: Reconstruct contracts from metadata store
|
|
10
|
+
- **Metadata Storage**: Store and retrieve schemas, metadata, and rules
|
|
11
|
+
- **Schema Generation**: Generate Pydantic models from JSON Schemas
|
|
12
|
+
- **Schema Conversion**: Convert Pydantic models to JSON Schemas
|
|
13
|
+
- **Runtime Validation**: Validate data against schemas
|
|
14
|
+
- **Quality Assurance**: Run quality checks, query metrics and violations
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install PyCharter with API dependencies:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install pycharter[api]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Running the API Server
|
|
25
|
+
|
|
26
|
+
### Using the CLI command:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pycharter api
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Using uvicorn directly:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uvicorn api.main:app --reload
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Using Python:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from api.main import main
|
|
42
|
+
main()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
The API requires a database connection for most endpoints. Configure it using environment variables:
|
|
48
|
+
|
|
49
|
+
### Database URL Configuration
|
|
50
|
+
|
|
51
|
+
The API uses `PYCHARTER_DATABASE_URL` environment variable to connect to the database. You can set it in several ways:
|
|
52
|
+
|
|
53
|
+
#### Method 1: Export in Shell (Recommended)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Set the environment variable
|
|
57
|
+
export PYCHARTER_DATABASE_URL="postgresql://user:password@localhost:5432/pycharter"
|
|
58
|
+
|
|
59
|
+
# Then run the API
|
|
60
|
+
pycharter api
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### Method 2: Inline with Command
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Set and run in one line
|
|
67
|
+
PYCHARTER_DATABASE_URL="postgresql://user:password@localhost:5432/pycharter" pycharter api
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Method 3: Add to Shell Profile (Persistent)
|
|
71
|
+
|
|
72
|
+
Add to your `~/.bashrc`, `~/.zshrc`, or `~/.profile`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
export PYCHARTER_DATABASE_URL="postgresql://user:password@localhost:5432/pycharter"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then reload your shell:
|
|
79
|
+
```bash
|
|
80
|
+
source ~/.bashrc # or source ~/.zshrc
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Method 4: Using a .env File
|
|
84
|
+
|
|
85
|
+
Create a `.env` file in your project root:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
PYCHARTER_DATABASE_URL=postgresql://user:password@localhost:5432/pycharter
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then load it before running (requires `python-dotenv`):
|
|
92
|
+
```bash
|
|
93
|
+
export $(cat .env | xargs)
|
|
94
|
+
pycharter api
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or use `dotenv-cli`:
|
|
98
|
+
```bash
|
|
99
|
+
pip install dotenv-cli
|
|
100
|
+
dotenv run pycharter api
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Alternative Environment Variable
|
|
104
|
+
|
|
105
|
+
PyCharter also supports Airflow-style configuration:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
export PYCHARTER__DATABASE__SQL_ALCHEMY_CONN="postgresql://user:password@localhost:5432/pycharter"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Metadata Store Configuration
|
|
112
|
+
|
|
113
|
+
The API can be configured to use different metadata store backends:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Metadata store type (in_memory, postgres, mongodb, redis)
|
|
117
|
+
export PYCHARTER_API_STORE_TYPE=postgres
|
|
118
|
+
|
|
119
|
+
# Connection string for database-backed stores
|
|
120
|
+
export PYCHARTER_API_CONNECTION_STRING=postgresql://user:pass@localhost:5432/pycharter
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Note**: If `PYCHARTER_DATABASE_URL` is set, it will be used for both the database connection and metadata store (for PostgreSQL).
|
|
124
|
+
|
|
125
|
+
## API Endpoints
|
|
126
|
+
|
|
127
|
+
### Contract Endpoints
|
|
128
|
+
|
|
129
|
+
#### `POST /api/v1/contracts/parse`
|
|
130
|
+
Parse a data contract dictionary into components.
|
|
131
|
+
|
|
132
|
+
**Request Body:**
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"contract": {
|
|
136
|
+
"schema": {...},
|
|
137
|
+
"metadata": {...},
|
|
138
|
+
"ownership": {...},
|
|
139
|
+
"governance_rules": {...}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Response:**
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"schema": {...},
|
|
148
|
+
"metadata": {...},
|
|
149
|
+
"ownership": {...},
|
|
150
|
+
"governance_rules": {...},
|
|
151
|
+
"coercion_rules": {...},
|
|
152
|
+
"validation_rules": {...},
|
|
153
|
+
"versions": {...}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### `POST /api/v1/contracts/build`
|
|
158
|
+
Build a complete contract from metadata store.
|
|
159
|
+
|
|
160
|
+
**Request Body:**
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"schema_id": "user_schema",
|
|
164
|
+
"version": "1.0.0",
|
|
165
|
+
"include_metadata": true,
|
|
166
|
+
"include_ownership": true,
|
|
167
|
+
"include_governance": true
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `GET /api/v1/contracts`
|
|
172
|
+
List all data contracts stored in the database.
|
|
173
|
+
|
|
174
|
+
#### `GET /api/v1/contracts/{contract_id}`
|
|
175
|
+
Get a specific data contract by ID.
|
|
176
|
+
|
|
177
|
+
### Metadata Endpoints
|
|
178
|
+
|
|
179
|
+
#### `GET /api/v1/metadata/schemas`
|
|
180
|
+
List all schemas stored in the metadata store.
|
|
181
|
+
|
|
182
|
+
#### `GET /api/v1/metadata/schemas/{schema_id}`
|
|
183
|
+
Get a schema by ID (optional version parameter).
|
|
184
|
+
|
|
185
|
+
#### `GET /api/v1/metadata/schemas/{schema_id}/complete`
|
|
186
|
+
Get complete schema with coercion and validation rules merged.
|
|
187
|
+
|
|
188
|
+
#### `POST /api/v1/metadata/schemas`
|
|
189
|
+
Store a schema in the metadata store.
|
|
190
|
+
|
|
191
|
+
#### `GET /api/v1/metadata/metadata/{schema_id}`
|
|
192
|
+
Get metadata for a schema (optional version parameter).
|
|
193
|
+
|
|
194
|
+
#### `POST /api/v1/metadata/metadata`
|
|
195
|
+
Store metadata for a schema.
|
|
196
|
+
|
|
197
|
+
#### `GET /api/v1/metadata/coercion-rules/{schema_id}`
|
|
198
|
+
Get coercion rules for a schema (optional version parameter).
|
|
199
|
+
|
|
200
|
+
#### `POST /api/v1/metadata/coercion-rules`
|
|
201
|
+
Store coercion rules for a schema.
|
|
202
|
+
|
|
203
|
+
#### `GET /api/v1/metadata/validation-rules/{schema_id}`
|
|
204
|
+
Get validation rules for a schema (optional version parameter).
|
|
205
|
+
|
|
206
|
+
#### `POST /api/v1/metadata/validation-rules`
|
|
207
|
+
Store validation rules for a schema.
|
|
208
|
+
|
|
209
|
+
### Quality Assurance Endpoints
|
|
210
|
+
|
|
211
|
+
#### `GET /api/v1/quality/metrics`
|
|
212
|
+
List quality metrics (optional filtering by schema_id, pagination).
|
|
213
|
+
|
|
214
|
+
#### `GET /api/v1/quality/metrics/{metric_id}`
|
|
215
|
+
Get a specific quality metric by ID.
|
|
216
|
+
|
|
217
|
+
#### `GET /api/v1/quality/reports/{schema_id}`
|
|
218
|
+
Get quality reports for a schema/data feed (optional data_source filter).
|
|
219
|
+
|
|
220
|
+
#### `POST /api/v1/quality/check`
|
|
221
|
+
Run a quality check against a data contract.
|
|
222
|
+
|
|
223
|
+
#### `POST /api/v1/quality/violations`
|
|
224
|
+
Query data quality violations.
|
|
225
|
+
|
|
226
|
+
### Schema Endpoints
|
|
227
|
+
|
|
228
|
+
#### `POST /api/v1/schemas/generate`
|
|
229
|
+
Generate Pydantic model from JSON Schema.
|
|
230
|
+
|
|
231
|
+
#### `POST /api/v1/schemas/convert`
|
|
232
|
+
Convert Pydantic model to JSON Schema.
|
|
233
|
+
|
|
234
|
+
### Validation Endpoints
|
|
235
|
+
|
|
236
|
+
#### `POST /api/v1/validation/validate`
|
|
237
|
+
Validate a single record against a schema.
|
|
238
|
+
|
|
239
|
+
#### `POST /api/v1/validation/validate-batch`
|
|
240
|
+
Validate a batch of records against a schema.
|
|
241
|
+
|
|
242
|
+
## API Documentation
|
|
243
|
+
|
|
244
|
+
Once the API server is running, you can access:
|
|
245
|
+
|
|
246
|
+
- **Swagger UI**: http://localhost:8000/docs
|
|
247
|
+
- **ReDoc**: http://localhost:8000/redoc
|
|
248
|
+
- **OpenAPI JSON**: http://localhost:8000/openapi.json
|
|
249
|
+
|
|
250
|
+
## Example Usage
|
|
251
|
+
|
|
252
|
+
### Using curl:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# List all contracts
|
|
256
|
+
curl http://localhost:8000/api/v1/contracts
|
|
257
|
+
|
|
258
|
+
# Get a specific contract
|
|
259
|
+
curl http://localhost:8000/api/v1/contracts/{contract_id}
|
|
260
|
+
|
|
261
|
+
# List all schemas
|
|
262
|
+
curl http://localhost:8000/api/v1/metadata/schemas
|
|
263
|
+
|
|
264
|
+
# Get a schema
|
|
265
|
+
curl http://localhost:8000/api/v1/metadata/schemas/{schema_id}
|
|
266
|
+
|
|
267
|
+
# Get quality metrics
|
|
268
|
+
curl http://localhost:8000/api/v1/quality/metrics
|
|
269
|
+
|
|
270
|
+
# Get quality report for a schema
|
|
271
|
+
curl http://localhost:8000/api/v1/quality/reports/{schema_id}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Using Python requests:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
import requests
|
|
278
|
+
|
|
279
|
+
# List contracts
|
|
280
|
+
response = requests.get("http://localhost:8000/api/v1/contracts")
|
|
281
|
+
contracts = response.json()
|
|
282
|
+
|
|
283
|
+
# Get quality metrics
|
|
284
|
+
response = requests.get("http://localhost:8000/api/v1/quality/metrics")
|
|
285
|
+
metrics = response.json()
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Troubleshooting
|
|
289
|
+
|
|
290
|
+
### Database Connection Issues
|
|
291
|
+
|
|
292
|
+
If you see errors about database URL not being configured:
|
|
293
|
+
|
|
294
|
+
1. **Check environment variable is set:**
|
|
295
|
+
```bash
|
|
296
|
+
echo $PYCHARTER_DATABASE_URL
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
2. **Verify database is accessible:**
|
|
300
|
+
```bash
|
|
301
|
+
psql $PYCHARTER_DATABASE_URL -c "SELECT 1;"
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
3. **Ensure database is initialized:**
|
|
305
|
+
```bash
|
|
306
|
+
pycharter db init $PYCHARTER_DATABASE_URL
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### API Not Starting
|
|
310
|
+
|
|
311
|
+
If the API fails to start:
|
|
312
|
+
|
|
313
|
+
1. **Check if uvicorn is installed:**
|
|
314
|
+
```bash
|
|
315
|
+
pip install pycharter[api]
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
2. **Check for port conflicts:**
|
|
319
|
+
```bash
|
|
320
|
+
# Use a different port
|
|
321
|
+
pycharter api --port 8001
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
3. **Check logs for errors:**
|
|
325
|
+
The API will print error messages to stderr if there are configuration issues.
|
|
326
|
+
|
|
327
|
+
## Production Deployment
|
|
328
|
+
|
|
329
|
+
For production, consider:
|
|
330
|
+
|
|
331
|
+
1. **Use a process manager** (systemd, supervisor, etc.)
|
|
332
|
+
2. **Set environment variables** in your deployment configuration
|
|
333
|
+
3. **Use a reverse proxy** (nginx, traefik) in front of the API
|
|
334
|
+
4. **Enable HTTPS** for secure connections
|
|
335
|
+
5. **Configure CORS** appropriately in `api/main.py`
|
|
336
|
+
6. **Use a production ASGI server** like Gunicorn with Uvicorn workers:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
gunicorn api.main:app -w 4 -k uvicorn.workers.UvicornWorker
|
|
340
|
+
```
|