classifyre-cli 0.6.2__tar.gz → 0.6.4__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.4/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/PKG-INFO +1 -1
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/package.json +1 -1
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/pyproject.toml +1 -1
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/scripts/generate_models.py +12 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/augmentation/session.py +19 -162
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/__init__.py +2 -0
- classifyre_cli-0.6.4/src/detectors/custom/runners/_custom_detector.py +72 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_factory.py +6 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/__init__.py +9 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/contract.py +45 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/files.py +102 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/preview.py +283 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/results.py +266 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/runner.py +258 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/sdk.py +334 -0
- classifyre_cli-0.6.4/src/detectors/custom_detector/session.py +443 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/engine_version.py +41 -1
- classifyre_cli-0.6.4/src/file_evaluation/asset_fixture.py +142 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/file_evaluation/runner.py +110 -6
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/main.py +26 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/models/generated_detectors.py +250 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/models/generated_single_asset_scan_results.py +6 -1
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/child.py +5 -1
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/cli.py +54 -4
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/protocol.py +24 -0
- classifyre_cli-0.6.4/src/pipeline/asset_payload_server.py +242 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/detector_pipeline.py +121 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/scan_cache.py +66 -15
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/redaction.py +22 -0
- classifyre_cli-0.6.4/tests/detectors/test_custom_detector_session.py +303 -0
- classifyre_cli-0.6.4/tests/test_custom_detector_end_to_end.py +152 -0
- classifyre_cli-0.6.4/tests/test_custom_detector_evaluation.py +75 -0
- classifyre_cli-0.6.4/tests/test_custom_detector_notebook.py +136 -0
- classifyre_cli-0.6.4/tests/test_custom_detector_templates.py +86 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_scan_cache.py +46 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/uv.lock +649 -628
- classifyre_cli-0.6.2/.turbo/turbo-build.log +0 -3
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/.gitignore +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/.python-version +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/README.md +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/main.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/scripts/generate_runtime_packages.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/scripts/generate_sdk_completions.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/augmentation/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/augmentation/contract.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/augmentation/preview.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/augmentation/runner.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/augmentation/sdk.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/config.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/config.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_tag.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/errors.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/graph/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/graph/edges.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/models/generated_input.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/cohort.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/contract.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/execute.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/files.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/groups.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/outputs.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/packages.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/redact.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/scaffold.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/sdk.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/notebook/serialize.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/outputs/base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/outputs/console.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/outputs/file.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/outputs/rest.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/detector_breaker.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/parsed_content_provider.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/payload_window.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/custom/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/custom/env.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/custom/runner.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/custom/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/git/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/git/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/hugging_face/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/hugging_face/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/object_storage/base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/reddit/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/reddit/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/sources/youtube/source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/telemetry.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/dependency_groups.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/embedded_files.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/external_video.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/file_parser.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/payload.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/range_reader.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/resources.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/source_files.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/sql_lineage.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/torch_runtime.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/transcription.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/urn.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/validation.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/conftest.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/test_tag_runner.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/pipeline/test_detector_breaker.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/pipeline/test_detector_pipeline.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_arrow_ipc_payloads.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_augmentation_contract.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_augmentation_edges.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_augmentation_patch.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_augmentation_scan_cache.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_augmentation_sdk.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_augmentation_session.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_augmentation_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_byte_valued_fields.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_config.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_custom_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_custom_source_relationships.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_custom_source_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_dependency_groups.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_detector_scope.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_git_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_graph_edges.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_hugging_face_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_lineage_loss_reporting.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_main.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_asset_extract.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_asset_tags.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_cohort.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_execute.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_files.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_groups.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_log_levels.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_packages.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_query_assets.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_sdk_exports.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_serialize.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notebook_templates.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_object_storage_download_efficiency.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_payload_window.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_payload_window_parquet.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_range_reading.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_reddit_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_rest_backpressure.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_runtime_packages.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_s3_compatible_storage_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_sdk_completions.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_secret_redaction.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_source_strategy_conformance.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_tabular_lineage.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_torch_runtime.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_urn.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_embedded_files.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_file_parser.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_payload.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_transcription.py +0 -0
- {classifyre_cli-0.6.2 → classifyre_cli-0.6.4}/tests/utils/test_video_processing.py +0 -0
|
@@ -15,6 +15,7 @@ _PIPELINE_TYPE_DEFAULTS: dict[str, str] = {
|
|
|
15
15
|
"RegexPipelineSchema": "REGEX",
|
|
16
16
|
"LLMPipelineSchema": "LLM",
|
|
17
17
|
"TagPipelineSchema": "TAG",
|
|
18
|
+
"CodeDetectorPipelineSchema": "CODE_DETECTOR",
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
# Pipeline schema classes whose `severity` field has a string default from JSON schema
|
|
@@ -27,6 +28,7 @@ _SEVERITY_ENUM_DEFAULT_CLASSES = {
|
|
|
27
28
|
"LLMPipelineSchema": "info",
|
|
28
29
|
"TextClassificationPipelineSchema": "info",
|
|
29
30
|
"TagPipelineSchema": "medium",
|
|
31
|
+
"CodeDetectorPipelineSchema": "medium",
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
|
|
@@ -60,6 +62,15 @@ def _patch_severity_enum_defaults(source: str) -> str:
|
|
|
60
62
|
return source
|
|
61
63
|
|
|
62
64
|
|
|
65
|
+
def _patch_category_enum_defaults(source: str) -> str:
|
|
66
|
+
"""Same fix as the severity one, for the code detector's ``category`` default."""
|
|
67
|
+
pattern = (
|
|
68
|
+
r"(class CodeDetectorPipelineSchema\(.*?category: DetectorCategory \| None = Field\(\n\s+)"
|
|
69
|
+
r"'QUALITY'(\s*,)"
|
|
70
|
+
)
|
|
71
|
+
return re.sub(pattern, r"\1DetectorCategory.QUALITY\2", source, flags=re.DOTALL)
|
|
72
|
+
|
|
73
|
+
|
|
63
74
|
def run_codegen(input_file):
|
|
64
75
|
"""Generate Pydantic models from a single JSON schema file."""
|
|
65
76
|
cmd = [
|
|
@@ -107,6 +118,7 @@ def main():
|
|
|
107
118
|
content = run_codegen(detector_schema)
|
|
108
119
|
content = _patch_pipeline_type_defaults(content)
|
|
109
120
|
content = _patch_severity_enum_defaults(content)
|
|
121
|
+
content = _patch_category_enum_defaults(content)
|
|
110
122
|
(MODEL_DIR / "generated_detectors.py").write_text(content)
|
|
111
123
|
print("Wrote src/models/generated_detectors.py")
|
|
112
124
|
|
|
@@ -18,7 +18,6 @@ silently-degraded run visible.
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import asyncio
|
|
21
|
-
import base64
|
|
22
21
|
import json
|
|
23
22
|
import logging
|
|
24
23
|
from collections.abc import Mapping
|
|
@@ -29,6 +28,7 @@ from ..graph.edges import edge_from_payload
|
|
|
29
28
|
from ..notebook.child import ChildProcessError, NotebookChildProcess
|
|
30
29
|
from ..notebook.groups import warm_declared_groups
|
|
31
30
|
from ..notebook.packages import install as install_packages
|
|
31
|
+
from ..pipeline.asset_payload_server import AssetPayloadServer
|
|
32
32
|
from ..utils.hashing import unhash_id
|
|
33
33
|
from ..utils.urn import normalize_urn_or_none
|
|
34
34
|
from .contract import validate_augmentation_notebook
|
|
@@ -143,10 +143,6 @@ class AugmentationSession:
|
|
|
143
143
|
self._warnings: list[str] = []
|
|
144
144
|
self._warning_set: set[str] = set()
|
|
145
145
|
self.stats = AugmentationStats()
|
|
146
|
-
# Run-scoped memo of fetched payloads, keyed by fetch candidate id.
|
|
147
|
-
# Bounded: one in-flight asset's worth per entry, each capped at
|
|
148
|
-
# max_payload_bytes, dropped by evict_asset_cache().
|
|
149
|
-
self._memo: dict[str, dict[str, Any]] = {}
|
|
150
146
|
|
|
151
147
|
# -- construction ----------------------------------------------------------
|
|
152
148
|
|
|
@@ -295,7 +291,6 @@ class AugmentationSession:
|
|
|
295
291
|
slot.child.terminate()
|
|
296
292
|
except Exception:
|
|
297
293
|
pass
|
|
298
|
-
self._memo.clear()
|
|
299
294
|
|
|
300
295
|
# -- per asset -----------------------------------------------------------------
|
|
301
296
|
|
|
@@ -314,163 +309,34 @@ class AugmentationSession:
|
|
|
314
309
|
"mime_type": None,
|
|
315
310
|
}
|
|
316
311
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
312
|
+
@property
|
|
313
|
+
def _payloads(self) -> AssetPayloadServer:
|
|
314
|
+
"""The source's shared payload server.
|
|
315
|
+
|
|
316
|
+
Shared with the code detectors that run on the same asset later in the
|
|
317
|
+
scan, so a payload augmentation fetched is not fetched again (G1 R20).
|
|
318
|
+
"""
|
|
319
|
+
return AssetPayloadServer.shared(
|
|
320
|
+
self._source, max_payload_bytes=self._limits["max_payload_bytes"]
|
|
321
|
+
)
|
|
324
322
|
|
|
325
323
|
async def _serve_need(self, asset: Any, memo_key: str, frame: dict[str, Any]) -> Any:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return {"bytes_b64": None, "mime": None}
|
|
331
|
-
raw, mime = fetched
|
|
332
|
-
return {"bytes_b64": base64.b64encode(raw).decode("ascii"), "mime": mime}
|
|
333
|
-
if op == "raw_pages":
|
|
334
|
-
pages = await self._fetch_raw_pages(asset)
|
|
335
|
-
return {"pages": pages}
|
|
336
|
-
if op == "text":
|
|
337
|
-
pages = await self._fetch_text_pages(asset)
|
|
338
|
-
whole = "".join(pages)
|
|
339
|
-
return {"text": whole, "pages": pages}
|
|
340
|
-
raise ChildProcessError(f"Unknown payload op {op!r}")
|
|
324
|
+
try:
|
|
325
|
+
return await self._payloads.serve(asset, frame)
|
|
326
|
+
except ValueError as exc:
|
|
327
|
+
raise ChildProcessError(str(exc)) from exc
|
|
341
328
|
|
|
342
329
|
async def _fetch_payload(self, asset: Any) -> tuple[bytes, str] | None:
|
|
343
330
|
"""Raw bytes for file-shaped sources; None for row-shaped ones."""
|
|
344
|
-
|
|
345
|
-
if "payload" in memo:
|
|
346
|
-
cached = memo["payload"]
|
|
347
|
-
return cached if cached is None else (bytes(cached[0]), str(cached[1]))
|
|
348
|
-
fetch = getattr(self._source, "fetch_content_bytes", None)
|
|
349
|
-
result: tuple[bytes, str] | None = None
|
|
350
|
-
if callable(fetch):
|
|
351
|
-
for candidate in self._candidates(asset):
|
|
352
|
-
try:
|
|
353
|
-
result = await fetch(candidate)
|
|
354
|
-
except Exception as exc:
|
|
355
|
-
logger.debug("Augmentation payload fetch failed for %s: %s", candidate, exc)
|
|
356
|
-
continue
|
|
357
|
-
if result is not None:
|
|
358
|
-
break
|
|
359
|
-
if result is not None:
|
|
360
|
-
raw, mime = result
|
|
361
|
-
cap = self._limits["max_payload_bytes"]
|
|
362
|
-
if len(raw) > cap:
|
|
363
|
-
logger.debug("Augmentation payload for %s truncated to %d bytes", candidate, cap)
|
|
364
|
-
raw = raw[:cap]
|
|
365
|
-
result = (raw, mime)
|
|
366
|
-
self._remember_bytes(asset, raw, mime)
|
|
367
|
-
memo["payload"] = result
|
|
368
|
-
return result
|
|
331
|
+
return await self._payloads.payload(asset)
|
|
369
332
|
|
|
370
333
|
async def _fetch_raw_pages(self, asset: Any) -> list[str]:
|
|
371
334
|
"""The connector's own raw representation, page by page (capped)."""
|
|
372
|
-
|
|
373
|
-
if "raw_pages" in memo:
|
|
374
|
-
return [str(page) for page in memo["raw_pages"]]
|
|
375
|
-
pages: list[str] = []
|
|
376
|
-
fetch = getattr(self._source, "fetch_content_pages", None)
|
|
377
|
-
cap = self._limits["max_payload_bytes"]
|
|
378
|
-
used = 0
|
|
379
|
-
if callable(fetch):
|
|
380
|
-
for candidate in self._candidates(asset):
|
|
381
|
-
try:
|
|
382
|
-
async for raw, _text in fetch(candidate):
|
|
383
|
-
if not raw:
|
|
384
|
-
continue
|
|
385
|
-
chunk = str(raw)
|
|
386
|
-
if used + len(chunk) > cap:
|
|
387
|
-
chunk = chunk[: max(0, cap - used)]
|
|
388
|
-
if chunk:
|
|
389
|
-
pages.append(chunk)
|
|
390
|
-
used += len(chunk)
|
|
391
|
-
if used >= cap or len(pages) >= 50:
|
|
392
|
-
break
|
|
393
|
-
if pages:
|
|
394
|
-
break
|
|
395
|
-
except Exception as exc:
|
|
396
|
-
logger.debug("Augmentation raw fetch failed for %s: %s", candidate, exc)
|
|
397
|
-
continue
|
|
398
|
-
memo["raw_pages"] = pages
|
|
399
|
-
return pages
|
|
335
|
+
return await self._payloads.raw_pages(asset)
|
|
400
336
|
|
|
401
337
|
async def _fetch_text_pages(self, asset: Any) -> list[str]:
|
|
402
338
|
"""Extracted text, page by page (capped)."""
|
|
403
|
-
|
|
404
|
-
if "text_pages" in memo:
|
|
405
|
-
return [str(page) for page in memo["text_pages"]]
|
|
406
|
-
pages: list[str] = []
|
|
407
|
-
fetch = getattr(self._source, "fetch_content_pages", None)
|
|
408
|
-
cap = self._limits["max_payload_bytes"]
|
|
409
|
-
used = 0
|
|
410
|
-
if callable(fetch):
|
|
411
|
-
for candidate in self._candidates(asset):
|
|
412
|
-
try:
|
|
413
|
-
async for _raw, text in fetch(candidate):
|
|
414
|
-
if not text:
|
|
415
|
-
continue
|
|
416
|
-
chunk = str(text)
|
|
417
|
-
if used + len(chunk) > cap:
|
|
418
|
-
chunk = chunk[: max(0, cap - used)]
|
|
419
|
-
if chunk:
|
|
420
|
-
pages.append(chunk)
|
|
421
|
-
used += len(chunk)
|
|
422
|
-
if used >= cap or len(pages) >= 50:
|
|
423
|
-
break
|
|
424
|
-
if pages:
|
|
425
|
-
break
|
|
426
|
-
except Exception as exc:
|
|
427
|
-
logger.debug("Augmentation text fetch failed for %s: %s", candidate, exc)
|
|
428
|
-
continue
|
|
429
|
-
if not pages:
|
|
430
|
-
provider = getattr(self._source, "fetch_content_bytes", None)
|
|
431
|
-
if callable(provider):
|
|
432
|
-
fetched = await self._fetch_payload(asset)
|
|
433
|
-
if fetched is not None:
|
|
434
|
-
raw, mime = fetched
|
|
435
|
-
try:
|
|
436
|
-
text_pages = await asyncio.to_thread(
|
|
437
|
-
list, self._source.iter_asset_pages(raw, mime)
|
|
438
|
-
)
|
|
439
|
-
for page in text_pages[:50]:
|
|
440
|
-
chunk = str(page)
|
|
441
|
-
if used + len(chunk) > cap:
|
|
442
|
-
chunk = chunk[: max(0, cap - used)]
|
|
443
|
-
if chunk:
|
|
444
|
-
pages.append(chunk)
|
|
445
|
-
used += len(chunk)
|
|
446
|
-
if used >= cap:
|
|
447
|
-
break
|
|
448
|
-
except Exception as exc:
|
|
449
|
-
logger.debug("Augmentation text fallback failed: %s", exc)
|
|
450
|
-
memo["text_pages"] = pages
|
|
451
|
-
return pages
|
|
452
|
-
|
|
453
|
-
def _memo_for(self, asset: Any) -> dict[str, Any]:
|
|
454
|
-
key = str(getattr(asset, "hash", "") or "")
|
|
455
|
-
return self._memo.setdefault(key, {})
|
|
456
|
-
|
|
457
|
-
def _remember_bytes(self, asset: Any, raw: bytes, mime: str) -> None:
|
|
458
|
-
"""Mirror fetched bytes onto the source so the pipeline reuses them.
|
|
459
|
-
|
|
460
|
-
``ParsedContentProvider.fetch_bytes`` consults this memo first instead
|
|
461
|
-
of re-downloading — the fetch augmentation already paid for is not
|
|
462
|
-
paid twice. Registered under every candidate id the pipeline tries
|
|
463
|
-
(external URL, then hash), and dropped by ``evict`` at the end of the
|
|
464
|
-
asset.
|
|
465
|
-
"""
|
|
466
|
-
remember = getattr(self._source, "remember_augmentation_bytes", None)
|
|
467
|
-
if not callable(remember):
|
|
468
|
-
return
|
|
469
|
-
for candidate in self._candidates(asset):
|
|
470
|
-
try:
|
|
471
|
-
remember(candidate, raw, mime)
|
|
472
|
-
except Exception:
|
|
473
|
-
pass
|
|
339
|
+
return await self._payloads.text_pages(asset)
|
|
474
340
|
|
|
475
341
|
async def augment(self, asset: Any) -> Any:
|
|
476
342
|
"""Enrich one asset. Never raises: failure degrades to a warning."""
|
|
@@ -718,13 +584,4 @@ class AugmentationSession:
|
|
|
718
584
|
|
|
719
585
|
def evict(self, asset: Any) -> None:
|
|
720
586
|
"""Drop one asset's payload memo. Called alongside evict_asset_cache."""
|
|
721
|
-
self.
|
|
722
|
-
forget = getattr(self._source, "forget_augmentation_bytes", None)
|
|
723
|
-
if not callable(forget):
|
|
724
|
-
return
|
|
725
|
-
# The bytes were mirrored under every candidate id; drop them all.
|
|
726
|
-
for candidate in self._candidates(asset):
|
|
727
|
-
try:
|
|
728
|
-
forget(candidate)
|
|
729
|
-
except Exception:
|
|
730
|
-
pass
|
|
587
|
+
self._payloads.evict(asset)
|
|
@@ -13,6 +13,7 @@ from ._base import (
|
|
|
13
13
|
BaseRunner,
|
|
14
14
|
_resolve_pipeline_severity,
|
|
15
15
|
)
|
|
16
|
+
from ._custom_detector import CustomDetectorRunner
|
|
16
17
|
from ._factory import create_runner
|
|
17
18
|
from ._gliner2 import (
|
|
18
19
|
GLiNER2Runner,
|
|
@@ -35,6 +36,7 @@ __all__ = [
|
|
|
35
36
|
"_IMAGE_CONTENT_TYPES",
|
|
36
37
|
"_TEXT_CONTENT_TYPES",
|
|
37
38
|
"BaseRunner",
|
|
39
|
+
"CustomDetectorRunner",
|
|
38
40
|
"GLiNER2Runner",
|
|
39
41
|
"ImageClassificationRunner",
|
|
40
42
|
"LLMRunner",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Runner for the ``CODE_DETECTOR`` pipeline type: a code detector.
|
|
2
|
+
|
|
3
|
+
Like ``TAG`` it never runs over a page of text: ``detect()`` returns nothing and
|
|
4
|
+
no content type is advertised, so the text, binary and link passes never
|
|
5
|
+
schedule it and the worker pool never receives it. The detector pipeline
|
|
6
|
+
recognises it by this class and calls it once per asset, after every other
|
|
7
|
+
detector, through the :class:`CustomDetectorSession` it owns.
|
|
8
|
+
|
|
9
|
+
The session is created lazily on first use and bound to the scan's source,
|
|
10
|
+
because it needs the source to serve payload and holds child processes that
|
|
11
|
+
must not exist in the worker pool's processes.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from ....models.generated_detectors import CodeDetectorPipelineSchema, PipelineResult
|
|
19
|
+
from ....models.generated_single_asset_scan_results import DetectionResult
|
|
20
|
+
from ._base import BaseRunner
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CustomDetectorRunner(BaseRunner):
|
|
24
|
+
"""Asset-scoped runner: judged per asset by a notebook, never per page."""
|
|
25
|
+
|
|
26
|
+
asset_scoped = True
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
schema: CodeDetectorPipelineSchema,
|
|
31
|
+
detector_key: str = "",
|
|
32
|
+
detector_name: str = "",
|
|
33
|
+
) -> None:
|
|
34
|
+
self.schema = schema
|
|
35
|
+
self._detector_key = detector_key
|
|
36
|
+
self._detector_name = detector_name
|
|
37
|
+
self._session: Any = None
|
|
38
|
+
|
|
39
|
+
def run(self, text: str) -> PipelineResult:
|
|
40
|
+
return PipelineResult(metadata={"runner": "CODE_DETECTOR"})
|
|
41
|
+
|
|
42
|
+
def detect(self, content: str | bytes, content_type: str) -> list[DetectionResult]:
|
|
43
|
+
"""Never called per page. The pipeline calls ``session().detect(asset)``."""
|
|
44
|
+
return []
|
|
45
|
+
|
|
46
|
+
def get_supported_content_types(self) -> list[str]:
|
|
47
|
+
return []
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def deterministic(self) -> bool:
|
|
51
|
+
return self.schema.deterministic is not False
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def needs_findings(self) -> bool:
|
|
55
|
+
return bool(self.schema.needs_findings)
|
|
56
|
+
|
|
57
|
+
def session(self, source: Any) -> Any:
|
|
58
|
+
"""The run's session, created on first use."""
|
|
59
|
+
if self._session is None:
|
|
60
|
+
from ...custom_detector.session import CustomDetectorSession
|
|
61
|
+
|
|
62
|
+
self._session = CustomDetectorSession(
|
|
63
|
+
key=self._detector_key,
|
|
64
|
+
name=self._detector_name,
|
|
65
|
+
schema=self.schema,
|
|
66
|
+
source=source,
|
|
67
|
+
)
|
|
68
|
+
return self._session
|
|
69
|
+
|
|
70
|
+
def close(self) -> None:
|
|
71
|
+
if self._session is not None:
|
|
72
|
+
self._session.close()
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from ....models.generated_detectors import (
|
|
6
|
+
CodeDetectorPipelineSchema,
|
|
6
7
|
GLiNER2PipelineSchema,
|
|
7
8
|
ImageClassificationPipelineSchema,
|
|
8
9
|
LLMPipelineSchema,
|
|
@@ -12,6 +13,7 @@ from ....models.generated_detectors import (
|
|
|
12
13
|
TextClassificationPipelineSchema,
|
|
13
14
|
)
|
|
14
15
|
from ._base import BaseRunner
|
|
16
|
+
from ._custom_detector import CustomDetectorRunner
|
|
15
17
|
from ._gliner2 import GLiNER2Runner
|
|
16
18
|
from ._image_classification import ImageClassificationRunner
|
|
17
19
|
from ._llm import LLMRunner
|
|
@@ -30,6 +32,7 @@ def create_runner(
|
|
|
30
32
|
| ImageClassificationPipelineSchema
|
|
31
33
|
| ObjectDetectionPipelineSchema
|
|
32
34
|
| TagPipelineSchema
|
|
35
|
+
| CodeDetectorPipelineSchema
|
|
33
36
|
),
|
|
34
37
|
detector_key: str = "",
|
|
35
38
|
detector_name: str = "",
|
|
@@ -50,5 +53,8 @@ def create_runner(
|
|
|
50
53
|
# detector that is not supposed to run anything at all.
|
|
51
54
|
if isinstance(schema, TagPipelineSchema):
|
|
52
55
|
return TagRunner(schema, detector_key, detector_name)
|
|
56
|
+
# Same reason as TAG: a code detector must never fall through to GLiNER2.
|
|
57
|
+
if isinstance(schema, CodeDetectorPipelineSchema):
|
|
58
|
+
return CustomDetectorRunner(schema, detector_key, detector_name)
|
|
53
59
|
# GLiNER2PipelineSchema is the default / backward-compat path
|
|
54
60
|
return GLiNER2Runner(schema, detector_key, detector_name)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Code detectors: the ``CODE_DETECTOR`` pipeline type.
|
|
2
|
+
|
|
3
|
+
A notebook that defines ``detect(asset, ctx)`` and yields ``Finding`` objects,
|
|
4
|
+
run in an isolated child process once per asset, after every other detector.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .session import CustomDetectorSession, DetectOutcome
|
|
8
|
+
|
|
9
|
+
__all__ = ["CustomDetectorSession", "DetectOutcome"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""The interface a code detector's notebook must cover.
|
|
2
|
+
|
|
3
|
+
A code detector answers one question -- "what is wrong with this asset?" -- so
|
|
4
|
+
``detect(asset, ctx)`` is required and ``setup(ctx)`` optional. Checked at the
|
|
5
|
+
three moments the connector contract is: on save (the API's cheap check),
|
|
6
|
+
before a preview, and before every scan, because the recipe travels to a job
|
|
7
|
+
that has to defend itself.
|
|
8
|
+
|
|
9
|
+
Only the shape is checked, through the shared ``validate_module`` /
|
|
10
|
+
``validate_notebook``. Isolation is the child process and its scrubbed
|
|
11
|
+
environment, not anything an AST can decide.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from collections.abc import Iterable, Mapping
|
|
17
|
+
|
|
18
|
+
from ...notebook.contract import ContractReport, validate_module, validate_notebook
|
|
19
|
+
from ...notebook.serialize import ModuleSource
|
|
20
|
+
|
|
21
|
+
REQUIRED_FUNCTIONS = ("detect",)
|
|
22
|
+
|
|
23
|
+
#: ``setup(ctx)`` runs once per worker before the first asset: load a list,
|
|
24
|
+
#: a model, a lookup table into ``ctx.state``.
|
|
25
|
+
OPTIONAL_FUNCTIONS = ("setup",)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_detector_module(module: ModuleSource) -> ContractReport:
|
|
29
|
+
"""Parse the assembled module and check ``detect()`` exists."""
|
|
30
|
+
return validate_module(module, required=REQUIRED_FUNCTIONS)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def validate_detector_notebook(
|
|
34
|
+
cells: Iterable[Mapping[str, object] | object],
|
|
35
|
+
) -> ContractReport:
|
|
36
|
+
"""Validate a code detector's cells."""
|
|
37
|
+
return validate_notebook(cells, required=REQUIRED_FUNCTIONS)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"OPTIONAL_FUNCTIONS",
|
|
42
|
+
"REQUIRED_FUNCTIONS",
|
|
43
|
+
"validate_detector_module",
|
|
44
|
+
"validate_detector_notebook",
|
|
45
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Materialize a code detector's uploaded files for ``ctx.file(name)``.
|
|
2
|
+
|
|
3
|
+
Downloaded by the parent, never by the child: the child is started without the
|
|
4
|
+
API URL or the callback key, and handing it either would undo the isolation it
|
|
5
|
+
exists for. The API injects ``files_runtime`` -- one entry per file, with a
|
|
6
|
+
content hash and a download URL -- into the pipeline schema at dispatch.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import shutil
|
|
15
|
+
import tempfile
|
|
16
|
+
from collections.abc import Iterable, Mapping
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
from urllib.parse import quote
|
|
20
|
+
|
|
21
|
+
import requests
|
|
22
|
+
|
|
23
|
+
from ...outputs.base import DEFAULT_REST_TIMEOUT_SEC
|
|
24
|
+
from ...outputs.rest import INTERNAL_KEY_HEADER
|
|
25
|
+
from ...utils.source_files import CHUNK_BYTES, api_base_url
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
#: A directory prepared by the caller (the preview job's init container, a
|
|
30
|
+
#: test) wins over downloading.
|
|
31
|
+
FILES_DIR_ENV = "CLASSIFYRE_DETECTOR_FILES_DIR"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _safe_name(name: str) -> str:
|
|
35
|
+
base = Path(name).name or "file"
|
|
36
|
+
return re.sub(r"[^A-Za-z0-9._ -]", "_", base)[:200] or "file"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _entries(files_runtime: Iterable[Any] | None) -> list[dict[str, Any]]:
|
|
40
|
+
entries: list[dict[str, Any]] = []
|
|
41
|
+
for raw in files_runtime or []:
|
|
42
|
+
entry = raw.model_dump() if hasattr(raw, "model_dump") else raw
|
|
43
|
+
if isinstance(entry, Mapping) and entry.get("id") and entry.get("name"):
|
|
44
|
+
entries.append(dict(entry))
|
|
45
|
+
return entries
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def materialize_detector_files(
|
|
49
|
+
detector_id: str | None,
|
|
50
|
+
files_runtime: Iterable[Any] | None,
|
|
51
|
+
) -> Path | None:
|
|
52
|
+
"""Download every file into a fresh temp dir; None when there are none.
|
|
53
|
+
|
|
54
|
+
A file that fails to download is logged and skipped: a rule that reads one
|
|
55
|
+
list out of three should still see the two that arrived, and a rule that
|
|
56
|
+
needs the missing one fails loudly on ``ctx.file(name)`` with its name.
|
|
57
|
+
"""
|
|
58
|
+
prepared = os.environ.get(FILES_DIR_ENV)
|
|
59
|
+
if prepared and Path(prepared).is_dir():
|
|
60
|
+
return Path(prepared)
|
|
61
|
+
entries = _entries(files_runtime)
|
|
62
|
+
if not entries:
|
|
63
|
+
return None
|
|
64
|
+
destination = Path(tempfile.mkdtemp(prefix="classifyre-detector-files-"))
|
|
65
|
+
headers = {"Connection": "close"}
|
|
66
|
+
key = os.environ.get("CLASSIFYRE_INTERNAL_KEY", "").strip()
|
|
67
|
+
if key:
|
|
68
|
+
headers[INTERNAL_KEY_HEADER] = key
|
|
69
|
+
written = 0
|
|
70
|
+
with requests.Session() as session:
|
|
71
|
+
for entry in entries:
|
|
72
|
+
url = str(entry.get("url") or "").strip()
|
|
73
|
+
if not url:
|
|
74
|
+
if not detector_id:
|
|
75
|
+
continue
|
|
76
|
+
url = (
|
|
77
|
+
f"{api_base_url()}/custom-detectors/{quote(detector_id, safe='')}"
|
|
78
|
+
f"/files/{quote(str(entry['id']), safe='')}/content"
|
|
79
|
+
)
|
|
80
|
+
target = destination / _safe_name(str(entry["name"]))
|
|
81
|
+
try:
|
|
82
|
+
with session.get(
|
|
83
|
+
url, headers=headers, stream=True, timeout=(30, DEFAULT_REST_TIMEOUT_SEC)
|
|
84
|
+
) as response:
|
|
85
|
+
response.raise_for_status()
|
|
86
|
+
with target.open("wb") as handle:
|
|
87
|
+
for chunk in response.iter_content(chunk_size=CHUNK_BYTES):
|
|
88
|
+
if chunk:
|
|
89
|
+
handle.write(chunk)
|
|
90
|
+
except (requests.RequestException, OSError) as exc:
|
|
91
|
+
logger.warning("Could not download detector file %s: %s", entry.get("name"), exc)
|
|
92
|
+
target.unlink(missing_ok=True)
|
|
93
|
+
continue
|
|
94
|
+
written += 1
|
|
95
|
+
if not written:
|
|
96
|
+
shutil.rmtree(destination, ignore_errors=True)
|
|
97
|
+
return None
|
|
98
|
+
logger.info("Made %d detector file(s) available to ctx.files", written)
|
|
99
|
+
return destination
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
__all__ = ["FILES_DIR_ENV", "materialize_detector_files"]
|