classifyre-cli 0.6.4__tar.gz → 0.6.5__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.5/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/PKG-INFO +1 -1
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/package.json +1 -1
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/pyproject.toml +1 -1
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/scripts/generate_sdk_completions.py +12 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/sdk.py +34 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/session.py +5 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/graph/__init__.py +2 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/graph/edges.py +51 -2
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/sdk.py +129 -1
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/urn.py +28 -1
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_sdk.py +1 -0
- classifyre_cli-0.6.5/tests/test_notebook_asset_entity.py +82 -0
- classifyre_cli-0.6.5/tests/test_semantic_declarations.py +96 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/uv.lock +348 -328
- classifyre_cli-0.6.4/.turbo/turbo-build.log +0 -3
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/.gitignore +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/.python-version +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/README.md +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/main.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/scripts/generate_runtime_packages.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/contract.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/preview.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/config.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/config.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_custom_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_tag.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/contract.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/files.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/preview.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/results.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/sdk.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/session.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/engine_version.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/errors.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/file_evaluation/asset_fixture.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/file_evaluation/runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/main.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/models/generated_detectors.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/models/generated_input.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/models/generated_single_asset_scan_results.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/child.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/cli.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/cohort.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/contract.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/execute.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/files.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/groups.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/outputs.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/packages.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/protocol.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/redact.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/scaffold.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/serialize.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/console.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/file.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/rest.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/asset_payload_server.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/detector_breaker.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/detector_pipeline.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/parsed_content_provider.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/payload_window.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/scan_cache.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/env.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/git/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/git/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hugging_face/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hugging_face/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/object_storage/base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/reddit/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/reddit/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/youtube/source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/telemetry.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/dependency_groups.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/embedded_files.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/external_video.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/file_parser.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/payload.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/range_reader.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/redaction.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/resources.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/source_files.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/sql_lineage.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/torch_runtime.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/transcription.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/validation.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/conftest.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_custom_detector_session.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_tag_runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_detector_breaker.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_detector_pipeline.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_arrow_ipc_payloads.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_contract.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_edges.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_patch.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_scan_cache.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_session.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_byte_valued_fields.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_config.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_end_to_end.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_evaluation.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_notebook.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_templates.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_source_relationships.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_source_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_dependency_groups.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_detector_scope.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_git_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_graph_edges.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_hugging_face_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_lineage_loss_reporting.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_main.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_asset_extract.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_asset_tags.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_cohort.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_execute.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_files.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_groups.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_log_levels.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_packages.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_query_assets.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_sdk_exports.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_serialize.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_templates.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_object_storage_download_efficiency.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_payload_window.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_payload_window_parquet.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_range_reading.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_reddit_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_rest_backpressure.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_runtime_packages.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_s3_compatible_storage_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_scan_cache.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sdk_completions.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_secret_redaction.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_source_strategy_conformance.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tabular_lineage.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_torch_runtime.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_urn.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_embedded_files.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_file_parser.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_payload.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_transcription.py +0 -0
- {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_video_processing.py +0 -0
|
@@ -35,6 +35,7 @@ from src.notebook.contract import ( # noqa: E402
|
|
|
35
35
|
from src.notebook.sdk import ( # noqa: E402
|
|
36
36
|
Asset,
|
|
37
37
|
Context,
|
|
38
|
+
Entity,
|
|
38
39
|
FieldMapping,
|
|
39
40
|
FlowType,
|
|
40
41
|
NotebookFile,
|
|
@@ -259,6 +260,13 @@ def build() -> dict[str, Any]:
|
|
|
259
260
|
"documentation": _summary(Tag),
|
|
260
261
|
"insertText": "Tag",
|
|
261
262
|
},
|
|
263
|
+
{
|
|
264
|
+
"label": "Entity",
|
|
265
|
+
"kind": "class",
|
|
266
|
+
"detail": 'Entity(name, type="ORGANIZATION", identifiers={...})',
|
|
267
|
+
"documentation": _summary(Entity),
|
|
268
|
+
"insertText": "Entity",
|
|
269
|
+
},
|
|
262
270
|
{
|
|
263
271
|
"label": "Ref",
|
|
264
272
|
"kind": "class",
|
|
@@ -304,6 +312,10 @@ def build() -> dict[str, Any]:
|
|
|
304
312
|
"documentation": _summary(Tag),
|
|
305
313
|
"fields": _fields(Tag),
|
|
306
314
|
},
|
|
315
|
+
"Entity": {
|
|
316
|
+
"documentation": _summary(Entity),
|
|
317
|
+
"fields": _fields(Entity),
|
|
318
|
+
},
|
|
307
319
|
"AugmentedAsset": {
|
|
308
320
|
"documentation": _summary(augmentation_sdk.AugmentedAsset),
|
|
309
321
|
"members": _members(augmentation_sdk.AugmentedAsset),
|
|
@@ -44,11 +44,13 @@ from ..graph.edges import (
|
|
|
44
44
|
FlowType,
|
|
45
45
|
Ref,
|
|
46
46
|
contains,
|
|
47
|
+
edge_to_payload,
|
|
47
48
|
flow,
|
|
48
49
|
references,
|
|
49
50
|
same_as,
|
|
50
51
|
uses,
|
|
51
52
|
)
|
|
53
|
+
from ..graph.edges import means as means_edge
|
|
52
54
|
from ..notebook.files import ParsedContent, pages, parse
|
|
53
55
|
from ..notebook.sdk import (
|
|
54
56
|
MODULE_NAME,
|
|
@@ -141,6 +143,7 @@ class AugmentedAsset:
|
|
|
141
143
|
self._added_metadata: dict[str, Any] = {}
|
|
142
144
|
self._tags: dict[str, str] = {}
|
|
143
145
|
self._links: list[str] = []
|
|
146
|
+
self._means: list[dict[str, Any]] = []
|
|
144
147
|
self._new_urn: str | None = None
|
|
145
148
|
self.warnings: list[str] = []
|
|
146
149
|
|
|
@@ -313,6 +316,33 @@ class AugmentedAsset:
|
|
|
313
316
|
if target not in self._links:
|
|
314
317
|
self._links.append(target)
|
|
315
318
|
|
|
319
|
+
def means(
|
|
320
|
+
self,
|
|
321
|
+
key: str,
|
|
322
|
+
*,
|
|
323
|
+
evidence: Mapping[str, Any] | None = None,
|
|
324
|
+
confidence: float | None = None,
|
|
325
|
+
) -> None:
|
|
326
|
+
"""Declare that this asset is about a glossary concept or entity, by key.
|
|
327
|
+
|
|
328
|
+
Additive, like ``tag()`` and ``link()``: a DECLARED semantic link. The
|
|
329
|
+
term need not exist yet; the declaration links once it is defined. An
|
|
330
|
+
invalid key is skipped with a warning.
|
|
331
|
+
"""
|
|
332
|
+
try:
|
|
333
|
+
edge = means_edge(
|
|
334
|
+
Ref.asset(self._hash),
|
|
335
|
+
Ref.term(str(key or "")),
|
|
336
|
+
evidence=dict(evidence or {}),
|
|
337
|
+
confidence=confidence,
|
|
338
|
+
)
|
|
339
|
+
except ValueError as exc:
|
|
340
|
+
self.warnings.append(f"Ignoring asset.means({key!r}): {exc}")
|
|
341
|
+
return
|
|
342
|
+
payload = edge_to_payload(edge)
|
|
343
|
+
if payload not in self._means:
|
|
344
|
+
self._means.append(payload)
|
|
345
|
+
|
|
316
346
|
def set_urn(self, urn: str) -> None:
|
|
317
347
|
"""Set the asset's cross-system identity, only if the connector left it empty.
|
|
318
348
|
|
|
@@ -337,6 +367,7 @@ class AugmentedAsset:
|
|
|
337
367
|
"metadata": dict(self._added_metadata),
|
|
338
368
|
"tags": dict(self._tags),
|
|
339
369
|
"links": list(self._links),
|
|
370
|
+
"means": list(self._means),
|
|
340
371
|
"urn": self._new_urn,
|
|
341
372
|
"warnings": list(self.warnings),
|
|
342
373
|
}
|
|
@@ -418,6 +449,7 @@ def build_augmentation_module(context: AugmentContext) -> types.ModuleType:
|
|
|
418
449
|
module.flow = flow # type: ignore[attr-defined]
|
|
419
450
|
module.contains = contains # type: ignore[attr-defined]
|
|
420
451
|
module.references = references # type: ignore[attr-defined]
|
|
452
|
+
module.means = means_edge # type: ignore[attr-defined]
|
|
421
453
|
module.same_as = same_as # type: ignore[attr-defined]
|
|
422
454
|
module.uses = uses # type: ignore[attr-defined]
|
|
423
455
|
module.urn_for = urn_for # type: ignore[attr-defined]
|
|
@@ -439,6 +471,7 @@ def build_augmentation_module(context: AugmentContext) -> types.ModuleType:
|
|
|
439
471
|
"contains",
|
|
440
472
|
"ctx",
|
|
441
473
|
"flow",
|
|
474
|
+
"means",
|
|
442
475
|
"pages",
|
|
443
476
|
"parse",
|
|
444
477
|
"references",
|
|
@@ -470,6 +503,7 @@ def augmentation_namespace(context: AugmentContext) -> dict[str, Any]:
|
|
|
470
503
|
"flow": flow,
|
|
471
504
|
"contains": contains,
|
|
472
505
|
"references": references,
|
|
506
|
+
"means": means_edge,
|
|
473
507
|
"same_as": same_as,
|
|
474
508
|
"uses": uses,
|
|
475
509
|
"urn_for": urn_for,
|
|
@@ -463,6 +463,11 @@ class AugmentationSession:
|
|
|
463
463
|
|
|
464
464
|
self._ingest_edges(patch, asset_hash=asset_hash or None)
|
|
465
465
|
|
|
466
|
+
# asset.means(): declarations, already built as MEANS edges by the SDK.
|
|
467
|
+
declared = patch.get("means")
|
|
468
|
+
if isinstance(declared, list) and declared:
|
|
469
|
+
self._ingest_edges({"edges": declared}, asset_hash=asset_hash or None)
|
|
470
|
+
|
|
466
471
|
for message in patch.get("warnings") or []:
|
|
467
472
|
self._warn(f"Augmentation on {getattr(asset, 'name', asset_hash)}: {message}")
|
|
468
473
|
|
|
@@ -35,7 +35,7 @@ from dataclasses import dataclass, field
|
|
|
35
35
|
from enum import StrEnum
|
|
36
36
|
from typing import Any, ClassVar
|
|
37
37
|
|
|
38
|
-
from ..utils.urn import Urn
|
|
38
|
+
from ..utils.urn import Urn, UrnError
|
|
39
39
|
|
|
40
40
|
__all__ = [
|
|
41
41
|
"ContainmentType",
|
|
@@ -189,6 +189,10 @@ class _RefMeta(type):
|
|
|
189
189
|
"uri": "Ref.urn(<a urn like 'company://at/firmenbuchnummer/606601k'>)",
|
|
190
190
|
"url": "Ref.urn(<a urn like 'company://at/firmenbuchnummer/606601k'>)",
|
|
191
191
|
"external": "Ref.urn(<a urn naming the object in its own system>)",
|
|
192
|
+
"terms": "Ref.term(<a glossary key like 'gmbh'>)",
|
|
193
|
+
"concept": "Ref.term(<a glossary key like 'gmbh'>)",
|
|
194
|
+
"glossary": "Ref.term(<a glossary key like 'gmbh'>)",
|
|
195
|
+
"meaning": "Ref.term(<a glossary key like 'gmbh'>)",
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
def __getattr__(cls, name: str) -> Any:
|
|
@@ -196,7 +200,8 @@ class _RefMeta(type):
|
|
|
196
200
|
hint = f" Did you mean {suggestion}?" if suggestion else ""
|
|
197
201
|
raise AttributeError(
|
|
198
202
|
f"Ref has no '{name}'. An edge endpoint is one of "
|
|
199
|
-
f"Ref.asset(<asset id>), Ref.urn(<urn>)
|
|
203
|
+
f"Ref.asset(<asset id>), Ref.urn(<urn>), Ref.finding(<finding id>) "
|
|
204
|
+
f"or Ref.term(<glossary key>)."
|
|
200
205
|
f"{hint}"
|
|
201
206
|
)
|
|
202
207
|
|
|
@@ -232,6 +237,18 @@ class Ref(metaclass=_RefMeta):
|
|
|
232
237
|
raise ValueError("Ref.finding() needs a finding id")
|
|
233
238
|
return Ref("finding", value)
|
|
234
239
|
|
|
240
|
+
@staticmethod
|
|
241
|
+
def term(key: str) -> Ref:
|
|
242
|
+
"""A glossary concept or entity by key: ``Ref("urn", "term://glossary/<key>")``.
|
|
243
|
+
|
|
244
|
+
The term need not exist yet: an unknown key is kept as a pending
|
|
245
|
+
reference and links once someone defines it.
|
|
246
|
+
"""
|
|
247
|
+
try:
|
|
248
|
+
return Ref("urn", str(Urn.term(key)))
|
|
249
|
+
except UrnError as error:
|
|
250
|
+
raise ValueError(str(error)) from error
|
|
251
|
+
|
|
235
252
|
|
|
236
253
|
@dataclass(frozen=True)
|
|
237
254
|
class Edge:
|
|
@@ -450,6 +467,38 @@ def references(
|
|
|
450
467
|
)
|
|
451
468
|
|
|
452
469
|
|
|
470
|
+
#: Relation type of a meaning declaration (C12), class REFERENCE.
|
|
471
|
+
MEANS = "MEANS"
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def means(
|
|
475
|
+
subject: Ref,
|
|
476
|
+
term: Ref | str,
|
|
477
|
+
*,
|
|
478
|
+
confidence: float | None = None,
|
|
479
|
+
evidence: dict[str, Any] | None = None,
|
|
480
|
+
method: Method = Method.SYSTEM_CATALOG,
|
|
481
|
+
) -> Edge:
|
|
482
|
+
"""``subject`` is about a glossary concept or entity (a DECLARED link).
|
|
483
|
+
|
|
484
|
+
``subject`` is an asset, a URN or a finding; ``term`` is ``Ref.term(key)``
|
|
485
|
+
or the bare key. ``evidence`` says where the meaning came from, e.g.
|
|
486
|
+
``{"field": "Rechtsform", "value": "GES"}``.
|
|
487
|
+
"""
|
|
488
|
+
target = Ref.term(term) if isinstance(term, str) else term
|
|
489
|
+
if target.kind != "urn" or not target.value.startswith("term://"):
|
|
490
|
+
raise ValueError("means() points at a glossary term: pass Ref.term(<key>) or the key")
|
|
491
|
+
return Edge(
|
|
492
|
+
frm=subject,
|
|
493
|
+
to=target,
|
|
494
|
+
edge_class=EdgeClass.REFERENCE,
|
|
495
|
+
relation_type=MEANS,
|
|
496
|
+
method=method,
|
|
497
|
+
confidence=_confidence(method, confidence),
|
|
498
|
+
evidence=dict(evidence or {}),
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
|
|
453
502
|
def uses(actor: Ref, target: Ref, *, type: UsageType = UsageType.ACCESSED) -> Edge:
|
|
454
503
|
"""``actor`` touched ``target``. Answers "who", not "where did this come from"."""
|
|
455
504
|
return Edge(
|
|
@@ -42,6 +42,7 @@ from ..graph.edges import (
|
|
|
42
42
|
UsageType,
|
|
43
43
|
contains,
|
|
44
44
|
flow,
|
|
45
|
+
means,
|
|
45
46
|
references,
|
|
46
47
|
same_as,
|
|
47
48
|
uses,
|
|
@@ -101,6 +102,112 @@ class Tag:
|
|
|
101
102
|
object.__setattr__(self, "severity", normalized)
|
|
102
103
|
|
|
103
104
|
|
|
105
|
+
#: What kind of named thing an entity is. Decides which finding labels its name
|
|
106
|
+
#: is matched under: a PERSON is found among person names, never among
|
|
107
|
+
#: organisations.
|
|
108
|
+
ENTITY_TYPES = ("PERSON", "ORGANIZATION", "LOCATION", "REFERENCE", "OTHER")
|
|
109
|
+
|
|
110
|
+
#: The asset-metadata key a declared entity travels under.
|
|
111
|
+
ENTITY_METADATA_KEY = "_entity"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True)
|
|
115
|
+
class Entity:
|
|
116
|
+
"""The named thing a record *is*: a company, a person, an account.
|
|
117
|
+
|
|
118
|
+
A register or a customer master does not merely mention ACME Holding GmbH --
|
|
119
|
+
its record is ACME. Saying so::
|
|
120
|
+
|
|
121
|
+
Asset(
|
|
122
|
+
id=fn,
|
|
123
|
+
urn=urn_for("firmenbuch", fn),
|
|
124
|
+
entity=Entity(
|
|
125
|
+
type="ORGANIZATION",
|
|
126
|
+
name="ACME Holding GmbH",
|
|
127
|
+
identifiers={"vat": "ATU12345678", "fn": "123456a"},
|
|
128
|
+
aliases=["ACME Holding"],
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
creates the entity in the glossary (or updates it), anchored to this
|
|
133
|
+
record, and from then on every document in every other source that carries
|
|
134
|
+
that name or one of those identifiers is a mention of it -- the e-mail that
|
|
135
|
+
quotes the VAT number, the contract that names the company.
|
|
136
|
+
|
|
137
|
+
``identifiers`` are keyed by the *label a detector finds them under*
|
|
138
|
+
(``iban``, ``email``, a custom detector's label): that label is what links
|
|
139
|
+
a finding to the entity, so an identifier under a label nothing detects
|
|
140
|
+
links nothing. ``key`` pins the glossary key, for a source that already
|
|
141
|
+
refers to the entity with ``Ref.term(key)``.
|
|
142
|
+
|
|
143
|
+
The asset needs a ``urn`` (or the entity a ``key``): it is what makes the
|
|
144
|
+
declaration the same entity on the next scan instead of a new one. A
|
|
145
|
+
declared entity is the register's word and is approved from the start; what
|
|
146
|
+
an operator changes afterwards is kept -- a later scan only adds.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
name: str
|
|
150
|
+
type: str = "OTHER"
|
|
151
|
+
key: str | None = None
|
|
152
|
+
identifiers: Mapping[str, str] = field(default_factory=dict)
|
|
153
|
+
aliases: tuple[str, ...] | list[str] = field(default_factory=tuple)
|
|
154
|
+
attributes: Mapping[str, Any] = field(default_factory=dict)
|
|
155
|
+
|
|
156
|
+
def __post_init__(self) -> None:
|
|
157
|
+
name = str(self.name or "").strip()
|
|
158
|
+
if not name:
|
|
159
|
+
raise ValueError("Entity.name is required and must be a non-empty string")
|
|
160
|
+
object.__setattr__(self, "name", name)
|
|
161
|
+
kind = str(self.type or "OTHER").strip().upper()
|
|
162
|
+
if kind not in ENTITY_TYPES:
|
|
163
|
+
raise ValueError(
|
|
164
|
+
f"Entity.type must be one of {', '.join(ENTITY_TYPES)}, got {self.type!r}"
|
|
165
|
+
)
|
|
166
|
+
object.__setattr__(self, "type", kind)
|
|
167
|
+
key = str(self.key).strip().lower() if self.key is not None else ""
|
|
168
|
+
object.__setattr__(self, "key", key or None)
|
|
169
|
+
if not isinstance(self.identifiers, Mapping):
|
|
170
|
+
raise TypeError(
|
|
171
|
+
'Entity.identifiers must map a label to a value, e.g. {"vat": "ATU12345678"}'
|
|
172
|
+
)
|
|
173
|
+
object.__setattr__(
|
|
174
|
+
self,
|
|
175
|
+
"identifiers",
|
|
176
|
+
{
|
|
177
|
+
str(label).strip(): str(value).strip()
|
|
178
|
+
for label, value in self.identifiers.items()
|
|
179
|
+
if str(label).strip() and value is not None and str(value).strip()
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
if isinstance(self.aliases, str | bytes):
|
|
183
|
+
raise TypeError("Entity.aliases must be a list of names, not a single string")
|
|
184
|
+
object.__setattr__(
|
|
185
|
+
self,
|
|
186
|
+
"aliases",
|
|
187
|
+
tuple(
|
|
188
|
+
dict.fromkeys(str(alias).strip() for alias in self.aliases if str(alias).strip())
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
if not isinstance(self.attributes, Mapping):
|
|
192
|
+
raise TypeError("Entity.attributes must be a mapping")
|
|
193
|
+
object.__setattr__(self, "attributes", dict(self.attributes))
|
|
194
|
+
|
|
195
|
+
def to_metadata(self) -> dict[str, Any]:
|
|
196
|
+
"""The declaration as it travels on the asset. Empty parts are left out,
|
|
197
|
+
so adding a field here never changes the checksum of an asset that does
|
|
198
|
+
not use it."""
|
|
199
|
+
payload: dict[str, Any] = {"name": self.name, "type": self.type}
|
|
200
|
+
if self.key:
|
|
201
|
+
payload["key"] = self.key
|
|
202
|
+
if self.identifiers:
|
|
203
|
+
payload["identifiers"] = dict(sorted(self.identifiers.items()))
|
|
204
|
+
if self.aliases:
|
|
205
|
+
payload["aliases"] = list(self.aliases)
|
|
206
|
+
if self.attributes:
|
|
207
|
+
payload["attributes"] = dict(self.attributes)
|
|
208
|
+
return payload
|
|
209
|
+
|
|
210
|
+
|
|
104
211
|
@dataclass
|
|
105
212
|
class Asset:
|
|
106
213
|
"""One thing worth scanning.
|
|
@@ -168,6 +275,10 @@ class Asset:
|
|
|
168
275
|
#: chose it (``metadata["_cohort"]``), which is how the platform measures
|
|
169
276
|
#: each band's yield. Never part of the asset's checksum.
|
|
170
277
|
cohort: CohortItem | None = None
|
|
278
|
+
#: The named thing this record *is* -- see :class:`Entity`. Recorded as
|
|
279
|
+
#: ``metadata["_entity"]``, so it is part of the checksum only for assets
|
|
280
|
+
#: that declare one. Needs ``urn`` (or ``Entity.key``).
|
|
281
|
+
entity: Entity | None = None
|
|
171
282
|
|
|
172
283
|
def __post_init__(self) -> None:
|
|
173
284
|
self.id = str(self.id).strip()
|
|
@@ -206,6 +317,17 @@ class Asset:
|
|
|
206
317
|
"key": self.cohort.key,
|
|
207
318
|
},
|
|
208
319
|
}
|
|
320
|
+
if self.entity is not None:
|
|
321
|
+
if not isinstance(self.entity, Entity):
|
|
322
|
+
raise TypeError("Asset.entity must be an Entity(...)")
|
|
323
|
+
if not (self.urn and str(self.urn).strip()) and not self.entity.key:
|
|
324
|
+
# Without either, the next scan could not tell this is the same
|
|
325
|
+
# entity and would create it again.
|
|
326
|
+
raise ValueError(
|
|
327
|
+
"Asset.entity needs Asset.urn (build one with urn_for(...)) or "
|
|
328
|
+
"Entity.key, so the next scan updates the same entity"
|
|
329
|
+
)
|
|
330
|
+
self.metadata = {**self.metadata, ENTITY_METADATA_KEY: self.entity.to_metadata()}
|
|
209
331
|
if not isinstance(self.extract, bool):
|
|
210
332
|
raise TypeError(f"Asset.extract must be True or False (got {self.extract!r})")
|
|
211
333
|
reason = str(self.reference_reason).strip() if self.reference_reason is not None else ""
|
|
@@ -843,6 +965,8 @@ def build_module(context: Context) -> types.ModuleType:
|
|
|
843
965
|
module.flow = flow # type: ignore[attr-defined]
|
|
844
966
|
module.contains = contains # type: ignore[attr-defined]
|
|
845
967
|
module.references = references # type: ignore[attr-defined]
|
|
968
|
+
module.means = means # type: ignore[attr-defined]
|
|
969
|
+
module.Entity = Entity # type: ignore[attr-defined]
|
|
846
970
|
module.same_as = same_as # type: ignore[attr-defined]
|
|
847
971
|
module.uses = uses # type: ignore[attr-defined]
|
|
848
972
|
module.urn_for = urn_for # type: ignore[attr-defined]
|
|
@@ -860,6 +984,7 @@ def build_module(context: Context) -> types.ModuleType:
|
|
|
860
984
|
"Asset",
|
|
861
985
|
"Context",
|
|
862
986
|
"ContainmentType",
|
|
987
|
+
"Entity",
|
|
863
988
|
"FieldMapping",
|
|
864
989
|
"FieldTransform",
|
|
865
990
|
"FlowType",
|
|
@@ -873,6 +998,7 @@ def build_module(context: Context) -> types.ModuleType:
|
|
|
873
998
|
"contains",
|
|
874
999
|
"ctx",
|
|
875
1000
|
"flow",
|
|
1001
|
+
"means",
|
|
876
1002
|
"pages",
|
|
877
1003
|
"parse",
|
|
878
1004
|
"references",
|
|
@@ -903,6 +1029,7 @@ def namespace(context: Context) -> dict[str, Any]:
|
|
|
903
1029
|
"__builtins__": __builtins__,
|
|
904
1030
|
"Asset": Asset,
|
|
905
1031
|
"Tag": Tag,
|
|
1032
|
+
"Entity": Entity,
|
|
906
1033
|
"Ref": Ref,
|
|
907
1034
|
"FieldMapping": FieldMapping,
|
|
908
1035
|
"FlowType": FlowType,
|
|
@@ -910,6 +1037,7 @@ def namespace(context: Context) -> dict[str, Any]:
|
|
|
910
1037
|
"flow": flow,
|
|
911
1038
|
"contains": contains,
|
|
912
1039
|
"references": references,
|
|
1040
|
+
"means": means,
|
|
913
1041
|
"same_as": same_as,
|
|
914
1042
|
"uses": uses,
|
|
915
1043
|
"urn_for": urn_for,
|
|
@@ -959,7 +1087,7 @@ def iter_relationships(value: Any) -> Iterable[Edge]:
|
|
|
959
1087
|
else:
|
|
960
1088
|
raise TypeError(
|
|
961
1089
|
"relationships() must yield edges built with flow(), contains(), "
|
|
962
|
-
"references(), same_as() or uses() -- got "
|
|
1090
|
+
"references(), means(), same_as() or uses() -- got "
|
|
963
1091
|
f"{type(item).__name__}. These are not interchangeable: only "
|
|
964
1092
|
"flow() is lineage, and only it answers 'what breaks if this changes'."
|
|
965
1093
|
)
|
|
@@ -20,11 +20,22 @@ character for character -- a test in each language pins the same table of cases.
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
22
|
import logging
|
|
23
|
+
import re
|
|
23
24
|
from dataclasses import dataclass
|
|
24
25
|
from enum import StrEnum
|
|
25
26
|
from typing import Any
|
|
26
27
|
|
|
27
|
-
__all__ = [
|
|
28
|
+
__all__ = [
|
|
29
|
+
"TERM_KEY_PATTERN",
|
|
30
|
+
"CasePolicy",
|
|
31
|
+
"Urn",
|
|
32
|
+
"UrnError",
|
|
33
|
+
"normalize_urn",
|
|
34
|
+
"normalize_urn_or_none",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
#: A glossary term key (contract C8), the same pattern as the API's.
|
|
38
|
+
TERM_KEY_PATTERN = re.compile(r"[a-z0-9][a-z0-9._-]{0,99}")
|
|
28
39
|
|
|
29
40
|
|
|
30
41
|
logger = logging.getLogger(__name__)
|
|
@@ -95,6 +106,10 @@ _register(_Platform("powerbi"))
|
|
|
95
106
|
_register(_Platform("kafka"))
|
|
96
107
|
_register(_Platform("file"))
|
|
97
108
|
|
|
109
|
+
# Glossary terms (contract C8): ``term://glossary/<key>``. Keys are lower-case
|
|
110
|
+
# by definition, so both halves fold.
|
|
111
|
+
_register(_Platform("term", path_case=CasePolicy.LOWER))
|
|
112
|
+
|
|
98
113
|
_DEFAULT_PLATFORM = _Platform("", authority_case=CasePolicy.LOWER, path_case=CasePolicy.PRESERVE)
|
|
99
114
|
|
|
100
115
|
|
|
@@ -254,6 +269,18 @@ class Urn:
|
|
|
254
269
|
def kafka(cls, bootstrap: str, topic: str) -> Urn:
|
|
255
270
|
return cls.of("kafka", bootstrap, topic)
|
|
256
271
|
|
|
272
|
+
@classmethod
|
|
273
|
+
def term(cls, key: str) -> Urn:
|
|
274
|
+
"""A glossary term by its key (C8): ``term://glossary/<key>``."""
|
|
275
|
+
value = (key or "").strip().lower()
|
|
276
|
+
if not TERM_KEY_PATTERN.fullmatch(value):
|
|
277
|
+
raise UrnError(
|
|
278
|
+
f"Not a glossary term key: {key!r}. Keys are lower-case letters, digits, "
|
|
279
|
+
"'.', '_' and '-', starting with a letter or digit, at most 100 characters "
|
|
280
|
+
"(e.g. 'gmbh', 'bank-account-iban')."
|
|
281
|
+
)
|
|
282
|
+
return cls.of("term", "glossary", value)
|
|
283
|
+
|
|
257
284
|
|
|
258
285
|
def _host_port(host: str, port: int | str | None) -> str:
|
|
259
286
|
"""``host:port``, or bare host when no port was configured."""
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""A record that *is* a named thing declares it (G5 R12)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from src.notebook.sdk import ENTITY_METADATA_KEY, Asset, Context, Entity, build_module, namespace
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_entity_is_importable_from_classifyre() -> None:
|
|
11
|
+
module = build_module(Context())
|
|
12
|
+
assert module.Entity is Entity
|
|
13
|
+
assert "Entity" in module.__all__
|
|
14
|
+
assert namespace(Context())["Entity"] is Entity
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_a_declared_entity_travels_in_the_asset_metadata() -> None:
|
|
18
|
+
asset = Asset(
|
|
19
|
+
id="123456a",
|
|
20
|
+
urn="firmenbuch://at/fn/123456a",
|
|
21
|
+
entity=Entity(
|
|
22
|
+
type="organization",
|
|
23
|
+
name=" ACME Holding GmbH ",
|
|
24
|
+
identifiers={"vat": "ATU12345678", "fn": "123456a"},
|
|
25
|
+
aliases=["ACME Holding", "ACME Holding", " "],
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
assert asset.metadata[ENTITY_METADATA_KEY] == {
|
|
30
|
+
"name": "ACME Holding GmbH",
|
|
31
|
+
"type": "ORGANIZATION",
|
|
32
|
+
# Sorted, so the same declaration always hashes to the same checksum.
|
|
33
|
+
"identifiers": {"fn": "123456a", "vat": "ATU12345678"},
|
|
34
|
+
"aliases": ["ACME Holding"],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_an_asset_without_an_entity_keeps_its_metadata_untouched() -> None:
|
|
39
|
+
# The declaration joins the checksum basis only when set, so existing
|
|
40
|
+
# corpora are not re-checksummed for a feature they do not use.
|
|
41
|
+
asset = Asset(id="a", metadata={"k": "v"})
|
|
42
|
+
assert asset.metadata == {"k": "v"}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_the_notebooks_own_metadata_is_kept() -> None:
|
|
46
|
+
asset = Asset(
|
|
47
|
+
id="a",
|
|
48
|
+
urn="crm://customer/1",
|
|
49
|
+
metadata={"segment": "retail"},
|
|
50
|
+
entity=Entity(name="Jane Doe", type="PERSON"),
|
|
51
|
+
)
|
|
52
|
+
assert asset.metadata["segment"] == "retail"
|
|
53
|
+
assert asset.metadata[ENTITY_METADATA_KEY] == {"name": "Jane Doe", "type": "PERSON"}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_a_key_stands_in_for_a_urn() -> None:
|
|
57
|
+
asset = Asset(id="a", entity=Entity(name="ACME", key="ACME-Holding"))
|
|
58
|
+
assert asset.metadata[ENTITY_METADATA_KEY]["key"] == "acme-holding"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_an_entity_needs_a_urn_or_a_key() -> None:
|
|
62
|
+
# Without either the next scan would create the entity again.
|
|
63
|
+
with pytest.raises(ValueError, match="urn"):
|
|
64
|
+
Asset(id="a", entity=Entity(name="ACME"))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_blank_identifiers_are_dropped() -> None:
|
|
68
|
+
entity = Entity(name="ACME", identifiers={"vat": " ", "": "x", "iban": " AT61 "})
|
|
69
|
+
assert entity.identifiers == {"iban": "AT61"}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_bad_shapes_are_rejected_with_what_was_wanted() -> None:
|
|
73
|
+
with pytest.raises(ValueError, match="non-empty"):
|
|
74
|
+
Entity(name=" ")
|
|
75
|
+
with pytest.raises(ValueError, match="one of"):
|
|
76
|
+
Entity(name="ACME", type="COMPANY")
|
|
77
|
+
with pytest.raises(TypeError, match="label"):
|
|
78
|
+
Entity(name="ACME", identifiers=["ATU12345678"]) # type: ignore[arg-type]
|
|
79
|
+
with pytest.raises(TypeError, match="list of names"):
|
|
80
|
+
Entity(name="ACME", aliases="ACME Holding")
|
|
81
|
+
with pytest.raises(TypeError, match="Entity"):
|
|
82
|
+
Asset(id="a", urn="x://a", entity={"name": "ACME"}) # type: ignore[arg-type]
|