classifyre-cli 0.6.13__tar.gz → 0.6.15__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.15/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/PKG-INFO +1 -1
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/package.json +1 -1
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/pyproject.toml +1 -1
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/media_artifact.py +2 -2
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/git/source.py +338 -93
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/transcription.py +3 -1
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_git_source.py +215 -1
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_transcription.py +38 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/uv.lock +242 -206
- classifyre_cli-0.6.13/.turbo/turbo-build.log +0 -3
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/.gitignore +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/.python-version +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/README.md +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/main.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/scripts/generate_runtime_packages.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/scripts/generate_sdk_completions.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/augmentation/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/augmentation/contract.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/augmentation/preview.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/augmentation/runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/augmentation/sdk.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/augmentation/session.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/config.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/config.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_custom_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_decision.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_provider_call.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_tag.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/contract.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/files.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/preview.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/results.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/sdk.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/custom_detector/session.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/engine_version.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/errors.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/file_evaluation/asset_fixture.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/file_evaluation/runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/graph/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/graph/edges.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/main.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/models/generated_detectors.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/models/generated_input.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/models/generated_single_asset_scan_results.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/child.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/cli.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/cohort.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/contract.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/execute.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/files.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/groups.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/outputs.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/packages.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/protocol.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/redact.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/scaffold.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/sdk.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/notebook/serialize.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/outputs/base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/outputs/console.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/outputs/file.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/outputs/rest.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/asset_payload_server.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/detector_breaker.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/detector_pipeline.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/finding_context.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/parsed_content_provider.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/payload_window.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/scan_cache.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/custom/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/custom/env.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/custom/runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/custom/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/dremio/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/dremio/client.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/dremio/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/git/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/hugging_face/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/hugging_face/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/object_storage/base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/reddit/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/reddit/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/sources/youtube/source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/telemetry.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/dependency_groups.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/embedded_files.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/external_video.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/file_parser.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/payload.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/range_reader.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/redaction.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/resources.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/sampling_cursor.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/sampling_frontier.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/source_files.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/sql_lineage.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/torch_runtime.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/urn.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/validation.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/conftest.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_decision_runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_custom_detector_session.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/test_tag_runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/pipeline/test_detector_breaker.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/pipeline/test_detector_pipeline.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_arrow_ipc_payloads.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_augmentation_contract.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_augmentation_edges.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_augmentation_patch.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_augmentation_scan_cache.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_augmentation_sdk.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_augmentation_session.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_augmentation_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_automatic_new_first.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_byte_valued_fields.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_config.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_detector_end_to_end.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_detector_evaluation.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_detector_notebook.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_detector_templates.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_source_relationships.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_custom_source_tags_end_to_end.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_dependency_groups.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_detector_scope.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_dremio_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_finding_context.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_graph_edges.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_hugging_face_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_lineage_loss_reporting.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_main.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_media_embedding.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_asset_entity.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_asset_extract.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_asset_tags.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_cohort.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_execute.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_files.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_groups.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_log_levels.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_packages.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_query_assets.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_sdk_exports.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_serialize.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notebook_templates.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_object_storage_download_efficiency.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_payload_window.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_payload_window_parquet.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_range_reading.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_reddit_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_rest_backpressure.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_runtime_packages.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_s3_compatible_storage_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_sampling_cursor.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_sampling_frontier.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_scan_cache.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_sdk_completions.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_secret_redaction.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_semantic_declarations.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_source_strategy_conformance.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_sql_lineage.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_tabular_lineage.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_torch_runtime.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_urn.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_view_dependencies_are_not_foreign_keys.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_embedded_files.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_file_parser.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_payload.py +0 -0
- {classifyre_cli-0.6.13 → classifyre_cli-0.6.15}/tests/utils/test_video_processing.py +0 -0
|
@@ -58,7 +58,7 @@ class MediaSpec:
|
|
|
58
58
|
max_edge: int = 768
|
|
59
59
|
quality: int = 85
|
|
60
60
|
sample_rate: int = 16_000
|
|
61
|
-
clip_seconds: int =
|
|
61
|
+
clip_seconds: int = 10
|
|
62
62
|
cache_key: str = ""
|
|
63
63
|
# Changes when the way this kind of media is prepared changes.
|
|
64
64
|
fingerprint: str = ""
|
|
@@ -84,7 +84,7 @@ class MediaSpec:
|
|
|
84
84
|
max_edge=number("maxEdge", 768, 32),
|
|
85
85
|
quality=min(100, number("quality", 85, 1)),
|
|
86
86
|
sample_rate=number("sampleRate", 16_000, 8_000),
|
|
87
|
-
clip_seconds=number("clipSeconds",
|
|
87
|
+
clip_seconds=number("clipSeconds", 10, 1),
|
|
88
88
|
cache_key=str(raw.get("cacheKey") or f"{MEDIA_CACHE_KEY_PREFIX}{modality}"),
|
|
89
89
|
fingerprint=str(raw.get("fingerprint") or ""),
|
|
90
90
|
)
|
|
@@ -35,6 +35,7 @@ import shutil
|
|
|
35
35
|
import stat
|
|
36
36
|
import subprocess
|
|
37
37
|
import tempfile
|
|
38
|
+
import time
|
|
38
39
|
from collections.abc import AsyncGenerator, Iterator
|
|
39
40
|
from dataclasses import dataclass
|
|
40
41
|
from datetime import UTC, datetime
|
|
@@ -77,6 +78,57 @@ _DEFAULT_TOKEN_USERNAME = "git"
|
|
|
77
78
|
# no scheme and from a Windows path by requiring the @.
|
|
78
79
|
_SCP_STYLE = re.compile(r"^(?P<user>[^/@]+)@(?P<host>[^/:]+):(?P<path>.+)$")
|
|
79
80
|
|
|
81
|
+
# How many times a single git invocation is attempted before the scan gives up
|
|
82
|
+
# on it. Transient network and promisor failures are retried with exponential
|
|
83
|
+
# backoff, so a blip costs seconds instead of the whole run. An operational
|
|
84
|
+
# knob rather than per-source config (like CLASSIFYRE_GIT_BINARY):
|
|
85
|
+
# CLASSIFYRE_GIT_MAX_ATTEMPTS.
|
|
86
|
+
_DEFAULT_GIT_MAX_ATTEMPTS = 3
|
|
87
|
+
_GIT_RETRY_BASE_DELAY_SECONDS = 2.0
|
|
88
|
+
_GIT_RETRY_MAX_DELAY_SECONDS = 30.0
|
|
89
|
+
|
|
90
|
+
# Substrings (matched lowercase) of git stderr that mean "try again" rather
|
|
91
|
+
# than "misconfigured". Kept to failures outside the scan's control: dropped
|
|
92
|
+
# connections, overloaded or unreachable servers, and a promisor remote that
|
|
93
|
+
# did not hand over an object on this attempt. Authentication failures, missing
|
|
94
|
+
# branches, unknown repositories and bad URLs fail immediately.
|
|
95
|
+
_TRANSIENT_GIT_ERROR_PATTERNS = (
|
|
96
|
+
"from promisor remote",
|
|
97
|
+
"lazy fetching disabled",
|
|
98
|
+
"connection reset",
|
|
99
|
+
"connection was reset",
|
|
100
|
+
"connection timed out",
|
|
101
|
+
"operation timed out",
|
|
102
|
+
"timed out",
|
|
103
|
+
"transfer closed",
|
|
104
|
+
"early eof",
|
|
105
|
+
"rpc failed",
|
|
106
|
+
"http 502",
|
|
107
|
+
"http 503",
|
|
108
|
+
"http 504",
|
|
109
|
+
"bad gateway",
|
|
110
|
+
"service unavailable",
|
|
111
|
+
"gateway timeout",
|
|
112
|
+
"could not resolve host",
|
|
113
|
+
"temporary failure in name resolution",
|
|
114
|
+
"network is unreachable",
|
|
115
|
+
"no route to host",
|
|
116
|
+
"connection refused",
|
|
117
|
+
"the remote end hung up",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Substrings (matched lowercase) showing the clone arrived without an object
|
|
121
|
+
# the listing needs. Unlike a transient failure, retrying the same command
|
|
122
|
+
# cannot help — the missing objects have to be fetched first.
|
|
123
|
+
_MISSING_OBJECT_PATTERNS = (
|
|
124
|
+
"from promisor remote",
|
|
125
|
+
"lazy fetching disabled",
|
|
126
|
+
"did not find object",
|
|
127
|
+
"unable to read",
|
|
128
|
+
"bad object",
|
|
129
|
+
"missing object",
|
|
130
|
+
)
|
|
131
|
+
|
|
80
132
|
|
|
81
133
|
class GitCommandError(RuntimeError):
|
|
82
134
|
"""A git invocation failed, with its stderr already redacted for logging."""
|
|
@@ -538,6 +590,37 @@ class GitSource(ObjectStorageSourceBase):
|
|
|
538
590
|
cleaned = cleaned.replace(secret, "***")
|
|
539
591
|
return cleaned
|
|
540
592
|
|
|
593
|
+
def _git_max_attempts(self) -> int:
|
|
594
|
+
"""How many times a git invocation is attempted before giving up.
|
|
595
|
+
|
|
596
|
+
``CLASSIFYRE_GIT_MAX_ATTEMPTS`` overrides the default; anything that
|
|
597
|
+
is not a positive integer means the default. Read per invocation so a
|
|
598
|
+
long-lived process picks up a changed value without restarting.
|
|
599
|
+
"""
|
|
600
|
+
try:
|
|
601
|
+
raw = os.environ.get("CLASSIFYRE_GIT_MAX_ATTEMPTS") or _DEFAULT_GIT_MAX_ATTEMPTS
|
|
602
|
+
return max(1, int(raw))
|
|
603
|
+
except (TypeError, ValueError):
|
|
604
|
+
return _DEFAULT_GIT_MAX_ATTEMPTS
|
|
605
|
+
|
|
606
|
+
@staticmethod
|
|
607
|
+
def _is_transient_git_error(message: str) -> bool:
|
|
608
|
+
"""Whether this failure is worth retrying rather than reporting."""
|
|
609
|
+
lowered = message.lower()
|
|
610
|
+
return any(pattern in lowered for pattern in _TRANSIENT_GIT_ERROR_PATTERNS)
|
|
611
|
+
|
|
612
|
+
@staticmethod
|
|
613
|
+
def _is_missing_object_error(message: str) -> bool:
|
|
614
|
+
"""Whether the clone is truncated rather than the network broken."""
|
|
615
|
+
lowered = message.lower()
|
|
616
|
+
return any(pattern in lowered for pattern in _MISSING_OBJECT_PATTERNS)
|
|
617
|
+
|
|
618
|
+
@staticmethod
|
|
619
|
+
def _git_retry_delay(attempt: int) -> float:
|
|
620
|
+
return min(
|
|
621
|
+
_GIT_RETRY_BASE_DELAY_SECONDS * (2.0 ** (attempt - 1)), _GIT_RETRY_MAX_DELAY_SECONDS
|
|
622
|
+
)
|
|
623
|
+
|
|
541
624
|
def _run_git(
|
|
542
625
|
self,
|
|
543
626
|
args: list[str],
|
|
@@ -545,29 +628,54 @@ class GitSource(ObjectStorageSourceBase):
|
|
|
545
628
|
cwd: Path | None = None,
|
|
546
629
|
timeout: float | None = None,
|
|
547
630
|
) -> bytes:
|
|
631
|
+
"""Run one git command, retrying transient failures with backoff.
|
|
632
|
+
|
|
633
|
+
A dropped connection or a promisor hiccup therefore costs seconds,
|
|
634
|
+
not the scan. Anything that retrying cannot fix — bad credentials, a
|
|
635
|
+
missing branch, an unknown repository — fails on the first attempt.
|
|
636
|
+
"""
|
|
548
637
|
command = [self._git_binary(), *args]
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
638
|
+
limit = timeout or self._command_timeout_seconds()
|
|
639
|
+
attempts = self._git_max_attempts()
|
|
640
|
+
error: GitCommandError | None = None
|
|
641
|
+
for attempt in range(1, attempts + 1):
|
|
642
|
+
try:
|
|
643
|
+
completed = subprocess.run(
|
|
644
|
+
command,
|
|
645
|
+
cwd=str(cwd) if cwd else None,
|
|
646
|
+
env=self._git_env(),
|
|
647
|
+
capture_output=True,
|
|
648
|
+
timeout=limit,
|
|
649
|
+
check=False,
|
|
650
|
+
)
|
|
651
|
+
except FileNotFoundError as exc: # pragma: no cover - environment issue
|
|
652
|
+
raise GitCommandError(
|
|
653
|
+
"The git executable was not found on this runner. Git scanning requires git "
|
|
654
|
+
"to be installed and on PATH."
|
|
655
|
+
) from exc
|
|
656
|
+
except subprocess.TimeoutExpired as exc:
|
|
657
|
+
error = GitCommandError(f"git {args[0]} timed out after {exc.timeout:.0f}s")
|
|
658
|
+
else:
|
|
659
|
+
if completed.returncode == 0:
|
|
660
|
+
return completed.stdout
|
|
661
|
+
stderr = self._redact(completed.stderr.decode("utf-8", errors="replace").strip())
|
|
662
|
+
error = GitCommandError(f"git {args[0]} failed: {stderr or completed.returncode}")
|
|
663
|
+
|
|
664
|
+
assert error is not None
|
|
665
|
+
if attempt >= attempts or self._aborted or not self._is_transient_git_error(str(error)):
|
|
666
|
+
raise error
|
|
667
|
+
delay = self._git_retry_delay(attempt)
|
|
668
|
+
logger.warning(
|
|
669
|
+
"git %s failed transiently (attempt %d/%d), retrying in %.0fs: %s",
|
|
670
|
+
args[0],
|
|
671
|
+
attempt,
|
|
672
|
+
attempts,
|
|
673
|
+
delay,
|
|
674
|
+
self._redact(str(error)),
|
|
557
675
|
)
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
"to be installed and on PATH."
|
|
562
|
-
) from exc
|
|
563
|
-
except subprocess.TimeoutExpired as exc:
|
|
564
|
-
raise GitCommandError(f"git {args[0]} timed out after {exc.timeout:.0f}s") from exc
|
|
565
|
-
|
|
566
|
-
if completed.returncode != 0:
|
|
567
|
-
stderr = self._redact(completed.stderr.decode("utf-8", errors="replace").strip())
|
|
568
|
-
raise GitCommandError(f"git {args[0]} failed: {stderr or completed.returncode}")
|
|
569
|
-
|
|
570
|
-
return completed.stdout
|
|
676
|
+
time.sleep(delay)
|
|
677
|
+
assert error is not None
|
|
678
|
+
raise error
|
|
571
679
|
|
|
572
680
|
@staticmethod
|
|
573
681
|
def _git_binary() -> str:
|
|
@@ -649,77 +757,183 @@ class GitSource(ObjectStorageSourceBase):
|
|
|
649
757
|
|
|
650
758
|
# ── listing ──────────────────────────────────────────────────────────
|
|
651
759
|
|
|
652
|
-
def
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
760
|
+
def _list_tree_raw(self, repo: Path) -> bytes:
|
|
761
|
+
"""The raw ``ls-tree`` output for the scanned commit.
|
|
762
|
+
|
|
763
|
+
Deliberately without ``-l``: the long format needs every blob's size,
|
|
764
|
+
which sends a filtered clone back to the promisor for each file it
|
|
765
|
+
deliberately left on the server — one failed lazy fetch and the whole
|
|
766
|
+
scan dies. The plain format only walks commits and trees, which a
|
|
767
|
+
filtered clone always carries, so listing never touches the network.
|
|
768
|
+
Sizes come from ``_batch_blob_sizes`` instead.
|
|
769
|
+
"""
|
|
770
|
+
args = ["ls-tree", "-r", "-z", "--full-tree", "HEAD"]
|
|
771
|
+
limit = self._command_timeout_seconds()
|
|
772
|
+
try:
|
|
773
|
+
return self._run_git(args, cwd=repo, timeout=limit)
|
|
774
|
+
except GitCommandError as exc:
|
|
775
|
+
if not self._is_missing_object_error(str(exc)) or not self._commit_sha:
|
|
776
|
+
raise
|
|
777
|
+
# The clone arrived truncated: a tree the listing needs never made
|
|
778
|
+
# it. The partial-clone filter stays recorded in the repo config,
|
|
779
|
+
# so fetching the scanned commit completes exactly the missing
|
|
780
|
+
# trees without pulling the blobs the strategy left behind.
|
|
781
|
+
logger.warning(
|
|
782
|
+
"Tree objects missing after clone; fetching %s to complete it",
|
|
783
|
+
self._commit_sha[:12],
|
|
784
|
+
)
|
|
785
|
+
self._run_git(
|
|
786
|
+
["fetch", "--no-tags", "origin", self._commit_sha], cwd=repo, timeout=limit
|
|
787
|
+
)
|
|
788
|
+
return self._run_git(args, cwd=repo, timeout=limit)
|
|
789
|
+
|
|
790
|
+
def _batch_blob_sizes(self, repo: Path, oids: list[str]) -> dict[str, int]:
|
|
791
|
+
"""The locally-known blob sizes, read straight out of the pack files.
|
|
792
|
+
|
|
793
|
+
``verify-pack -v`` lists every object the clone actually holds, with
|
|
794
|
+
its size, from purely local data: unlike ``ls-tree -l`` or
|
|
795
|
+
``cat-file --batch-check`` it can neither stall the listing on the
|
|
796
|
+
network nor fail it. Older git versions fetch a missing blob just to
|
|
797
|
+
report its size, and one transient promisor failure then fails the
|
|
798
|
+
whole command — exactly the SCAN FAILED this replaces. A blob with no
|
|
799
|
+
pack entry is one this clone deliberately left on the server (or has
|
|
800
|
+
not fetched yet); its size stays unknown rather than being guessed at.
|
|
801
|
+
|
|
802
|
+
Anything unexpected — a failed process, an unparsable line — degrades
|
|
803
|
+
to unknown rather than failing the scan: an unmeasured file is still
|
|
804
|
+
scanned, just spooled to disk instead of held in memory.
|
|
805
|
+
"""
|
|
806
|
+
unique = list(dict.fromkeys(oids))
|
|
807
|
+
if not unique or self._clone_strategy() == "blobless":
|
|
808
|
+
return {}
|
|
809
|
+
wanted = set(unique)
|
|
810
|
+
sizes: dict[str, int] = {}
|
|
811
|
+
try:
|
|
812
|
+
pack_dir = repo / ".git" / "objects" / "pack"
|
|
813
|
+
indexes = sorted(pack_dir.glob("*.idx")) if pack_dir.is_dir() else []
|
|
814
|
+
for index in indexes:
|
|
815
|
+
if len(sizes) >= len(wanted):
|
|
816
|
+
break
|
|
817
|
+
self._accumulate_pack_sizes(repo, index, wanted, sizes)
|
|
818
|
+
for oid in unique:
|
|
819
|
+
if oid in sizes:
|
|
820
|
+
continue
|
|
821
|
+
# Fresh clones keep everything in packs, but a blob kept as a
|
|
822
|
+
# loose object is still held locally — and asking for the size
|
|
823
|
+
# of an object that exists cannot trigger a fetch.
|
|
824
|
+
if (repo / ".git" / "objects" / oid[:2] / oid[2:]).is_file():
|
|
825
|
+
try:
|
|
826
|
+
out = self._run_git(["cat-file", "-s", oid], cwd=repo)
|
|
827
|
+
sizes[oid] = int(out.decode("utf-8", errors="replace").strip())
|
|
828
|
+
except (GitCommandError, ValueError):
|
|
829
|
+
continue
|
|
830
|
+
except OSError as exc:
|
|
831
|
+
logger.warning("Could not read blob sizes, treating every file as unmeasured: %s", exc)
|
|
832
|
+
return sizes
|
|
833
|
+
|
|
834
|
+
def _accumulate_pack_sizes(
|
|
835
|
+
self, repo: Path, index: Path, wanted: set[str], sizes: dict[str, int]
|
|
836
|
+
) -> None:
|
|
837
|
+
"""Add the wanted blobs found in one pack index to ``sizes``."""
|
|
838
|
+
try:
|
|
839
|
+
completed = subprocess.run(
|
|
840
|
+
[self._git_binary(), "verify-pack", "-v", str(index)],
|
|
841
|
+
cwd=str(repo),
|
|
842
|
+
env=self._git_env(),
|
|
843
|
+
capture_output=True,
|
|
844
|
+
timeout=self._command_timeout_seconds(),
|
|
845
|
+
check=False,
|
|
846
|
+
)
|
|
847
|
+
except (subprocess.TimeoutExpired, OSError) as exc:
|
|
848
|
+
logger.warning("Could not read blob sizes, treating every file as unmeasured: %s", exc)
|
|
849
|
+
return
|
|
669
850
|
if completed.returncode != 0:
|
|
670
851
|
stderr = self._redact(completed.stderr.decode("utf-8", errors="replace").strip())
|
|
671
|
-
|
|
852
|
+
logger.warning(
|
|
853
|
+
"Could not read blob sizes, treating every file as unmeasured: %s",
|
|
854
|
+
stderr or completed.returncode,
|
|
855
|
+
)
|
|
856
|
+
return
|
|
857
|
+
for line in completed.stdout.decode("utf-8", errors="replace").splitlines():
|
|
858
|
+
parts = line.split()
|
|
859
|
+
# "<oid> blob <size> ..." for a blob held locally; pack headers,
|
|
860
|
+
# trees, commits and statistics lines never match and are skipped.
|
|
861
|
+
if len(parts) >= 3 and parts[1] == "blob" and parts[2].isdigit() and parts[0] in wanted:
|
|
862
|
+
sizes[parts[0]] = int(parts[2])
|
|
863
|
+
if len(sizes) >= len(wanted):
|
|
864
|
+
return
|
|
865
|
+
|
|
866
|
+
def _list_objects(self) -> Iterator[ObjectRef]:
|
|
867
|
+
repo = self._repo()
|
|
868
|
+
raw = self._list_tree_raw(repo)
|
|
869
|
+
|
|
870
|
+
candidates: list[tuple[str, str]] = [] # (blob object id, repo-relative path)
|
|
871
|
+
for entry in raw.decode("utf-8", errors="replace").split("\0"):
|
|
872
|
+
if self._aborted:
|
|
873
|
+
return
|
|
874
|
+
parsed = self._split_tree_entry(entry)
|
|
875
|
+
if parsed is None:
|
|
876
|
+
continue
|
|
877
|
+
_mode, object_type, oid, path = parsed
|
|
878
|
+
# Submodules are recorded as a commit pointer, not content; symlinks
|
|
879
|
+
# point at a path that has no meaning outside a checked-out worktree.
|
|
880
|
+
if object_type != "blob" or _mode == "120000":
|
|
881
|
+
continue
|
|
882
|
+
cleaned = path.strip().lstrip("/")
|
|
883
|
+
if not cleaned:
|
|
884
|
+
continue
|
|
885
|
+
if not self._path_in_scope(cleaned):
|
|
886
|
+
continue
|
|
887
|
+
if not self._object_matches_extension_filters(cleaned):
|
|
888
|
+
continue
|
|
889
|
+
candidates.append((oid, cleaned))
|
|
890
|
+
|
|
891
|
+
sizes = self._batch_blob_sizes(repo, [oid for oid, _ in candidates])
|
|
672
892
|
|
|
673
893
|
commit_time = self._commit_time()
|
|
674
894
|
commit_times = self._last_commit_times() if self._include_last_commit() else {}
|
|
675
895
|
self._lfs_globs = self._lfs_tracked_patterns()
|
|
676
896
|
|
|
677
|
-
for
|
|
897
|
+
for oid, path in candidates:
|
|
678
898
|
if self._aborted:
|
|
679
899
|
return
|
|
680
|
-
ref = self.
|
|
900
|
+
ref = self._make_object_ref(path, oid, sizes.get(oid), commit_time, commit_times)
|
|
681
901
|
if ref is None:
|
|
682
902
|
continue
|
|
683
903
|
self._ref_by_key[ref.key] = ref
|
|
684
904
|
yield ref
|
|
685
905
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
commit_time: datetime,
|
|
690
|
-
commit_times: dict[str, datetime],
|
|
691
|
-
) -> GitObjectRef | None:
|
|
906
|
+
@staticmethod
|
|
907
|
+
def _split_tree_entry(entry: str) -> tuple[str, str, str, str] | None:
|
|
908
|
+
"""Split one NUL-separated ``ls-tree`` record into mode, type, id, path."""
|
|
692
909
|
if not entry.strip() or "\t" not in entry:
|
|
693
910
|
return None
|
|
694
911
|
info, path = entry.split("\t", 1)
|
|
695
912
|
fields = info.split()
|
|
696
|
-
if len(fields) <
|
|
697
|
-
return None
|
|
698
|
-
mode, object_type, oid, raw_size = fields[0], fields[1], fields[2], fields[3]
|
|
699
|
-
|
|
700
|
-
# Submodules are recorded as a commit pointer, not content; symlinks
|
|
701
|
-
# point at a path that has no meaning outside a checked-out worktree.
|
|
702
|
-
if object_type != "blob" or mode == "120000":
|
|
913
|
+
if len(fields) < 3:
|
|
703
914
|
return None
|
|
915
|
+
return fields[0], fields[1], fields[2], path
|
|
704
916
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
#
|
|
714
|
-
#
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
917
|
+
def _make_object_ref(
|
|
918
|
+
self,
|
|
919
|
+
path: str,
|
|
920
|
+
oid: str,
|
|
921
|
+
size: int | None,
|
|
922
|
+
commit_time: datetime,
|
|
923
|
+
commit_times: dict[str, datetime],
|
|
924
|
+
) -> GitObjectRef | None:
|
|
925
|
+
# A size of None is a blob this clone does not hold — exactly the
|
|
926
|
+
# files a filtered clone deliberately left on the server. Git cannot
|
|
927
|
+
# report the size of an object it does not have, so it stays 0 until
|
|
928
|
+
# the file is read rather than being guessed at.
|
|
929
|
+
size_known = size is not None
|
|
930
|
+
resolved = size or 0
|
|
931
|
+
if size_known and resolved == 0 and not self._include_empty_objects():
|
|
718
932
|
return None
|
|
719
933
|
|
|
720
934
|
return GitObjectRef(
|
|
721
935
|
key=path,
|
|
722
|
-
size=
|
|
936
|
+
size=resolved,
|
|
723
937
|
last_modified=commit_times.get(path, commit_time),
|
|
724
938
|
etag=oid,
|
|
725
939
|
blob_id=oid,
|
|
@@ -810,37 +1024,68 @@ class GitSource(ObjectStorageSourceBase):
|
|
|
810
1024
|
large repository stay small. The blob is written into the per-run object
|
|
811
1025
|
store, so a second read of the same file costs nothing and everything is
|
|
812
1026
|
discarded together at the end of the run.
|
|
1027
|
+
|
|
1028
|
+
A fetch that fails before producing a single byte is retried: that is
|
|
1029
|
+
the promisor failing the handshake, not the file being unreadable.
|
|
1030
|
+
Once bytes have flowed the stream is not restarted — the consumer
|
|
1031
|
+
already holds a prefix, and a second stream would duplicate it.
|
|
813
1032
|
"""
|
|
814
1033
|
oid = self._blob_id(ref)
|
|
815
1034
|
if oid == _EMPTY_BLOB_OID:
|
|
816
1035
|
return
|
|
817
1036
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
break
|
|
831
|
-
yield chunk
|
|
832
|
-
finally:
|
|
1037
|
+
attempts = self._git_max_attempts()
|
|
1038
|
+
yielded = 0
|
|
1039
|
+
for attempt in range(1, attempts + 1):
|
|
1040
|
+
process = subprocess.Popen(
|
|
1041
|
+
[self._git_binary(), "cat-file", "blob", oid],
|
|
1042
|
+
cwd=str(self._repo()),
|
|
1043
|
+
env=self._git_env(),
|
|
1044
|
+
stdout=subprocess.PIPE,
|
|
1045
|
+
stderr=subprocess.PIPE,
|
|
1046
|
+
)
|
|
1047
|
+
assert process.stdout is not None
|
|
1048
|
+
outcome: GitCommandError | None = None
|
|
833
1049
|
try:
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1050
|
+
while True:
|
|
1051
|
+
chunk = process.stdout.read(_CHUNK_BYTES)
|
|
1052
|
+
if not chunk:
|
|
1053
|
+
break
|
|
1054
|
+
yielded += len(chunk)
|
|
1055
|
+
yield chunk
|
|
1056
|
+
finally:
|
|
1057
|
+
try:
|
|
1058
|
+
process.stdout.close()
|
|
1059
|
+
except Exception: # pragma: no cover - close is best effort
|
|
1060
|
+
pass
|
|
1061
|
+
stderr = process.stderr.read() if process.stderr else b""
|
|
1062
|
+
if process.stderr is not None:
|
|
1063
|
+
process.stderr.close()
|
|
1064
|
+
# Recorded, not raised: raising or returning from inside a
|
|
1065
|
+
# finally would silence the exception the stream ended with.
|
|
1066
|
+
if process.wait() != 0:
|
|
1067
|
+
message = self._redact(stderr.decode("utf-8", errors="replace").strip())
|
|
1068
|
+
outcome = GitCommandError(
|
|
1069
|
+
f"Could not read {ref.key} from Git: {message or process.returncode}"
|
|
1070
|
+
)
|
|
1071
|
+
if outcome is None:
|
|
1072
|
+
return
|
|
1073
|
+
if (
|
|
1074
|
+
yielded > 0
|
|
1075
|
+
or attempt >= attempts
|
|
1076
|
+
or self._aborted
|
|
1077
|
+
or not self._is_transient_git_error(str(outcome))
|
|
1078
|
+
):
|
|
1079
|
+
raise outcome
|
|
1080
|
+
delay = self._git_retry_delay(attempt)
|
|
1081
|
+
logger.warning(
|
|
1082
|
+
"Retrying read of %s after transient Git failure (attempt %d/%d): %s",
|
|
1083
|
+
ref.key,
|
|
1084
|
+
attempt,
|
|
1085
|
+
attempts,
|
|
1086
|
+
outcome,
|
|
1087
|
+
)
|
|
1088
|
+
time.sleep(delay)
|
|
844
1089
|
|
|
845
1090
|
def _download_object(self, ref: ObjectRef) -> tuple[bytes, str | None]:
|
|
846
1091
|
"""Every byte of one file.
|
|
@@ -249,7 +249,9 @@ def _split_audio_chunks(
|
|
|
249
249
|
frames if isinstance(frames, list) else ([frames] if frames is not None else [])
|
|
250
250
|
)
|
|
251
251
|
for out_frame in result:
|
|
252
|
-
|
|
252
|
+
# The plane's buffer is padded past its samples (832 bytes for
|
|
253
|
+
# 355 of them); the padding is not audio.
|
|
254
|
+
current.extend(bytes(out_frame.planes[0])[: out_frame.samples * 2])
|
|
253
255
|
while len(current) >= bytes_per_chunk:
|
|
254
256
|
yield _pcm_to_wav(bytes(current[:bytes_per_chunk]), _TARGET_SAMPLE_RATE)
|
|
255
257
|
del current[:bytes_per_chunk]
|