classifyre-cli 0.6.14__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.
Files changed (393) hide show
  1. classifyre_cli-0.6.15/.turbo/turbo-build.log +3 -0
  2. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/PKG-INFO +1 -1
  3. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/package.json +1 -1
  4. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/pyproject.toml +1 -1
  5. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/media_artifact.py +2 -2
  6. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/git/source.py +338 -93
  7. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/transcription.py +3 -1
  8. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_git_source.py +215 -1
  9. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_transcription.py +38 -0
  10. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/uv.lock +223 -200
  11. classifyre_cli-0.6.14/.turbo/turbo-build.log +0 -3
  12. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/.gitignore +0 -0
  13. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/.python-version +0 -0
  14. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/README.md +0 -0
  15. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/main.py +0 -0
  16. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/scripts/generate_models.py +0 -0
  17. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/scripts/generate_runtime_packages.py +0 -0
  18. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/scripts/generate_sdk_completions.py +0 -0
  19. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/__init__.py +0 -0
  20. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/augmentation/__init__.py +0 -0
  21. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/augmentation/contract.py +0 -0
  22. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/augmentation/preview.py +0 -0
  23. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/augmentation/runner.py +0 -0
  24. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/augmentation/sdk.py +0 -0
  25. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/augmentation/session.py +0 -0
  26. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/config.py +0 -0
  27. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/__init__.py +0 -0
  28. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/base.py +0 -0
  29. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/broken_links/__init__.py +0 -0
  30. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/broken_links/detector.py +0 -0
  31. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/config.py +0 -0
  32. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/content/__init__.py +0 -0
  33. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/__init__.py +0 -0
  34. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/detector.py +0 -0
  35. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/extractor.py +0 -0
  36. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/__init__.py +0 -0
  37. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_base.py +0 -0
  38. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_custom_detector.py +0 -0
  39. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_decision.py +0 -0
  40. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_factory.py +0 -0
  41. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_gliner2.py +0 -0
  42. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_image_classification.py +0 -0
  43. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_llm.py +0 -0
  44. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_object_detection.py +0 -0
  45. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_provider_call.py +0 -0
  46. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_regex.py +0 -0
  47. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_tag.py +0 -0
  48. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/runners/_text_classification.py +0 -0
  49. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom/trainer.py +0 -0
  50. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/__init__.py +0 -0
  51. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/contract.py +0 -0
  52. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/files.py +0 -0
  53. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/preview.py +0 -0
  54. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/results.py +0 -0
  55. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/runner.py +0 -0
  56. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/sdk.py +0 -0
  57. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/custom_detector/session.py +0 -0
  58. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/dependencies.py +0 -0
  59. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/engine_version.py +0 -0
  60. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/errors.py +0 -0
  61. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/pii/__init__.py +0 -0
  62. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/pii/detector.py +0 -0
  63. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/secrets/__init__.py +0 -0
  64. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/secrets/detector.py +0 -0
  65. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/threat/__init__.py +0 -0
  66. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/threat/code_security_detector.py +0 -0
  67. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/detectors/threat/yara_detector.py +0 -0
  68. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/file_evaluation/__init__.py +0 -0
  69. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/file_evaluation/asset_fixture.py +0 -0
  70. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/file_evaluation/runner.py +0 -0
  71. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/graph/__init__.py +0 -0
  72. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/graph/edges.py +0 -0
  73. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/main.py +0 -0
  74. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/models/generated_detectors.py +0 -0
  75. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/models/generated_input.py +0 -0
  76. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/models/generated_single_asset_scan_results.py +0 -0
  77. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/__init__.py +0 -0
  78. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/child.py +0 -0
  79. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/cli.py +0 -0
  80. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/cohort.py +0 -0
  81. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/contract.py +0 -0
  82. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/execute.py +0 -0
  83. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/files.py +0 -0
  84. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/groups.py +0 -0
  85. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/outputs.py +0 -0
  86. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/packages.py +0 -0
  87. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/protocol.py +0 -0
  88. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/redact.py +0 -0
  89. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/scaffold.py +0 -0
  90. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/sdk.py +0 -0
  91. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/notebook/serialize.py +0 -0
  92. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/outputs/__init__.py +0 -0
  93. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/outputs/base.py +0 -0
  94. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/outputs/console.py +0 -0
  95. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/outputs/factory.py +0 -0
  96. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/outputs/file.py +0 -0
  97. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/outputs/rest.py +0 -0
  98. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/__init__.py +0 -0
  99. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/asset_payload_server.py +0 -0
  100. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/content_provider.py +0 -0
  101. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/detector_breaker.py +0 -0
  102. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/detector_pipeline.py +0 -0
  103. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/finding_context.py +0 -0
  104. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/parsed_content_provider.py +0 -0
  105. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/payload_window.py +0 -0
  106. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/scan_cache.py +0 -0
  107. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/text_artifact.py +0 -0
  108. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/pipeline/worker_pool.py +0 -0
  109. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/__init__.py +0 -0
  110. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/asset_metadata.py +0 -0
  111. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/atlassian_common.py +0 -0
  112. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/azure_blob_storage/__init__.py +0 -0
  113. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/azure_blob_storage/source.py +0 -0
  114. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/base.py +0 -0
  115. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/confluence/__init__.py +0 -0
  116. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/confluence/source.py +0 -0
  117. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/custom/__init__.py +0 -0
  118. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/custom/env.py +0 -0
  119. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/custom/runner.py +0 -0
  120. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/custom/source.py +0 -0
  121. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/databricks/__init__.py +0 -0
  122. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/databricks/source.py +0 -0
  123. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/delta_lake/__init__.py +0 -0
  124. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/delta_lake/source.py +0 -0
  125. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/dependencies.py +0 -0
  126. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/dremio/__init__.py +0 -0
  127. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/dremio/client.py +0 -0
  128. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/dremio/source.py +0 -0
  129. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/dropbox/__init__.py +0 -0
  130. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/dropbox/auth.py +0 -0
  131. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/dropbox/source.py +0 -0
  132. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/elasticsearch/__init__.py +0 -0
  133. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/elasticsearch/source.py +0 -0
  134. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/email/__init__.py +0 -0
  135. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/email/source.py +0 -0
  136. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/git/__init__.py +0 -0
  137. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/google_cloud_storage/__init__.py +0 -0
  138. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/google_cloud_storage/source.py +0 -0
  139. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/google_workspace/__init__.py +0 -0
  140. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/google_workspace/source.py +0 -0
  141. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/hive/__init__.py +0 -0
  142. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/hive/source.py +0 -0
  143. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/hugging_face/__init__.py +0 -0
  144. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/hugging_face/source.py +0 -0
  145. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/iceberg/__init__.py +0 -0
  146. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/iceberg/source.py +0 -0
  147. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/jira/__init__.py +0 -0
  148. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/jira/source.py +0 -0
  149. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/kafka/__init__.py +0 -0
  150. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/kafka/rest.py +0 -0
  151. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/kafka/source.py +0 -0
  152. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/lakehouse_base.py +0 -0
  153. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/local_folder/__init__.py +0 -0
  154. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/local_folder/source.py +0 -0
  155. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/meilisearch/__init__.py +0 -0
  156. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/meilisearch/source.py +0 -0
  157. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/microsoft_365/__init__.py +0 -0
  158. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/microsoft_365/source.py +0 -0
  159. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/mongodb/__init__.py +0 -0
  160. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/mongodb/source.py +0 -0
  161. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/mssql/__init__.py +0 -0
  162. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/mssql/source.py +0 -0
  163. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/mysql/__init__.py +0 -0
  164. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/mysql/source.py +0 -0
  165. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/neo4j/__init__.py +0 -0
  166. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/neo4j/source.py +0 -0
  167. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/notion/__init__.py +0 -0
  168. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/notion/client.py +0 -0
  169. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/notion/source.py +0 -0
  170. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/object_storage/base.py +0 -0
  171. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/opensearch/__init__.py +0 -0
  172. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/opensearch/source.py +0 -0
  173. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/oracle/__init__.py +0 -0
  174. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/oracle/source.py +0 -0
  175. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/postgresql/__init__.py +0 -0
  176. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/postgresql/source.py +0 -0
  177. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/powerbi/__init__.py +0 -0
  178. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/powerbi/source.py +0 -0
  179. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/recipe_normalizer.py +0 -0
  180. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/reddit/__init__.py +0 -0
  181. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/reddit/source.py +0 -0
  182. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/s3_client.py +0 -0
  183. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/s3_compatible_storage/README.md +0 -0
  184. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/s3_compatible_storage/__init__.py +0 -0
  185. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/s3_compatible_storage/source.py +0 -0
  186. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/sandbox/__init__.py +0 -0
  187. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/sandbox/source.py +0 -0
  188. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/search_engine_base.py +0 -0
  189. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/servicedesk/__init__.py +0 -0
  190. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/servicedesk/source.py +0 -0
  191. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/slack/__init__.py +0 -0
  192. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/slack/source.py +0 -0
  193. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/snowflake/__init__.py +0 -0
  194. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/snowflake/source.py +0 -0
  195. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/sqlite/__init__.py +0 -0
  196. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/sqlite/source.py +0 -0
  197. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/tableau/__init__.py +0 -0
  198. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/tableau/source.py +0 -0
  199. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/tabular_base.py +0 -0
  200. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/tabular_utils.py +0 -0
  201. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/wordpress/__init__.py +0 -0
  202. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/wordpress/source.py +0 -0
  203. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/youtube/__init__.py +0 -0
  204. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/sources/youtube/source.py +0 -0
  205. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/telemetry.py +0 -0
  206. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/__init__.py +0 -0
  207. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/archive_extraction.py +0 -0
  208. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/content_extraction.py +0 -0
  209. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/dependency_groups.py +0 -0
  210. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/embedded_files.py +0 -0
  211. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/external_video.py +0 -0
  212. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/file_metadata.py +0 -0
  213. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/file_parser.py +0 -0
  214. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/file_to_images.py +0 -0
  215. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/hashing.py +0 -0
  216. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/legacy_office.py +0 -0
  217. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/payload.py +0 -0
  218. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/range_reader.py +0 -0
  219. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/redaction.py +0 -0
  220. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/resources.py +0 -0
  221. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/sampling_cursor.py +0 -0
  222. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/sampling_frontier.py +0 -0
  223. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/source_files.py +0 -0
  224. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/sql_lineage.py +0 -0
  225. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/torch_runtime.py +0 -0
  226. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/urn.py +0 -0
  227. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/uv_sync.py +0 -0
  228. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/validation.py +0 -0
  229. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/src/utils/video_processing.py +0 -0
  230. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/__init__.py +0 -0
  231. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/_lakehouse_fakes.py +0 -0
  232. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/conftest.py +0 -0
  233. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/__init__.py +0 -0
  234. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
  235. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/conftest.py +0 -0
  236. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/content/__init__.py +0 -0
  237. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/__init__.py +0 -0
  238. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/conftest.py +0 -0
  239. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_decision_runner.py +0 -0
  240. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_gliner2_classification.py +0 -0
  241. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_invoice_extraction.py +0 -0
  242. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_llm_runner.py +0 -0
  243. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_pipeline_integration.py +0 -0
  244. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_regex_runner.py +0 -0
  245. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_runner_hardening.py +0 -0
  246. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/custom/test_transformer_runners.py +0 -0
  247. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/__init__.py +0 -0
  248. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/conftest.py +0 -0
  249. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/sample_invoice.pdf +0 -0
  250. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_chunking.py +0 -0
  251. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_detector.py +0 -0
  252. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
  253. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
  254. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/pii/test_pii_severity.py +0 -0
  255. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/secrets/__init__.py +0 -0
  256. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/secrets/test_secrets_detector.py +0 -0
  257. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
  258. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_base_detector.py +0 -0
  259. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
  260. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_custom_detector_session.py +0 -0
  261. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_detector_catalog_commercial.py +0 -0
  262. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_detector_pipeline_types.py +0 -0
  263. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_detector_schema_examples.py +0 -0
  264. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_detector_types.py +0 -0
  265. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_phase2_detectors.py +0 -0
  266. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_registry.py +0 -0
  267. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/test_tag_runner.py +0 -0
  268. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/threat/__init__.py +0 -0
  269. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/threat/test_code_security_detector.py +0 -0
  270. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/detectors/threat/test_yara_detector.py +0 -0
  271. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
  272. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/integration/test_wordpress_links_assets.py +0 -0
  273. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/pipeline/test_detector_breaker.py +0 -0
  274. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/pipeline/test_detector_outcomes.py +0 -0
  275. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/pipeline/test_detector_pipeline.py +0 -0
  276. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/pipeline/test_worker_pool.py +0 -0
  277. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_arrow_ipc_payloads.py +0 -0
  278. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_assets_metadata_catalog.py +0 -0
  279. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_augmentation_contract.py +0 -0
  280. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_augmentation_edges.py +0 -0
  281. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_augmentation_patch.py +0 -0
  282. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_augmentation_scan_cache.py +0 -0
  283. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_augmentation_sdk.py +0 -0
  284. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_augmentation_session.py +0 -0
  285. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_augmentation_tags_end_to_end.py +0 -0
  286. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_automatic_new_first.py +0 -0
  287. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_azure_blob_storage_source.py +0 -0
  288. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_base_source_attachment.py +0 -0
  289. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_base_source_sampling.py +0 -0
  290. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_byte_valued_fields.py +0 -0
  291. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_config.py +0 -0
  292. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_confluence_source.py +0 -0
  293. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_detector_end_to_end.py +0 -0
  294. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_detector_evaluation.py +0 -0
  295. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_detector_notebook.py +0 -0
  296. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_detector_templates.py +0 -0
  297. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_extractor.py +0 -0
  298. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_source.py +0 -0
  299. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_source_relationships.py +0 -0
  300. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_custom_source_tags_end_to_end.py +0 -0
  301. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_databricks_source.py +0 -0
  302. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_delta_lake_source.py +0 -0
  303. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_dependency_groups.py +0 -0
  304. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_detector_scope.py +0 -0
  305. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_dremio_source.py +0 -0
  306. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_dropbox_auth_command.py +0 -0
  307. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_dropbox_source.py +0 -0
  308. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_elasticsearch_source.py +0 -0
  309. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_email_source.py +0 -0
  310. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_file_evaluation_runner.py +0 -0
  311. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_finding_context.py +0 -0
  312. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_google_cloud_storage_source.py +0 -0
  313. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_google_workspace_source.py +0 -0
  314. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_graph_edges.py +0 -0
  315. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_hashing.py +0 -0
  316. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_hive_source.py +0 -0
  317. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_hugging_face_source.py +0 -0
  318. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_iceberg_source.py +0 -0
  319. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_jira_source.py +0 -0
  320. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_kafka_source.py +0 -0
  321. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_lineage_loss_reporting.py +0 -0
  322. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_local_folder_source.py +0 -0
  323. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_main.py +0 -0
  324. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_media_embedding.py +0 -0
  325. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_meilisearch_source.py +0 -0
  326. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_microsoft_365_source.py +0 -0
  327. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_mongodb_source.py +0 -0
  328. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_mssql_source.py +0 -0
  329. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_mysql_source.py +0 -0
  330. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_neo4j_source.py +0 -0
  331. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_asset_entity.py +0 -0
  332. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_asset_extract.py +0 -0
  333. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_asset_tags.py +0 -0
  334. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_cohort.py +0 -0
  335. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_execute.py +0 -0
  336. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_files.py +0 -0
  337. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_groups.py +0 -0
  338. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_log_levels.py +0 -0
  339. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_packages.py +0 -0
  340. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_query_assets.py +0 -0
  341. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_sdk_exports.py +0 -0
  342. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_serialize.py +0 -0
  343. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notebook_templates.py +0 -0
  344. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_notion_source.py +0 -0
  345. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_object_storage_download_efficiency.py +0 -0
  346. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_opensearch_source.py +0 -0
  347. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_oracle_source.py +0 -0
  348. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_outputs.py +0 -0
  349. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_payload_window.py +0 -0
  350. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_payload_window_parquet.py +0 -0
  351. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_postgresql_source.py +0 -0
  352. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_powerbi_source.py +0 -0
  353. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_range_reading.py +0 -0
  354. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_recipe_normalizer.py +0 -0
  355. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_reddit_source.py +0 -0
  356. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_rest_backpressure.py +0 -0
  357. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_runtime_packages.py +0 -0
  358. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_s3_compatible_storage_source.py +0 -0
  359. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_sampling_automatic.py +0 -0
  360. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_sampling_cursor.py +0 -0
  361. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_sampling_frontier.py +0 -0
  362. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_sandbox_source.py +0 -0
  363. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_scan_cache.py +0 -0
  364. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_sdk_completions.py +0 -0
  365. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_secret_redaction.py +0 -0
  366. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_semantic_declarations.py +0 -0
  367. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_servicedesk_source.py +0 -0
  368. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_slack_source.py +0 -0
  369. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_snowflake_source.py +0 -0
  370. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_source_dependency_groups.py +0 -0
  371. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_source_strategy_conformance.py +0 -0
  372. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_sql_lineage.py +0 -0
  373. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_sqlite_source.py +0 -0
  374. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_tableau_source.py +0 -0
  375. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_tabular_automatic_sampling.py +0 -0
  376. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_tabular_lineage.py +0 -0
  377. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_tabular_utils.py +0 -0
  378. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_torch_runtime.py +0 -0
  379. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_urn.py +0 -0
  380. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_uv_sync.py +0 -0
  381. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_view_dependencies_are_not_foreign_keys.py +0 -0
  382. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_wordpress_source.py +0 -0
  383. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_youtube_source.py +0 -0
  384. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/test_youtube_source_integration.py +0 -0
  385. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_archive_extraction.py +0 -0
  386. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_content_extraction.py +0 -0
  387. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_embedded_files.py +0 -0
  388. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_file_metadata.py +0 -0
  389. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_file_parser.py +0 -0
  390. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_file_to_images.py +0 -0
  391. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_legacy_office.py +0 -0
  392. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_payload.py +0 -0
  393. {classifyre_cli-0.6.14 → classifyre_cli-0.6.15}/tests/utils/test_video_processing.py +0 -0
@@ -0,0 +1,3 @@
1
+ $ uv sync
2
+ Resolved 325 packages in 244ms
3
+ Checked 52 packages in 2ms
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: classifyre-cli
3
- Version: 0.6.14
3
+ Version: 0.6.15
4
4
  Summary: Classifyre CLI — scan and classify unstructured data sources
5
5
  License: MIT
6
6
  Keywords: data,ingestion,metadata,pii,secrets,unstructured
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classifyre/cli",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "build": "uv sync",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "classifyre-cli"
3
- version = "0.6.14"
3
+ version = "0.6.15"
4
4
  description = "Classifyre CLI — scan and classify unstructured data sources"
5
5
  readme = "README.md"
6
6
  # Upper bound is load-bearing: onnxruntime publishes no cp315 wheels (notably
@@ -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 = 30
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", 30, 1),
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
- try:
550
- completed = subprocess.run(
551
- command,
552
- cwd=str(cwd) if cwd else None,
553
- env=self._git_env(),
554
- capture_output=True,
555
- timeout=timeout or self._command_timeout_seconds(),
556
- check=False,
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
- except FileNotFoundError as exc: # pragma: no cover - environment issue
559
- raise GitCommandError(
560
- "The git executable was not found on this runner. Git scanning requires git "
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 _list_objects(self) -> Iterator[ObjectRef]:
653
- repo = self._repo()
654
- env = dict(self._git_env())
655
- # Sizes come from the objects already present locally. Without this, a
656
- # blob missing from a filtered clone would be lazily fetched just to
657
- # report its size — turning a metadata-only listing into a full download
658
- # of the repository, which is the whole thing this source avoids.
659
- env["GIT_NO_LAZY_FETCH"] = "1"
660
-
661
- completed = subprocess.run(
662
- [self._git_binary(), "ls-tree", "-r", "-l", "-z", "--full-tree", "HEAD"],
663
- cwd=str(repo),
664
- env=env,
665
- capture_output=True,
666
- timeout=self._command_timeout_seconds(),
667
- check=False,
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
- raise GitCommandError(f"git ls-tree failed: {stderr or completed.returncode}")
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 entry in completed.stdout.decode("utf-8", errors="replace").split("\0"):
897
+ for oid, path in candidates:
678
898
  if self._aborted:
679
899
  return
680
- ref = self._parse_tree_entry(entry, commit_time, commit_times)
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
- def _parse_tree_entry(
687
- self,
688
- entry: str,
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) < 4:
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
- path = path.strip().lstrip("/")
706
- if not path:
707
- return None
708
- if not self._path_in_scope(path):
709
- return None
710
- if not self._object_matches_extension_filters(path):
711
- return None
712
-
713
- # "BAD" is what ls-tree reports for a blob this clone does not hold —
714
- # exactly the files a filtered clone deliberately left on the server.
715
- size_known = raw_size.isdigit()
716
- size = int(raw_size) if size_known else 0
717
- if size_known and size == 0 and not self._include_empty_objects():
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=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
- process = subprocess.Popen(
819
- [self._git_binary(), "cat-file", "blob", oid],
820
- cwd=str(self._repo()),
821
- env=self._git_env(),
822
- stdout=subprocess.PIPE,
823
- stderr=subprocess.PIPE,
824
- )
825
- assert process.stdout is not None
826
- try:
827
- while True:
828
- chunk = process.stdout.read(_CHUNK_BYTES)
829
- if not chunk:
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
- process.stdout.close()
835
- except Exception: # pragma: no cover - close is best effort
836
- pass
837
- stderr = process.stderr.read() if process.stderr else b""
838
- if process.stderr is not None:
839
- process.stderr.close()
840
- returncode = process.wait()
841
- if returncode != 0:
842
- message = self._redact(stderr.decode("utf-8", errors="replace").strip())
843
- raise GitCommandError(f"Could not read {ref.key} from Git: {message or returncode}")
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
- current.extend(bytes(out_frame.planes[0]))
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]