classifyre-cli 0.6.18__tar.gz → 0.7.1__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 (395) hide show
  1. classifyre_cli-0.7.1/.turbo/turbo-build.log +3 -0
  2. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/PKG-INFO +1 -1
  3. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/package.json +1 -1
  4. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/pyproject.toml +1 -1
  5. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/main.py +5 -0
  6. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/outputs/rest.py +7 -0
  7. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/base.py +30 -0
  8. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/databricks/source.py +56 -12
  9. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/hive/source.py +4 -1
  10. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/mysql/source.py +1 -1
  11. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/neo4j/source.py +3 -0
  12. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/tabular_base.py +22 -2
  13. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/sql_lineage.py +134 -8
  14. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_databricks_source.py +90 -0
  15. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_hive_source.py +23 -0
  16. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_mysql_source.py +104 -0
  17. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_neo4j_source.py +26 -0
  18. classifyre_cli-0.7.1/tests/test_sql_lineage.py +319 -0
  19. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/uv.lock +175 -187
  20. classifyre_cli-0.6.18/.turbo/turbo-build.log +0 -3
  21. classifyre_cli-0.6.18/tests/test_sql_lineage.py +0 -153
  22. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/.gitignore +0 -0
  23. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/.python-version +0 -0
  24. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/README.md +0 -0
  25. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/main.py +0 -0
  26. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/scripts/generate_models.py +0 -0
  27. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/scripts/generate_runtime_packages.py +0 -0
  28. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/scripts/generate_sdk_completions.py +0 -0
  29. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/__init__.py +0 -0
  30. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/augmentation/__init__.py +0 -0
  31. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/augmentation/contract.py +0 -0
  32. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/augmentation/preview.py +0 -0
  33. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/augmentation/runner.py +0 -0
  34. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/augmentation/sdk.py +0 -0
  35. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/augmentation/session.py +0 -0
  36. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/config.py +0 -0
  37. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/__init__.py +0 -0
  38. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/base.py +0 -0
  39. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/broken_links/__init__.py +0 -0
  40. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/broken_links/detector.py +0 -0
  41. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/config.py +0 -0
  42. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/content/__init__.py +0 -0
  43. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/__init__.py +0 -0
  44. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/detector.py +0 -0
  45. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/extractor.py +0 -0
  46. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/__init__.py +0 -0
  47. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_base.py +0 -0
  48. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_custom_detector.py +0 -0
  49. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_decision.py +0 -0
  50. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_factory.py +0 -0
  51. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_gliner2.py +0 -0
  52. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_image_classification.py +0 -0
  53. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_llm.py +0 -0
  54. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_object_detection.py +0 -0
  55. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_provider_call.py +0 -0
  56. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_regex.py +0 -0
  57. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_tag.py +0 -0
  58. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/runners/_text_classification.py +0 -0
  59. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom/trainer.py +0 -0
  60. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/__init__.py +0 -0
  61. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/contract.py +0 -0
  62. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/files.py +0 -0
  63. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/preview.py +0 -0
  64. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/results.py +0 -0
  65. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/runner.py +0 -0
  66. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/sdk.py +0 -0
  67. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/custom_detector/session.py +0 -0
  68. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/dependencies.py +0 -0
  69. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/engine_version.py +0 -0
  70. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/errors.py +0 -0
  71. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/pii/__init__.py +0 -0
  72. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/pii/detector.py +0 -0
  73. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/secrets/__init__.py +0 -0
  74. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/secrets/detector.py +0 -0
  75. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/threat/__init__.py +0 -0
  76. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/threat/code_security_detector.py +0 -0
  77. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/detectors/threat/yara_detector.py +0 -0
  78. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/file_evaluation/__init__.py +0 -0
  79. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/file_evaluation/asset_fixture.py +0 -0
  80. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/file_evaluation/runner.py +0 -0
  81. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/graph/__init__.py +0 -0
  82. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/graph/edges.py +0 -0
  83. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/models/generated_detectors.py +0 -0
  84. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/models/generated_input.py +0 -0
  85. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/models/generated_single_asset_scan_results.py +0 -0
  86. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/__init__.py +0 -0
  87. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/child.py +0 -0
  88. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/cli.py +0 -0
  89. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/cohort.py +0 -0
  90. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/contract.py +0 -0
  91. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/execute.py +0 -0
  92. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/files.py +0 -0
  93. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/groups.py +0 -0
  94. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/outputs.py +0 -0
  95. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/packages.py +0 -0
  96. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/protocol.py +0 -0
  97. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/redact.py +0 -0
  98. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/scaffold.py +0 -0
  99. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/sdk.py +0 -0
  100. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/notebook/serialize.py +0 -0
  101. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/outputs/__init__.py +0 -0
  102. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/outputs/base.py +0 -0
  103. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/outputs/console.py +0 -0
  104. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/outputs/factory.py +0 -0
  105. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/outputs/file.py +0 -0
  106. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/__init__.py +0 -0
  107. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/asset_payload_server.py +0 -0
  108. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/content_provider.py +0 -0
  109. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/detector_breaker.py +0 -0
  110. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/detector_pipeline.py +0 -0
  111. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/finding_context.py +0 -0
  112. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/media_artifact.py +0 -0
  113. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/parsed_content_provider.py +0 -0
  114. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/payload_window.py +0 -0
  115. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/scan_cache.py +0 -0
  116. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/text_artifact.py +0 -0
  117. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/pipeline/worker_pool.py +0 -0
  118. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/__init__.py +0 -0
  119. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/asset_metadata.py +0 -0
  120. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/atlassian_common.py +0 -0
  121. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/azure_blob_storage/__init__.py +0 -0
  122. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/azure_blob_storage/source.py +0 -0
  123. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/confluence/__init__.py +0 -0
  124. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/confluence/source.py +0 -0
  125. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/custom/__init__.py +0 -0
  126. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/custom/env.py +0 -0
  127. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/custom/runner.py +0 -0
  128. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/custom/source.py +0 -0
  129. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/databricks/__init__.py +0 -0
  130. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/delta_lake/__init__.py +0 -0
  131. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/delta_lake/source.py +0 -0
  132. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/dependencies.py +0 -0
  133. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/dremio/__init__.py +0 -0
  134. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/dremio/client.py +0 -0
  135. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/dremio/source.py +0 -0
  136. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/dropbox/__init__.py +0 -0
  137. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/dropbox/auth.py +0 -0
  138. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/dropbox/source.py +0 -0
  139. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/elasticsearch/__init__.py +0 -0
  140. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/elasticsearch/source.py +0 -0
  141. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/email/__init__.py +0 -0
  142. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/email/source.py +0 -0
  143. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/git/__init__.py +0 -0
  144. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/git/source.py +0 -0
  145. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/google_cloud_storage/__init__.py +0 -0
  146. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/google_cloud_storage/source.py +0 -0
  147. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/google_workspace/__init__.py +0 -0
  148. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/google_workspace/source.py +0 -0
  149. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/hive/__init__.py +0 -0
  150. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/hugging_face/__init__.py +0 -0
  151. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/hugging_face/source.py +0 -0
  152. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/iceberg/__init__.py +0 -0
  153. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/iceberg/source.py +0 -0
  154. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/jira/__init__.py +0 -0
  155. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/jira/source.py +0 -0
  156. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/kafka/__init__.py +0 -0
  157. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/kafka/rest.py +0 -0
  158. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/kafka/source.py +0 -0
  159. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/lakehouse_base.py +0 -0
  160. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/local_folder/__init__.py +0 -0
  161. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/local_folder/source.py +0 -0
  162. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/meilisearch/__init__.py +0 -0
  163. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/meilisearch/source.py +0 -0
  164. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/microsoft_365/__init__.py +0 -0
  165. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/microsoft_365/source.py +0 -0
  166. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/mongodb/__init__.py +0 -0
  167. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/mongodb/source.py +0 -0
  168. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/mssql/__init__.py +0 -0
  169. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/mssql/source.py +0 -0
  170. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/mysql/__init__.py +0 -0
  171. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/neo4j/__init__.py +0 -0
  172. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/notion/__init__.py +0 -0
  173. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/notion/client.py +0 -0
  174. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/notion/source.py +0 -0
  175. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/object_storage/base.py +0 -0
  176. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/opensearch/__init__.py +0 -0
  177. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/opensearch/source.py +0 -0
  178. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/oracle/__init__.py +0 -0
  179. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/oracle/source.py +0 -0
  180. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/postgresql/__init__.py +0 -0
  181. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/postgresql/source.py +0 -0
  182. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/powerbi/__init__.py +0 -0
  183. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/powerbi/source.py +0 -0
  184. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/recipe_normalizer.py +0 -0
  185. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/reddit/__init__.py +0 -0
  186. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/reddit/source.py +0 -0
  187. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/s3_client.py +0 -0
  188. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/s3_compatible_storage/README.md +0 -0
  189. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/s3_compatible_storage/__init__.py +0 -0
  190. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/s3_compatible_storage/source.py +0 -0
  191. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/sandbox/__init__.py +0 -0
  192. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/sandbox/source.py +0 -0
  193. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/search_engine_base.py +0 -0
  194. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/servicedesk/__init__.py +0 -0
  195. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/servicedesk/source.py +0 -0
  196. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/slack/__init__.py +0 -0
  197. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/slack/source.py +0 -0
  198. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/snowflake/__init__.py +0 -0
  199. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/snowflake/source.py +0 -0
  200. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/sqlite/__init__.py +0 -0
  201. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/sqlite/source.py +0 -0
  202. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/tableau/__init__.py +0 -0
  203. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/tableau/source.py +0 -0
  204. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/tabular_utils.py +0 -0
  205. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/wordpress/__init__.py +0 -0
  206. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/wordpress/source.py +0 -0
  207. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/youtube/__init__.py +0 -0
  208. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/sources/youtube/source.py +0 -0
  209. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/telemetry.py +0 -0
  210. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/__init__.py +0 -0
  211. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/archive_extraction.py +0 -0
  212. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/content_extraction.py +0 -0
  213. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/dependency_groups.py +0 -0
  214. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/embedded_files.py +0 -0
  215. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/external_video.py +0 -0
  216. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/file_metadata.py +0 -0
  217. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/file_parser.py +0 -0
  218. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/file_to_images.py +0 -0
  219. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/hashing.py +0 -0
  220. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/legacy_office.py +0 -0
  221. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/payload.py +0 -0
  222. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/range_reader.py +0 -0
  223. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/redaction.py +0 -0
  224. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/resources.py +0 -0
  225. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/sampling_cursor.py +0 -0
  226. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/sampling_frontier.py +0 -0
  227. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/source_files.py +0 -0
  228. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/torch_runtime.py +0 -0
  229. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/transcription.py +0 -0
  230. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/urn.py +0 -0
  231. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/uv_sync.py +0 -0
  232. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/validation.py +0 -0
  233. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/src/utils/video_processing.py +0 -0
  234. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/__init__.py +0 -0
  235. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/_lakehouse_fakes.py +0 -0
  236. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/conftest.py +0 -0
  237. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/__init__.py +0 -0
  238. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
  239. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/conftest.py +0 -0
  240. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/content/__init__.py +0 -0
  241. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/__init__.py +0 -0
  242. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/conftest.py +0 -0
  243. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_decision_runner.py +0 -0
  244. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_gliner25_runner.py +0 -0
  245. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_gliner2_classification.py +0 -0
  246. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_invoice_extraction.py +0 -0
  247. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_llm_runner.py +0 -0
  248. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_pipeline_integration.py +0 -0
  249. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_regex_runner.py +0 -0
  250. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_runner_hardening.py +0 -0
  251. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/custom/test_transformer_runners.py +0 -0
  252. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/__init__.py +0 -0
  253. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/conftest.py +0 -0
  254. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/sample_invoice.pdf +0 -0
  255. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/test_pii_chunking.py +0 -0
  256. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/test_pii_detector.py +0 -0
  257. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
  258. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
  259. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/pii/test_pii_severity.py +0 -0
  260. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/secrets/__init__.py +0 -0
  261. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/secrets/test_secrets_detector.py +0 -0
  262. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
  263. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_base_detector.py +0 -0
  264. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
  265. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_custom_detector_session.py +0 -0
  266. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_detector_catalog_commercial.py +0 -0
  267. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_detector_pipeline_types.py +0 -0
  268. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_detector_schema_examples.py +0 -0
  269. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_detector_types.py +0 -0
  270. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_phase2_detectors.py +0 -0
  271. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_registry.py +0 -0
  272. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/test_tag_runner.py +0 -0
  273. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/threat/__init__.py +0 -0
  274. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/threat/test_code_security_detector.py +0 -0
  275. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/detectors/threat/test_yara_detector.py +0 -0
  276. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
  277. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/integration/test_wordpress_links_assets.py +0 -0
  278. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/pipeline/test_detector_breaker.py +0 -0
  279. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/pipeline/test_detector_outcomes.py +0 -0
  280. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/pipeline/test_detector_pipeline.py +0 -0
  281. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/pipeline/test_worker_pool.py +0 -0
  282. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_arrow_ipc_payloads.py +0 -0
  283. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_assets_metadata_catalog.py +0 -0
  284. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_augmentation_contract.py +0 -0
  285. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_augmentation_edges.py +0 -0
  286. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_augmentation_patch.py +0 -0
  287. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_augmentation_scan_cache.py +0 -0
  288. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_augmentation_sdk.py +0 -0
  289. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_augmentation_session.py +0 -0
  290. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_augmentation_tags_end_to_end.py +0 -0
  291. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_automatic_new_first.py +0 -0
  292. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_azure_blob_storage_source.py +0 -0
  293. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_base_source_attachment.py +0 -0
  294. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_base_source_sampling.py +0 -0
  295. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_byte_valued_fields.py +0 -0
  296. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_config.py +0 -0
  297. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_confluence_source.py +0 -0
  298. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_detector_end_to_end.py +0 -0
  299. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_detector_evaluation.py +0 -0
  300. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_detector_notebook.py +0 -0
  301. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_detector_templates.py +0 -0
  302. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_extractor.py +0 -0
  303. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_source.py +0 -0
  304. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_source_relationships.py +0 -0
  305. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_custom_source_tags_end_to_end.py +0 -0
  306. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_delta_lake_source.py +0 -0
  307. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_dependency_groups.py +0 -0
  308. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_detector_scope.py +0 -0
  309. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_dremio_source.py +0 -0
  310. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_dropbox_auth_command.py +0 -0
  311. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_dropbox_source.py +0 -0
  312. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_elasticsearch_source.py +0 -0
  313. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_email_source.py +0 -0
  314. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_file_evaluation_runner.py +0 -0
  315. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_finding_context.py +0 -0
  316. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_git_source.py +0 -0
  317. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_google_cloud_storage_source.py +0 -0
  318. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_google_workspace_source.py +0 -0
  319. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_graph_edges.py +0 -0
  320. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_hashing.py +0 -0
  321. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_hugging_face_source.py +0 -0
  322. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_iceberg_source.py +0 -0
  323. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_jira_source.py +0 -0
  324. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_kafka_source.py +0 -0
  325. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_lineage_loss_reporting.py +0 -0
  326. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_local_folder_source.py +0 -0
  327. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_main.py +0 -0
  328. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_media_embedding.py +0 -0
  329. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_meilisearch_source.py +0 -0
  330. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_microsoft_365_source.py +0 -0
  331. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_mongodb_source.py +0 -0
  332. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_mssql_source.py +0 -0
  333. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_asset_entity.py +0 -0
  334. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_asset_extract.py +0 -0
  335. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_asset_tags.py +0 -0
  336. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_cohort.py +0 -0
  337. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_execute.py +0 -0
  338. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_files.py +0 -0
  339. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_groups.py +0 -0
  340. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_log_levels.py +0 -0
  341. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_packages.py +0 -0
  342. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_query_assets.py +0 -0
  343. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_sdk_exports.py +0 -0
  344. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_serialize.py +0 -0
  345. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notebook_templates.py +0 -0
  346. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_notion_source.py +0 -0
  347. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_object_storage_download_efficiency.py +0 -0
  348. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_opensearch_source.py +0 -0
  349. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_oracle_source.py +0 -0
  350. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_outputs.py +0 -0
  351. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_payload_window.py +0 -0
  352. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_payload_window_parquet.py +0 -0
  353. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_postgresql_source.py +0 -0
  354. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_powerbi_source.py +0 -0
  355. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_range_reading.py +0 -0
  356. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_recipe_normalizer.py +0 -0
  357. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_reddit_source.py +0 -0
  358. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_rest_backpressure.py +0 -0
  359. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_runtime_packages.py +0 -0
  360. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_s3_compatible_storage_source.py +0 -0
  361. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_sampling_automatic.py +0 -0
  362. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_sampling_cursor.py +0 -0
  363. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_sampling_frontier.py +0 -0
  364. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_sandbox_source.py +0 -0
  365. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_scan_cache.py +0 -0
  366. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_sdk_completions.py +0 -0
  367. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_secret_redaction.py +0 -0
  368. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_semantic_declarations.py +0 -0
  369. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_servicedesk_source.py +0 -0
  370. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_slack_source.py +0 -0
  371. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_snowflake_source.py +0 -0
  372. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_source_dependency_groups.py +0 -0
  373. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_source_strategy_conformance.py +0 -0
  374. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_sqlite_source.py +0 -0
  375. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_tableau_source.py +0 -0
  376. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_tabular_automatic_sampling.py +0 -0
  377. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_tabular_lineage.py +0 -0
  378. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_tabular_utils.py +0 -0
  379. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_torch_runtime.py +0 -0
  380. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_urn.py +0 -0
  381. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_uv_sync.py +0 -0
  382. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_view_dependencies_are_not_foreign_keys.py +0 -0
  383. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_wordpress_source.py +0 -0
  384. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_youtube_source.py +0 -0
  385. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/test_youtube_source_integration.py +0 -0
  386. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_archive_extraction.py +0 -0
  387. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_content_extraction.py +0 -0
  388. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_embedded_files.py +0 -0
  389. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_file_metadata.py +0 -0
  390. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_file_parser.py +0 -0
  391. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_file_to_images.py +0 -0
  392. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_legacy_office.py +0 -0
  393. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_payload.py +0 -0
  394. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_transcription.py +0 -0
  395. {classifyre_cli-0.6.18 → classifyre_cli-0.7.1}/tests/utils/test_video_processing.py +0 -0
@@ -0,0 +1,3 @@
1
+ $ uv sync
2
+ Resolved 325 packages in 329ms
3
+ Checked 52 packages in 4ms
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: classifyre-cli
3
- Version: 0.6.18
3
+ Version: 0.7.1
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.18",
3
+ "version": "0.7.1",
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.18"
3
+ version = "0.7.1"
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
@@ -971,6 +971,11 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
971
971
  source.partial_coverage_reason or "no reason given",
972
972
  )
973
973
 
974
+ if getattr(source, "listing_complete", False) and hasattr(
975
+ sink, "set_listing_complete"
976
+ ):
977
+ sink.set_listing_complete()
978
+
974
979
  # Relationships go *before* finish(): finish() finalizes the
975
980
  # run and marks it COMPLETED, and edges that land after that
976
981
  # belong to a run that already claimed to be done.
@@ -203,6 +203,7 @@ class FinalizeIngestRunRequest(BaseModel):
203
203
  # "This run covered a slice of the source." Suppresses retirement: absence
204
204
  # from a cohort run is not evidence that an asset is gone.
205
205
  partial_coverage: bool | None = Field(None, serialization_alias="partialCoverage")
206
+ discovery_complete: bool | None = Field(None, serialization_alias="discoveryComplete")
206
207
  # Per ctx.cohort(): keys each band visited, and the weights it ran with.
207
208
  cohort_stats: dict[str, Any] | None = Field(None, serialization_alias="cohortStats")
208
209
  # Detectors the circuit breaker disabled mid-run (scan-cache identity
@@ -261,6 +262,7 @@ class RestOutputSink:
261
262
  self._scan_cache_savings: tuple[int, int] | None = None
262
263
  self._partial_coverage: bool = False
263
264
  self._partial_coverage_reason: str = ""
265
+ self._listing_complete: bool = False
264
266
  self._detectors_degraded: list[str] = []
265
267
  self._cohort_stats: dict[str, Any] | None = None
266
268
  self.relationship_report = RelationshipReport()
@@ -279,6 +281,10 @@ class RestOutputSink:
279
281
  if reason:
280
282
  self._partial_coverage_reason = str(reason)
281
283
 
284
+ def set_listing_complete(self) -> None:
285
+ """Mark that discovery named every object of the configured scope."""
286
+ self._listing_complete = True
287
+
282
288
  def set_detectors_degraded(self, keys: list[str]) -> None:
283
289
  """Record detectors the breaker disabled mid-run, for the next run's sweep."""
284
290
  seen: set[str] = set(self._detectors_degraded)
@@ -415,6 +421,7 @@ class RestOutputSink:
415
421
  relationships_dropped=lineage.dropped,
416
422
  relationship_errors=lineage.errors or None,
417
423
  partial_coverage=True if self._partial_coverage else None,
424
+ discovery_complete=True if self._listing_complete else None,
418
425
  cohort_stats=self._cohort_stats,
419
426
  detectors_degraded=self._detectors_degraded or None,
420
427
  )
@@ -89,6 +89,8 @@ class BaseSource(ABC):
89
89
  self._next_sampling_cursor: dict[str, Any] | None = None
90
90
  self._partial_coverage: bool = False
91
91
  self._partial_coverage_reason: str = ""
92
+ self._listing_complete: bool = False
93
+ self._listing_errors: int = 0
92
94
  self._sampling_cursor_lock = threading.Lock()
93
95
  self._apply_initial_sampling_override(normalized_recipe)
94
96
  recipe.clear()
@@ -191,6 +193,34 @@ class BaseSource(ABC):
191
193
  if reason and not self._partial_coverage_reason:
192
194
  self._partial_coverage_reason = str(reason)
193
195
 
196
+ def note_listing_error(self) -> None:
197
+ """Record that part of the catalog could not be listed this run.
198
+
199
+ A listing that skipped a schema, a folder or a label cannot say that
200
+ what it did not list is gone, so it forfeits :meth:`declare_listing_complete`.
201
+ """
202
+ self._listing_errors = getattr(self, "_listing_errors", 0) + 1
203
+
204
+ def declare_listing_complete(self) -> None:
205
+ """Say that discovery named every object of the configured scope.
206
+
207
+ Sampling decides how much of each asset is read, not which assets
208
+ exist. A catalog connector (tables, node labels) lists its whole scope
209
+ on every run whatever the strategy, so an asset missing from that list
210
+ is gone and can be retired -- without this, only strategy=ALL ever
211
+ retired anything and every other source kept deleted tables, moved
212
+ databases and renamed labels as "unchanged" forever.
213
+
214
+ Ignored when any part of the listing failed or the run declared partial
215
+ coverage.
216
+ """
217
+ if getattr(self, "_listing_errors", 0) == 0:
218
+ self._listing_complete = True
219
+
220
+ @property
221
+ def listing_complete(self) -> bool:
222
+ return bool(getattr(self, "_listing_complete", False)) and not self.partial_coverage
223
+
194
224
  @property
195
225
  def partial_coverage(self) -> bool:
196
226
  return bool(getattr(self, "_partial_coverage", False))
@@ -109,6 +109,9 @@ class DatabricksSource(BaseTabularSource):
109
109
  self._validate_auth_configuration()
110
110
 
111
111
  self.session = requests.Session()
112
+ # table_key -> upstream table -> ids of the notebooks Unity Catalog saw
113
+ # move data between the two. Filled by _lineage_refs_for_table.
114
+ self._lineage_notebook_ids: dict[tuple[str, ...], dict[tuple[str, str, str], set[str]]] = {}
112
115
  self._access_token: str | None = None
113
116
  self._access_token_expiry: datetime | None = None
114
117
 
@@ -409,9 +412,13 @@ class DatabricksSource(BaseTabularSource):
409
412
  without_scheme = raw.split("://", 1)[-1]
410
413
  return without_scheme.strip("/").split("/", 1)[0]
411
414
 
412
- def _available_columns(self, table_ref: TableRef) -> list[str]:
415
+ def _available_column_types(self, table_ref: TableRef) -> dict[str, str]:
416
+ # ``system.information_schema`` sees every catalog from one connection;
417
+ # the default query reads only the connection's current catalog and a
418
+ # parameter style Databricks does not take, so types used to come back
419
+ # empty for every column.
413
420
  query = (
414
- "SELECT column_name "
421
+ "SELECT column_name, COALESCE(full_data_type, data_type) "
415
422
  "FROM system.information_schema.columns "
416
423
  f"WHERE table_catalog = {_quote_literal(table_ref.database)} "
417
424
  f"AND table_schema = {_quote_literal(table_ref.schema or '')} "
@@ -421,17 +428,17 @@ class DatabricksSource(BaseTabularSource):
421
428
  conn = self._get_cached_connection(table_ref.database)
422
429
  with conn.cursor() as cursor:
423
430
  cursor.execute(query)
424
- columns: list[str] = []
431
+ columns: dict[str, str] = {}
425
432
  for row in cursor.fetchall():
426
- candidate: Any | None = None
427
- try:
428
- candidate = row[0] # type: ignore[index]
429
- except Exception:
430
- candidate = None
431
- if isinstance(candidate, str):
432
- columns.append(candidate)
433
+ name = row[0] if len(row) > 0 else None
434
+ if isinstance(name, str):
435
+ data_type = row[1] if len(row) > 1 else None
436
+ columns[name] = str(data_type) if data_type is not None else ""
433
437
  return columns
434
438
 
439
+ def _available_columns(self, table_ref: TableRef) -> list[str]:
440
+ return list(self._available_column_types(table_ref))
441
+
435
442
  # ── Databricks-specific cell serialization ───────────────────────────
436
443
 
437
444
  def _serialize_cell(self, value: Any) -> str:
@@ -598,6 +605,7 @@ class DatabricksSource(BaseTabularSource):
598
605
  try:
599
606
  schemas = self._list_schemas_for_catalog(database)
600
607
  except Exception as exc:
608
+ self.note_listing_error()
601
609
  logger.warning("Skipping catalog %s due to schema listing error: %s", database, exc)
602
610
  return tables
603
611
  for schema in schemas:
@@ -606,6 +614,7 @@ class DatabricksSource(BaseTabularSource):
606
614
  try:
607
615
  tables.extend(self._list_tables_for_schema(database, schema))
608
616
  except Exception as exc:
617
+ self.note_listing_error()
609
618
  logger.warning(
610
619
  "Skipping schema %s.%s due to table listing error: %s",
611
620
  database,
@@ -653,6 +662,7 @@ class DatabricksSource(BaseTabularSource):
653
662
  },
654
663
  )
655
664
  refs: set[tuple[str, str, str]] = set()
665
+ notebooks: dict[tuple[str, str, str], set[str]] = {}
656
666
  for key in ("upstreams", "upstream_tables"):
657
667
  entries = response.get(key)
658
668
  if isinstance(entries, list):
@@ -661,6 +671,12 @@ class DatabricksSource(BaseTabularSource):
661
671
  parsed = self._lineage_table_ref_from_payload(entry)
662
672
  if parsed:
663
673
  refs.add(parsed)
674
+ for info in entry.get("notebookInfos") or []:
675
+ if isinstance(info, dict) and info.get("notebook_id") is not None:
676
+ notebooks.setdefault(parsed, set()).add(
677
+ str(info["notebook_id"])
678
+ )
679
+ self._lineage_notebook_ids[table_ref.table_key] = notebooks
664
680
  return refs
665
681
 
666
682
  def _emit_table_lineage(
@@ -668,6 +684,7 @@ class DatabricksSource(BaseTabularSource):
668
684
  table_ref: TableRef,
669
685
  upstream_refs: set[tuple[str, str, str]],
670
686
  table_hash_by_key: dict[tuple[str, ...], str],
687
+ notebook_hash_by_id: dict[str, str] | None = None,
671
688
  ) -> None:
672
689
  """Turn Unity Catalog's lineage into typed edges.
673
690
 
@@ -675,6 +692,9 @@ class DatabricksSource(BaseTabularSource):
675
692
  question directly, so nothing here is parsed out of SQL — these edges
676
693
  are as trustworthy as lineage gets, and are marked SYSTEM_CATALOG.
677
694
 
695
+ When Unity Catalog saw a notebook run the statement, the edge carries it
696
+ as ``via`` -- if that notebook is one of this scan's assets.
697
+
678
698
  The upstreams that are *not* part of this scan are the important half.
679
699
  They used to be dropped on the floor (``if target in table_hash_by_key``),
680
700
  which meant a table fed by a catalog outside the configured scope looked
@@ -686,6 +706,7 @@ class DatabricksSource(BaseTabularSource):
686
706
  return
687
707
 
688
708
  column_mappings = self._column_mappings_for_table(table_ref)
709
+ notebook_ids = self._lineage_notebook_ids.get(table_ref.table_key, {})
689
710
  for upstream in sorted(upstream_refs):
690
711
  upstream_hash = table_hash_by_key.get(upstream)
691
712
  upstream_ref = (
@@ -693,12 +714,21 @@ class DatabricksSource(BaseTabularSource):
693
714
  )
694
715
  if upstream_ref is None:
695
716
  continue
717
+ via_hash = next(
718
+ (
719
+ notebook_hash_by_id[notebook_id]
720
+ for notebook_id in sorted(notebook_ids.get(upstream, ()))
721
+ if notebook_id in (notebook_hash_by_id or {})
722
+ ),
723
+ None,
724
+ )
696
725
  self.add_edge(
697
726
  flow(
698
727
  upstream=upstream_ref,
699
728
  downstream=Ref.asset(downstream_hash),
700
729
  type=FlowType.TRANSFORM,
701
730
  fields=column_mappings.get(upstream, []),
731
+ via=Ref.asset(via_hash) if via_hash else None,
702
732
  method=Method.SYSTEM_CATALOG,
703
733
  )
704
734
  )
@@ -786,6 +816,7 @@ class DatabricksSource(BaseTabularSource):
786
816
  "get", "/api/2.0/workspace/list", params={"path": path}
787
817
  )
788
818
  except Exception as exc:
819
+ self.note_listing_error()
789
820
  logger.warning("Skipping workspace path %s due to listing error: %s", path, exc)
790
821
  continue
791
822
  objects = payload.get("objects")
@@ -865,6 +896,7 @@ class DatabricksSource(BaseTabularSource):
865
896
  try:
866
897
  payload = self._request_json("get", "/api/2.0/pipelines", params=params)
867
898
  except Exception as exc:
899
+ self.note_listing_error()
868
900
  logger.warning("Could not list Databricks pipelines: %s", exc)
869
901
  break
870
902
  values: list[dict[str, Any]] = []
@@ -935,6 +967,15 @@ class DatabricksSource(BaseTabularSource):
935
967
  t.table_key: self.generate_hash_id(t.raw_id) for t in tables
936
968
  }
937
969
 
970
+ # Listed before the tables so a lineage edge can name the notebook that
971
+ # produced it; the notebook assets are still emitted after the tables.
972
+ notebooks = list(self._iter_notebooks())
973
+ notebook_hash_by_id = {
974
+ nb.object_id: self.generate_hash_id(f"notebook_#_{nb.path}")
975
+ for nb in notebooks
976
+ if nb.object_id
977
+ }
978
+
938
979
  include_lineage = self._extraction_options().include_table_lineage
939
980
  if include_lineage and tables:
940
981
  logger.info("Fetching table lineage for %d table(s)...", len(tables))
@@ -957,7 +998,9 @@ class DatabricksSource(BaseTabularSource):
957
998
  for target in sorted(upstream_refs)
958
999
  if target in table_hash_by_key
959
1000
  ]
960
- self._emit_table_lineage(table_ref, upstream_refs, table_hash_by_key)
1001
+ self._emit_table_lineage(
1002
+ table_ref, upstream_refs, table_hash_by_key, notebook_hash_by_id
1003
+ )
961
1004
  except Exception as exc:
962
1005
  logger.warning(
963
1006
  "Could not resolve Databricks lineage for %s: %s", table_label, exc
@@ -974,7 +1017,7 @@ class DatabricksSource(BaseTabularSource):
974
1017
 
975
1018
  # Notebooks
976
1019
  notebook_count = 0
977
- for notebook in self._iter_notebooks():
1020
+ for notebook in notebooks:
978
1021
  if self._aborted:
979
1022
  break
980
1023
  batch.append(self._notebook_to_asset(notebook))
@@ -997,6 +1040,7 @@ class DatabricksSource(BaseTabularSource):
997
1040
  batch = []
998
1041
  if pipeline_count:
999
1042
  logger.info("Discovered %d pipeline(s)", pipeline_count)
1043
+ self._declare_listing_if_whole()
1000
1044
 
1001
1045
  if batch:
1002
1046
  yield batch
@@ -293,7 +293,10 @@ class HiveSource(BaseTabularSource):
293
293
  upstreams = [
294
294
  key
295
295
  for key in upstream_tables_from_sql(
296
- definition, dialect="hive", default_database=view.database
296
+ definition,
297
+ dialect="hive",
298
+ default_database=view.database,
299
+ two_level=True,
297
300
  )
298
301
  if key != view.table_key and key in scoped
299
302
  ]
@@ -322,7 +322,7 @@ class MySQLSource(BaseTabularSource):
322
322
  upstreams = [
323
323
  key
324
324
  for key in upstream_tables_from_sql(
325
- definition, dialect="mysql", default_database=database
325
+ definition, dialect="mysql", default_database=database, two_level=True
326
326
  )
327
327
  if key != view_key
328
328
  ]
@@ -325,6 +325,9 @@ class Neo4jSource(BaseSource):
325
325
  yield batch
326
326
  batch = []
327
327
 
328
+ if not self._aborted and not self._scope_options().node_limit_per_label:
329
+ self.declare_listing_complete()
330
+
328
331
  if batch:
329
332
  logger.info("Emitting final batch of %d asset(s)", len(batch))
330
333
  yield batch
@@ -32,7 +32,12 @@ from ..utils.file_metadata import build_columns
32
32
  from ..utils.file_parser import render_bytes_cell
33
33
  from ..utils.hashing import hash_id, unhash_id
34
34
  from ..utils.sampling_frontier import RangeFrontier
35
- from ..utils.sql_lineage import NO_UPSTREAM, column_mappings_by_table, sqlglot_dialect
35
+ from ..utils.sql_lineage import (
36
+ NO_UPSTREAM,
37
+ column_mappings_by_table,
38
+ fields_for_table,
39
+ sqlglot_dialect,
40
+ )
36
41
  from ..utils.urn import Urn, UrnError
37
42
  from .base import BaseSource
38
43
  from .tabular_utils import (
@@ -314,9 +319,23 @@ class BaseTabularSource(BaseSource):
314
319
  try:
315
320
  tables.extend(self._list_tables_for_database(database))
316
321
  except Exception as exc:
322
+ self.note_listing_error()
317
323
  logger.warning("Skipping database %s due to listing error: %s", database, exc)
318
324
  return tables
319
325
 
326
+ def _listing_is_limited(self) -> bool:
327
+ """Whether a configured cap can have cut the table list short."""
328
+ scope_options = getattr(self, "_scope_options", None)
329
+ scope = scope_options() if callable(scope_options) else None
330
+ return bool(
331
+ getattr(scope, "table_limit", None) or getattr(scope, "table_limit_per_schema", None)
332
+ )
333
+
334
+ def _declare_listing_if_whole(self) -> None:
335
+ """Offer this run's table list as the whole scope, when it plausibly is."""
336
+ if not self._aborted and not self._listing_is_limited():
337
+ self.declare_listing_complete()
338
+
320
339
  # ── Primary keys ─────────────────────────────────────────────────────
321
340
 
322
341
  def _get_primary_key_columns(self, table_ref: TableRef) -> list[str]:
@@ -1039,7 +1058,7 @@ class BaseTabularSource(BaseSource):
1039
1058
  # spelled the name (a synonym, a quoted alias).
1040
1059
  fields = [mapping for group in by_table.values() for mapping in group]
1041
1060
  else:
1042
- fields = list(by_table.get(str(upstream_key[-1]).lower(), []))
1061
+ fields = fields_for_table(by_table, upstream_key)
1043
1062
  upstream_hash = table_hash_by_key.get(upstream_key)
1044
1063
  upstream_ref = (
1045
1064
  Ref.asset(upstream_hash)
@@ -1092,6 +1111,7 @@ class BaseTabularSource(BaseSource):
1092
1111
  table_hash_by_key: dict[tuple[str, ...], str] = {
1093
1112
  t.table_key: self.generate_hash_id(t.raw_id) for t in tables
1094
1113
  }
1114
+ self._declare_listing_if_whole()
1095
1115
  fk_links = self._collect_foreign_key_links(tables)
1096
1116
  self._emit_relationship_edges(tables, table_hash_by_key, fk_links)
1097
1117
 
@@ -32,6 +32,7 @@ logger = logging.getLogger(__name__)
32
32
  __all__ = [
33
33
  "NO_UPSTREAM",
34
34
  "column_mappings_by_table",
35
+ "fields_for_table",
35
36
  "sqlglot_dialect",
36
37
  "table_paths_from_sql",
37
38
  "upstream_tables_from_sql",
@@ -171,6 +172,114 @@ def _paths(node: Any, trail: tuple[Any, ...] = ()) -> Iterator[tuple[Any, ...]]:
171
172
  yield from _paths(child, trail)
172
173
 
173
174
 
175
+ def _table_key(table: Any) -> str:
176
+ """A table as the SQL named it: ``schema.table``, or ``table`` when written bare.
177
+
178
+ The schema stays in the key because two tables can share a name across
179
+ schemas (``core.customers`` and ``edge.customers``) and a view can read both.
180
+ """
181
+ name = str(table.name).lower()
182
+ schema = str(table.db).lower()
183
+ return f"{schema}.{name}" if schema else name
184
+
185
+
186
+ def fields_for_table(
187
+ by_table: dict[str, list[FieldMapping]], table_key: tuple[str, ...]
188
+ ) -> list[FieldMapping]:
189
+ """The mappings that belong to one upstream, given its catalog key.
190
+
191
+ ``table_key`` ends ``(…, schema, table)``. The schema-qualified spelling wins,
192
+ so same-named tables in two schemas are never confused; a table written bare
193
+ (it was on the search path) matches by name; and a name that only one
194
+ qualified entry carries is that entry, however the SQL spelled the schema.
195
+ """
196
+ name = str(table_key[-1]).lower()
197
+ if len(table_key) >= 2:
198
+ qualified = f"{str(table_key[-2]).lower()}.{name}"
199
+ if qualified in by_table:
200
+ return list(by_table[qualified])
201
+ if name in by_table:
202
+ return list(by_table[name])
203
+ candidates = [key for key in by_table if key.rsplit(".", 1)[-1] == name]
204
+ return list(by_table[candidates[0]]) if len(candidates) == 1 else []
205
+
206
+
207
+ def _inline_lateral_subqueries(statement: Any, exp: Any) -> None:
208
+ """Make ``LATERAL (SELECT …) t`` an ordinary derived table.
209
+
210
+ sqlglot's lineage stops at a lateral and reports every column its subquery
211
+ mentions, including the outer table's correlated one, as feeding the output.
212
+ As a plain derived table the output columns follow their own projection to
213
+ the table they come from; the correlation is still found among the
214
+ indirect dependencies because scopes resolve names through their parents.
215
+ A lateral over a function (``jsonb_each(…)``) is left alone.
216
+ """
217
+ for lateral in list(statement.find_all(exp.Lateral)):
218
+ inner = lateral.this
219
+ if not isinstance(inner, exp.Subquery):
220
+ continue
221
+ alias = lateral.args.get("alias")
222
+ if alias is not None:
223
+ inner.set("alias", alias.copy())
224
+ lateral.replace(inner.copy())
225
+
226
+
227
+ def _qualify_lone_sources(statement: Any, exp: Any) -> None:
228
+ """Name the table on columns written without one, where only one table is in play.
229
+
230
+ Postgres stores a single-table view with no qualifiers (``WHERE status <> …``),
231
+ and sqlglot will not place such a column without the table's schema. With one
232
+ table in the ``FROM`` there is only one place it can come from, so say so.
233
+ That also stops an output name that re-uses a column name (``qty::int AS qty``)
234
+ from being ambiguous the next time ``qty`` is read.
235
+
236
+ A name in ``ORDER BY`` / ``GROUP BY`` / ``HAVING`` that matches an output alias
237
+ is left for sqlglot to expand: it means the alias, not the column.
238
+ """
239
+ for select in list(statement.find_all(exp.Select)):
240
+ source = select.args.get("from_") or select.args.get("from")
241
+ if source is None or select.args.get("joins"):
242
+ continue
243
+ node = source.this
244
+ if isinstance(node, exp.Table) and isinstance(node.this, exp.Identifier):
245
+ qualifier = (node.args.get("alias") and node.args["alias"].this) or node.this
246
+ elif isinstance(node, exp.Subquery) and node.args.get("alias"):
247
+ qualifier = node.args["alias"].this
248
+ else:
249
+ continue
250
+ if not isinstance(qualifier, exp.Identifier):
251
+ continue
252
+
253
+ derived = {
254
+ projection.alias.lower()
255
+ for projection in select.expressions
256
+ if isinstance(projection, exp.Alias)
257
+ and not (
258
+ isinstance(projection.this, exp.Column)
259
+ and projection.this.name.lower() == projection.alias.lower()
260
+ )
261
+ }
262
+ for column in list(select.find_all(exp.Column)):
263
+ if column.table or not isinstance(column.this, exp.Identifier):
264
+ continue
265
+ if column.find_ancestor(exp.Select) is not select:
266
+ continue
267
+ tail = column.find_ancestor(exp.Order, exp.Group, exp.Having)
268
+ if tail is not None and tail.parent is select and column.name.lower() in derived:
269
+ continue
270
+ column.set("table", qualifier.copy())
271
+
272
+
273
+ def _source_in_scope(scope: Any, name: str) -> Any:
274
+ """Look a table alias up in this scope, then in the scopes around it."""
275
+ while scope is not None:
276
+ found = scope.sources.get(name)
277
+ if found is not None:
278
+ return found
279
+ scope = scope.parent
280
+ return None
281
+
282
+
174
283
  def column_mappings_by_table(
175
284
  sql: str,
176
285
  *,
@@ -178,9 +287,10 @@ def column_mappings_by_table(
178
287
  ) -> dict[str, list[FieldMapping]]:
179
288
  """Per-column dependencies of a view, split by the table they come from.
180
289
 
181
- Keys are lower-cased table names (the last part of the name), so a caller
182
- holding ``(db, schema, table)`` upstream keys can attach to each edge only
183
- the columns that table really feeds. :data:`NO_UPSTREAM` holds output
290
+ Keys are lower-cased table names as the SQL wrote them (``schema.table``, or
291
+ ``table`` when bare); :func:`fields_for_table` picks the entry for a catalog
292
+ key, so a caller can attach to each edge only the columns that table really
293
+ feeds. :data:`NO_UPSTREAM` holds output
184
294
  columns no table feeds.
185
295
 
186
296
  Columns are resolved through CTEs, derived tables and set operations, so a
@@ -214,6 +324,8 @@ def column_mappings_by_table(
214
324
  for projection in statement.copy().selects
215
325
  if projection.alias_or_name
216
326
  }
327
+ _inline_lateral_subqueries(statement, exp)
328
+ _qualify_lone_sources(statement, exp)
217
329
  statement = qualify(
218
330
  statement, dialect=dialect, validate_qualify_columns=False, identify=False
219
331
  )
@@ -234,7 +346,10 @@ def column_mappings_by_table(
234
346
  # which we deliberately do not resolve. Dataset-level it is.
235
347
  continue
236
348
  try:
237
- node = lineage(name, statement, {}, dialect=dialect, scope=root)
349
+ # Quoted, so an alias like "Id" is matched as written instead of folded.
350
+ node = lineage(
351
+ exp.column(name, quoted=True), statement, {}, dialect=dialect, scope=root
352
+ )
238
353
  except Exception as exc:
239
354
  logger.debug("Could not trace column %s: %s", name, exc)
240
355
  continue
@@ -258,8 +373,8 @@ def column_mappings_by_table(
258
373
  constant = _Trace()
259
374
  constant.add(projections, None, exp)
260
375
  continue
261
- table = str(leaf.source.name).lower()
262
- column = leaf.name.rsplit(".", 1)[-1]
376
+ table = _table_key(leaf.source)
377
+ column = leaf.name.rsplit(".", 1)[-1].strip('"`')
263
378
  if table and column and column != "*":
264
379
  per_table.setdefault(table, _Trace()).add(projections, column, exp)
265
380
 
@@ -312,9 +427,9 @@ def column_mappings_by_table(
312
427
  if clause is None:
313
428
  continue
314
429
  for column in clause.find_all(exp.Column):
315
- source = scope.sources.get(column.table)
430
+ source = _source_in_scope(scope, column.table)
316
431
  if isinstance(source, exp.Table) and column.name and column.name != "*":
317
- indirect.setdefault(str(source.name).lower(), set()).add(column.name)
432
+ indirect.setdefault(_table_key(source), set()).add(column.name)
318
433
 
319
434
  for table, columns in indirect.items():
320
435
  by_table.setdefault(table, []).append(
@@ -329,6 +444,7 @@ def upstream_tables_from_sql(
329
444
  dialect: str | None = None,
330
445
  default_database: str | None = None,
331
446
  default_schema: str | None = None,
447
+ two_level: bool = False,
332
448
  ) -> list[tuple[str, ...]]:
333
449
  """Table keys a statement reads from, for catalogs with no dependency view.
334
450
 
@@ -338,6 +454,11 @@ def upstream_tables_from_sql(
338
454
  the wrong default schema points at nothing — so callers should prefer the
339
455
  catalog wherever it exists and treat these edges as SQL_PARSED.
340
456
 
457
+ ``two_level`` is for engines whose tables are named ``database.table`` (MySQL,
458
+ Hive). sqlglot files the first part under ``db``, so without it a qualified
459
+ name came back as ``(default_database, database, table)`` -- a key no asset
460
+ has, which turned every such view's edges into unresolved ones.
461
+
341
462
  CTE names are excluded: a ``WITH`` alias is not a table, and emitting it
342
463
  would create lineage to an object that does not exist.
343
464
  """
@@ -366,6 +487,11 @@ def upstream_tables_from_sql(
366
487
  name = table.name
367
488
  if not name or name.lower() in cte_names:
368
489
  continue
490
+ if two_level:
491
+ namespace = table.text("db") or default_database
492
+ if namespace:
493
+ keys.add((namespace, name))
494
+ continue
369
495
  schema = table.text("db") or default_schema
370
496
  database = table.text("catalog") or default_database
371
497
  if database and schema: