classifyre-cli 0.6.18__tar.gz → 0.7.0__tar.gz
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.
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/.turbo/turbo-build.log +1 -1
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/PKG-INFO +1 -1
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/package.json +1 -1
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/pyproject.toml +1 -1
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/tabular_base.py +7 -2
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/sql_lineage.py +123 -8
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_sql_lineage.py +131 -6
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/uv.lock +113 -125
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/.gitignore +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/.python-version +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/README.md +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/main.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/scripts/generate_runtime_packages.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/scripts/generate_sdk_completions.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/augmentation/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/augmentation/contract.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/augmentation/preview.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/augmentation/runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/augmentation/sdk.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/augmentation/session.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/config.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/base.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/config.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_custom_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_decision.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_provider_call.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_tag.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/contract.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/files.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/preview.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/results.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/sdk.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/custom_detector/session.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/engine_version.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/errors.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/file_evaluation/asset_fixture.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/file_evaluation/runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/graph/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/graph/edges.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/main.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/models/generated_detectors.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/models/generated_input.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/models/generated_single_asset_scan_results.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/child.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/cli.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/cohort.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/contract.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/execute.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/files.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/groups.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/outputs.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/packages.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/protocol.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/redact.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/scaffold.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/sdk.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/notebook/serialize.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/outputs/base.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/outputs/console.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/outputs/file.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/outputs/rest.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/asset_payload_server.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/detector_breaker.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/detector_pipeline.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/finding_context.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/media_artifact.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/parsed_content_provider.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/payload_window.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/scan_cache.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/base.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/custom/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/custom/env.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/custom/runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/custom/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/dremio/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/dremio/client.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/dremio/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/git/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/git/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/hugging_face/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/hugging_face/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/object_storage/base.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/reddit/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/reddit/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/sources/youtube/source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/telemetry.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/dependency_groups.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/embedded_files.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/external_video.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/file_parser.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/payload.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/range_reader.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/redaction.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/resources.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/sampling_cursor.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/sampling_frontier.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/source_files.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/torch_runtime.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/transcription.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/urn.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/validation.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/conftest.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_decision_runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_gliner25_runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_custom_detector_session.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/test_tag_runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/pipeline/test_detector_breaker.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/pipeline/test_detector_pipeline.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_arrow_ipc_payloads.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_augmentation_contract.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_augmentation_edges.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_augmentation_patch.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_augmentation_scan_cache.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_augmentation_sdk.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_augmentation_session.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_augmentation_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_automatic_new_first.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_byte_valued_fields.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_config.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_detector_end_to_end.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_detector_evaluation.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_detector_notebook.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_detector_templates.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_source_relationships.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_custom_source_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_dependency_groups.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_detector_scope.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_dremio_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_finding_context.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_git_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_graph_edges.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_hugging_face_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_lineage_loss_reporting.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_main.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_media_embedding.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_asset_entity.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_asset_extract.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_asset_tags.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_cohort.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_execute.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_files.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_groups.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_log_levels.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_packages.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_query_assets.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_sdk_exports.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_serialize.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notebook_templates.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_object_storage_download_efficiency.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_payload_window.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_payload_window_parquet.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_range_reading.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_reddit_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_rest_backpressure.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_runtime_packages.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_s3_compatible_storage_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_sampling_cursor.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_sampling_frontier.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_scan_cache.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_sdk_completions.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_secret_redaction.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_semantic_declarations.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_source_strategy_conformance.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_tabular_lineage.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_torch_runtime.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_urn.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_view_dependencies_are_not_foreign_keys.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_embedded_files.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_file_parser.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_payload.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_transcription.py +0 -0
- {classifyre_cli-0.6.18 → classifyre_cli-0.7.0}/tests/utils/test_video_processing.py +0 -0
|
@@ -32,7 +32,12 @@ from ..utils.file_metadata import build_columns
|
|
|
32
32
|
from ..utils.file_parser import render_bytes_cell
|
|
33
33
|
from ..utils.hashing import hash_id, unhash_id
|
|
34
34
|
from ..utils.sampling_frontier import RangeFrontier
|
|
35
|
-
from ..utils.sql_lineage import
|
|
35
|
+
from ..utils.sql_lineage import (
|
|
36
|
+
NO_UPSTREAM,
|
|
37
|
+
column_mappings_by_table,
|
|
38
|
+
fields_for_table,
|
|
39
|
+
sqlglot_dialect,
|
|
40
|
+
)
|
|
36
41
|
from ..utils.urn import Urn, UrnError
|
|
37
42
|
from .base import BaseSource
|
|
38
43
|
from .tabular_utils import (
|
|
@@ -1039,7 +1044,7 @@ class BaseTabularSource(BaseSource):
|
|
|
1039
1044
|
# spelled the name (a synonym, a quoted alias).
|
|
1040
1045
|
fields = [mapping for group in by_table.values() for mapping in group]
|
|
1041
1046
|
else:
|
|
1042
|
-
fields =
|
|
1047
|
+
fields = fields_for_table(by_table, upstream_key)
|
|
1043
1048
|
upstream_hash = table_hash_by_key.get(upstream_key)
|
|
1044
1049
|
upstream_ref = (
|
|
1045
1050
|
Ref.asset(upstream_hash)
|
|
@@ -32,6 +32,7 @@ logger = logging.getLogger(__name__)
|
|
|
32
32
|
__all__ = [
|
|
33
33
|
"NO_UPSTREAM",
|
|
34
34
|
"column_mappings_by_table",
|
|
35
|
+
"fields_for_table",
|
|
35
36
|
"sqlglot_dialect",
|
|
36
37
|
"table_paths_from_sql",
|
|
37
38
|
"upstream_tables_from_sql",
|
|
@@ -171,6 +172,114 @@ def _paths(node: Any, trail: tuple[Any, ...] = ()) -> Iterator[tuple[Any, ...]]:
|
|
|
171
172
|
yield from _paths(child, trail)
|
|
172
173
|
|
|
173
174
|
|
|
175
|
+
def _table_key(table: Any) -> str:
|
|
176
|
+
"""A table as the SQL named it: ``schema.table``, or ``table`` when written bare.
|
|
177
|
+
|
|
178
|
+
The schema stays in the key because two tables can share a name across
|
|
179
|
+
schemas (``core.customers`` and ``edge.customers``) and a view can read both.
|
|
180
|
+
"""
|
|
181
|
+
name = str(table.name).lower()
|
|
182
|
+
schema = str(table.db).lower()
|
|
183
|
+
return f"{schema}.{name}" if schema else name
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def fields_for_table(
|
|
187
|
+
by_table: dict[str, list[FieldMapping]], table_key: tuple[str, ...]
|
|
188
|
+
) -> list[FieldMapping]:
|
|
189
|
+
"""The mappings that belong to one upstream, given its catalog key.
|
|
190
|
+
|
|
191
|
+
``table_key`` ends ``(…, schema, table)``. The schema-qualified spelling wins,
|
|
192
|
+
so same-named tables in two schemas are never confused; a table written bare
|
|
193
|
+
(it was on the search path) matches by name; and a name that only one
|
|
194
|
+
qualified entry carries is that entry, however the SQL spelled the schema.
|
|
195
|
+
"""
|
|
196
|
+
name = str(table_key[-1]).lower()
|
|
197
|
+
if len(table_key) >= 2:
|
|
198
|
+
qualified = f"{str(table_key[-2]).lower()}.{name}"
|
|
199
|
+
if qualified in by_table:
|
|
200
|
+
return list(by_table[qualified])
|
|
201
|
+
if name in by_table:
|
|
202
|
+
return list(by_table[name])
|
|
203
|
+
candidates = [key for key in by_table if key.rsplit(".", 1)[-1] == name]
|
|
204
|
+
return list(by_table[candidates[0]]) if len(candidates) == 1 else []
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _inline_lateral_subqueries(statement: Any, exp: Any) -> None:
|
|
208
|
+
"""Make ``LATERAL (SELECT …) t`` an ordinary derived table.
|
|
209
|
+
|
|
210
|
+
sqlglot's lineage stops at a lateral and reports every column its subquery
|
|
211
|
+
mentions, including the outer table's correlated one, as feeding the output.
|
|
212
|
+
As a plain derived table the output columns follow their own projection to
|
|
213
|
+
the table they come from; the correlation is still found among the
|
|
214
|
+
indirect dependencies because scopes resolve names through their parents.
|
|
215
|
+
A lateral over a function (``jsonb_each(…)``) is left alone.
|
|
216
|
+
"""
|
|
217
|
+
for lateral in list(statement.find_all(exp.Lateral)):
|
|
218
|
+
inner = lateral.this
|
|
219
|
+
if not isinstance(inner, exp.Subquery):
|
|
220
|
+
continue
|
|
221
|
+
alias = lateral.args.get("alias")
|
|
222
|
+
if alias is not None:
|
|
223
|
+
inner.set("alias", alias.copy())
|
|
224
|
+
lateral.replace(inner.copy())
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _qualify_lone_sources(statement: Any, exp: Any) -> None:
|
|
228
|
+
"""Name the table on columns written without one, where only one table is in play.
|
|
229
|
+
|
|
230
|
+
Postgres stores a single-table view with no qualifiers (``WHERE status <> …``),
|
|
231
|
+
and sqlglot will not place such a column without the table's schema. With one
|
|
232
|
+
table in the ``FROM`` there is only one place it can come from, so say so.
|
|
233
|
+
That also stops an output name that re-uses a column name (``qty::int AS qty``)
|
|
234
|
+
from being ambiguous the next time ``qty`` is read.
|
|
235
|
+
|
|
236
|
+
A name in ``ORDER BY`` / ``GROUP BY`` / ``HAVING`` that matches an output alias
|
|
237
|
+
is left for sqlglot to expand: it means the alias, not the column.
|
|
238
|
+
"""
|
|
239
|
+
for select in list(statement.find_all(exp.Select)):
|
|
240
|
+
source = select.args.get("from_") or select.args.get("from")
|
|
241
|
+
if source is None or select.args.get("joins"):
|
|
242
|
+
continue
|
|
243
|
+
node = source.this
|
|
244
|
+
if isinstance(node, exp.Table) and isinstance(node.this, exp.Identifier):
|
|
245
|
+
qualifier = (node.args.get("alias") and node.args["alias"].this) or node.this
|
|
246
|
+
elif isinstance(node, exp.Subquery) and node.args.get("alias"):
|
|
247
|
+
qualifier = node.args["alias"].this
|
|
248
|
+
else:
|
|
249
|
+
continue
|
|
250
|
+
if not isinstance(qualifier, exp.Identifier):
|
|
251
|
+
continue
|
|
252
|
+
|
|
253
|
+
derived = {
|
|
254
|
+
projection.alias.lower()
|
|
255
|
+
for projection in select.expressions
|
|
256
|
+
if isinstance(projection, exp.Alias)
|
|
257
|
+
and not (
|
|
258
|
+
isinstance(projection.this, exp.Column)
|
|
259
|
+
and projection.this.name.lower() == projection.alias.lower()
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
for column in list(select.find_all(exp.Column)):
|
|
263
|
+
if column.table or not isinstance(column.this, exp.Identifier):
|
|
264
|
+
continue
|
|
265
|
+
if column.find_ancestor(exp.Select) is not select:
|
|
266
|
+
continue
|
|
267
|
+
tail = column.find_ancestor(exp.Order, exp.Group, exp.Having)
|
|
268
|
+
if tail is not None and tail.parent is select and column.name.lower() in derived:
|
|
269
|
+
continue
|
|
270
|
+
column.set("table", qualifier.copy())
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _source_in_scope(scope: Any, name: str) -> Any:
|
|
274
|
+
"""Look a table alias up in this scope, then in the scopes around it."""
|
|
275
|
+
while scope is not None:
|
|
276
|
+
found = scope.sources.get(name)
|
|
277
|
+
if found is not None:
|
|
278
|
+
return found
|
|
279
|
+
scope = scope.parent
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
|
|
174
283
|
def column_mappings_by_table(
|
|
175
284
|
sql: str,
|
|
176
285
|
*,
|
|
@@ -178,9 +287,10 @@ def column_mappings_by_table(
|
|
|
178
287
|
) -> dict[str, list[FieldMapping]]:
|
|
179
288
|
"""Per-column dependencies of a view, split by the table they come from.
|
|
180
289
|
|
|
181
|
-
Keys are lower-cased table names
|
|
182
|
-
|
|
183
|
-
the columns that table really
|
|
290
|
+
Keys are lower-cased table names as the SQL wrote them (``schema.table``, or
|
|
291
|
+
``table`` when bare); :func:`fields_for_table` picks the entry for a catalog
|
|
292
|
+
key, so a caller can attach to each edge only the columns that table really
|
|
293
|
+
feeds. :data:`NO_UPSTREAM` holds output
|
|
184
294
|
columns no table feeds.
|
|
185
295
|
|
|
186
296
|
Columns are resolved through CTEs, derived tables and set operations, so a
|
|
@@ -214,6 +324,8 @@ def column_mappings_by_table(
|
|
|
214
324
|
for projection in statement.copy().selects
|
|
215
325
|
if projection.alias_or_name
|
|
216
326
|
}
|
|
327
|
+
_inline_lateral_subqueries(statement, exp)
|
|
328
|
+
_qualify_lone_sources(statement, exp)
|
|
217
329
|
statement = qualify(
|
|
218
330
|
statement, dialect=dialect, validate_qualify_columns=False, identify=False
|
|
219
331
|
)
|
|
@@ -234,7 +346,10 @@ def column_mappings_by_table(
|
|
|
234
346
|
# which we deliberately do not resolve. Dataset-level it is.
|
|
235
347
|
continue
|
|
236
348
|
try:
|
|
237
|
-
|
|
349
|
+
# Quoted, so an alias like "Id" is matched as written instead of folded.
|
|
350
|
+
node = lineage(
|
|
351
|
+
exp.column(name, quoted=True), statement, {}, dialect=dialect, scope=root
|
|
352
|
+
)
|
|
238
353
|
except Exception as exc:
|
|
239
354
|
logger.debug("Could not trace column %s: %s", name, exc)
|
|
240
355
|
continue
|
|
@@ -258,8 +373,8 @@ def column_mappings_by_table(
|
|
|
258
373
|
constant = _Trace()
|
|
259
374
|
constant.add(projections, None, exp)
|
|
260
375
|
continue
|
|
261
|
-
table =
|
|
262
|
-
column = leaf.name.rsplit(".", 1)[-1]
|
|
376
|
+
table = _table_key(leaf.source)
|
|
377
|
+
column = leaf.name.rsplit(".", 1)[-1].strip('"`')
|
|
263
378
|
if table and column and column != "*":
|
|
264
379
|
per_table.setdefault(table, _Trace()).add(projections, column, exp)
|
|
265
380
|
|
|
@@ -312,9 +427,9 @@ def column_mappings_by_table(
|
|
|
312
427
|
if clause is None:
|
|
313
428
|
continue
|
|
314
429
|
for column in clause.find_all(exp.Column):
|
|
315
|
-
source = scope
|
|
430
|
+
source = _source_in_scope(scope, column.table)
|
|
316
431
|
if isinstance(source, exp.Table) and column.name and column.name != "*":
|
|
317
|
-
indirect.setdefault(
|
|
432
|
+
indirect.setdefault(_table_key(source), set()).add(column.name)
|
|
318
433
|
|
|
319
434
|
for table, columns in indirect.items():
|
|
320
435
|
by_table.setdefault(table, []).append(
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from src.graph.edges import FieldMapping, FieldTransform
|
|
6
|
-
from src.utils.sql_lineage import NO_UPSTREAM, column_mappings_by_table
|
|
6
|
+
from src.utils.sql_lineage import NO_UPSTREAM, column_mappings_by_table, fields_for_table
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def _by_downstream(mappings: list[FieldMapping]) -> dict[str | None, FieldMapping]:
|
|
@@ -102,7 +102,7 @@ def test_upper_case_snowflake_names_resolve() -> None:
|
|
|
102
102
|
"SELECT ID, TRIM(NAME) AS NAME FROM ANALYTICS.PUBLIC.ACCOUNTS", dialect="snowflake"
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
-
name = _by_downstream(by_table["accounts"])["NAME"]
|
|
105
|
+
name = _by_downstream(by_table["public.accounts"])["NAME"]
|
|
106
106
|
assert name.upstreams == ("NAME",)
|
|
107
107
|
assert name.steps == ("TRIM",)
|
|
108
108
|
|
|
@@ -134,8 +134,8 @@ def test_postgres_stored_view_text() -> None:
|
|
|
134
134
|
dialect="postgres",
|
|
135
135
|
)
|
|
136
136
|
|
|
137
|
-
assert _by_downstream(by_table["orders"])[None].upstreams == ("customer_id", "status")
|
|
138
|
-
assert _by_downstream(by_table["customers"])[None].upstreams == ("id",)
|
|
137
|
+
assert _by_downstream(by_table["shop.orders"])[None].upstreams == ("customer_id", "status")
|
|
138
|
+
assert _by_downstream(by_table["shop.customers"])[None].upstreams == ("id",)
|
|
139
139
|
assert _by_downstream(by_table[NO_UPSTREAM])["channel"].steps == ()
|
|
140
140
|
|
|
141
141
|
|
|
@@ -146,8 +146,133 @@ def test_each_union_branch_keeps_its_own_kind_and_expression() -> None:
|
|
|
146
146
|
dialect="postgres",
|
|
147
147
|
)
|
|
148
148
|
|
|
149
|
-
orders = _by_downstream(by_table["orders"])["amount"]
|
|
150
|
-
refunds = _by_downstream(by_table["refunds"])["amount"]
|
|
149
|
+
orders = _by_downstream(by_table["shop.orders"])["amount"]
|
|
150
|
+
refunds = _by_downstream(by_table["shop.refunds"])["amount"]
|
|
151
151
|
assert orders.type is FieldTransform.IDENTITY
|
|
152
152
|
assert refunds.steps == ("-",)
|
|
153
153
|
assert refunds.transform == "(-refunds.amount)"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ── Postgres stored text: what pg_get_viewdef really writes ──────────────
|
|
157
|
+
#
|
|
158
|
+
# A single-table view is stored with no qualifiers on its columns at all, which
|
|
159
|
+
# is the shape sqlglot cannot place without a schema.
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_single_table_view_keeps_where_group_and_order_columns_as_indirect() -> None:
|
|
163
|
+
by_table = column_mappings_by_table(
|
|
164
|
+
""" SELECT order_date,
|
|
165
|
+
sum(gross_amount) AS revenue
|
|
166
|
+
FROM lab_mart.fct_orders f
|
|
167
|
+
WHERE status <> 'CANCELLED'::text
|
|
168
|
+
GROUP BY order_date
|
|
169
|
+
ORDER BY order_date;""",
|
|
170
|
+
dialect="postgres",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
mappings = _by_downstream(by_table["lab_mart.fct_orders"])
|
|
174
|
+
assert mappings["revenue"].upstreams == ("gross_amount",)
|
|
175
|
+
assert mappings[None].type is FieldTransform.INDIRECT
|
|
176
|
+
assert mappings[None].upstreams == ("order_date", "status")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_output_name_that_shadows_a_source_column_can_be_used_again() -> None:
|
|
180
|
+
# `qty::integer AS qty` renames in place, then `qty` is used a second time.
|
|
181
|
+
by_table = column_mappings_by_table(
|
|
182
|
+
""" SELECT order_ref,
|
|
183
|
+
qty::integer AS qty,
|
|
184
|
+
qty::integer::numeric * unit_price::numeric AS line_total
|
|
185
|
+
FROM lab_raw.order_lines_raw l;""",
|
|
186
|
+
dialect="postgres",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
mappings = _by_downstream(by_table["lab_raw.order_lines_raw"])
|
|
190
|
+
assert mappings["qty"].upstreams == ("qty",)
|
|
191
|
+
assert mappings["line_total"].upstreams == ("qty", "unit_price")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_order_by_an_output_alias_is_not_mistaken_for_a_column() -> None:
|
|
195
|
+
by_table = column_mappings_by_table(
|
|
196
|
+
"SELECT region, SUM(amount) AS total FROM sales GROUP BY region ORDER BY total DESC",
|
|
197
|
+
dialect="postgres",
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
mappings = _by_downstream(by_table["sales"])
|
|
201
|
+
assert mappings["total"].upstreams == ("amount",)
|
|
202
|
+
assert mappings[None].upstreams == ("region",)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_quoted_mixed_case_output_names_resolve() -> None:
|
|
206
|
+
by_table = column_mappings_by_table(
|
|
207
|
+
""" SELECT "Order ID" AS "Id",
|
|
208
|
+
lower("Customer Email") AS "Mail",
|
|
209
|
+
"Total (EUR)" * 1.19 AS "Gross"
|
|
210
|
+
FROM lab_edge."Mixed Case Orders" m;""",
|
|
211
|
+
dialect="postgres",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
mappings = _by_downstream(by_table["lab_edge.mixed case orders"])
|
|
215
|
+
assert mappings["Id"].upstreams == ("Order ID",)
|
|
216
|
+
assert mappings["Mail"].steps == ("LOWER",)
|
|
217
|
+
assert mappings["Gross"].upstreams == ("Total (EUR)",)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_same_table_name_in_two_schemas_is_kept_apart() -> None:
|
|
221
|
+
by_table = column_mappings_by_table(
|
|
222
|
+
""" SELECT c.email,
|
|
223
|
+
e.loyalty_tier
|
|
224
|
+
FROM lab_core.customers c
|
|
225
|
+
JOIN lab_edge.customers e ON e.customer_id = c.customer_id;""",
|
|
226
|
+
dialect="postgres",
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
core = _by_downstream(by_table["lab_core.customers"])
|
|
230
|
+
edge = _by_downstream(by_table["lab_edge.customers"])
|
|
231
|
+
assert set(core) == {"email", None}
|
|
232
|
+
assert set(edge) == {"loyalty_tier", None}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def test_lateral_subquery_is_followed_to_its_own_table() -> None:
|
|
236
|
+
by_table = column_mappings_by_table(
|
|
237
|
+
""" SELECT c.customer_id,
|
|
238
|
+
t.order_id,
|
|
239
|
+
t.total_amount
|
|
240
|
+
FROM lab_core.customers c
|
|
241
|
+
CROSS JOIN LATERAL ( SELECT o.order_id,
|
|
242
|
+
o.total_amount
|
|
243
|
+
FROM lab_core.orders o
|
|
244
|
+
WHERE o.customer_id = c.customer_id
|
|
245
|
+
ORDER BY o.total_amount DESC
|
|
246
|
+
LIMIT 3) t;""",
|
|
247
|
+
dialect="postgres",
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
customers = _by_downstream(by_table["lab_core.customers"])
|
|
251
|
+
orders = _by_downstream(by_table["lab_core.orders"])
|
|
252
|
+
assert set(customers) == {"customer_id", None}
|
|
253
|
+
assert orders["order_id"].upstreams == ("order_id",)
|
|
254
|
+
assert orders["total_amount"].upstreams == ("total_amount",)
|
|
255
|
+
# The correlation `o.customer_id = c.customer_id` shapes rows from both.
|
|
256
|
+
assert "customer_id" in orders[None].upstreams
|
|
257
|
+
assert customers[None].upstreams == ("customer_id",)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_fields_for_table_prefers_the_schema_it_was_written_with() -> None:
|
|
261
|
+
by_table = column_mappings_by_table(
|
|
262
|
+
"SELECT c.email, e.tier FROM lab_core.customers c "
|
|
263
|
+
"JOIN lab_edge.customers e ON e.customer_id = c.customer_id",
|
|
264
|
+
dialect="postgres",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
core = fields_for_table(by_table, ("db", "lab_core", "customers"))
|
|
268
|
+
edge = fields_for_table(by_table, ("db", "lab_edge", "customers"))
|
|
269
|
+
assert {m.downstream for m in core} == {"email", None}
|
|
270
|
+
assert {m.downstream for m in edge} == {"tier", None}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def test_fields_for_table_matches_a_bare_name_by_table() -> None:
|
|
274
|
+
# On the search path Postgres writes the table without its schema.
|
|
275
|
+
by_table = column_mappings_by_table("SELECT id FROM orders", dialect="postgres")
|
|
276
|
+
|
|
277
|
+
assert [m.downstream for m in fields_for_table(by_table, ("db", "public", "orders"))] == ["id"]
|
|
278
|
+
assert fields_for_table(by_table, ("db", "public", "refunds")) == []
|