classifyre-cli 0.4.129__tar.gz → 0.4.131__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 (326) hide show
  1. classifyre_cli-0.4.131/.turbo/turbo-build.log +3 -0
  2. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/PKG-INFO +1 -1
  3. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/package.json +1 -1
  4. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/pyproject.toml +8 -1
  5. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/scripts/generate_runtime_packages.py +3 -0
  6. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/scripts/generate_sdk_completions.py +64 -2
  7. classifyre_cli-0.4.131/src/graph/__init__.py +37 -0
  8. classifyre_cli-0.4.131/src/graph/edges.py +409 -0
  9. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/main.py +60 -14
  10. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/models/generated_input.py +4 -0
  11. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/models/generated_single_asset_scan_results.py +5 -0
  12. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/contract.py +1 -1
  13. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/sdk.py +91 -0
  14. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/outputs/rest.py +44 -6
  15. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/base.py +62 -4
  16. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/custom/runner.py +16 -1
  17. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/custom/source.py +67 -0
  18. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/databricks/source.py +124 -0
  19. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/hive/source.py +50 -1
  20. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/lakehouse_base.py +15 -0
  21. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/mssql/source.py +64 -1
  22. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/mysql/source.py +53 -1
  23. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/oracle/source.py +83 -1
  24. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/postgresql/source.py +73 -1
  25. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/powerbi/source.py +97 -1
  26. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/snowflake/source.py +100 -1
  27. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/tableau/source.py +140 -1
  28. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/tabular_base.py +142 -1
  29. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/tabular_utils.py +16 -0
  30. classifyre_cli-0.4.131/src/utils/sql_lineage.py +233 -0
  31. classifyre_cli-0.4.131/src/utils/urn.py +296 -0
  32. classifyre_cli-0.4.131/tests/test_custom_source_relationships.py +138 -0
  33. classifyre_cli-0.4.131/tests/test_graph_edges.py +149 -0
  34. classifyre_cli-0.4.131/tests/test_tabular_lineage.py +252 -0
  35. classifyre_cli-0.4.131/tests/test_urn.py +94 -0
  36. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/uv.lock +73 -76
  37. classifyre_cli-0.4.129/.turbo/turbo-build.log +0 -3
  38. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/.gitignore +0 -0
  39. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/.python-version +0 -0
  40. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/README.md +0 -0
  41. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/main.py +0 -0
  42. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/scripts/generate_models.py +0 -0
  43. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/__init__.py +0 -0
  44. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/config.py +0 -0
  45. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/__init__.py +0 -0
  46. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/base.py +0 -0
  47. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/broken_links/__init__.py +0 -0
  48. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/broken_links/detector.py +0 -0
  49. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/config.py +0 -0
  50. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/content/__init__.py +0 -0
  51. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/__init__.py +0 -0
  52. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/detector.py +0 -0
  53. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/extractor.py +0 -0
  54. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/__init__.py +0 -0
  55. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_base.py +0 -0
  56. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_factory.py +0 -0
  57. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_gliner2.py +0 -0
  58. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_image_classification.py +0 -0
  59. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_llm.py +0 -0
  60. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_object_detection.py +0 -0
  61. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_regex.py +0 -0
  62. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/runners/_text_classification.py +0 -0
  63. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/custom/trainer.py +0 -0
  64. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/dependencies.py +0 -0
  65. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/engine_version.py +0 -0
  66. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/pii/__init__.py +0 -0
  67. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/pii/detector.py +0 -0
  68. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/secrets/__init__.py +0 -0
  69. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/secrets/detector.py +0 -0
  70. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/threat/__init__.py +0 -0
  71. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/threat/code_security_detector.py +0 -0
  72. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/detectors/threat/yara_detector.py +0 -0
  73. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/file_evaluation/__init__.py +0 -0
  74. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/file_evaluation/runner.py +0 -0
  75. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/models/generated_detectors.py +0 -0
  76. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/__init__.py +0 -0
  77. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/cli.py +0 -0
  78. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/execute.py +0 -0
  79. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/files.py +0 -0
  80. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/groups.py +0 -0
  81. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/outputs.py +0 -0
  82. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/packages.py +0 -0
  83. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/protocol.py +0 -0
  84. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/redact.py +0 -0
  85. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/scaffold.py +0 -0
  86. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/notebook/serialize.py +0 -0
  87. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/outputs/__init__.py +0 -0
  88. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/outputs/base.py +0 -0
  89. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/outputs/console.py +0 -0
  90. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/outputs/factory.py +0 -0
  91. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/outputs/file.py +0 -0
  92. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/__init__.py +0 -0
  93. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/content_provider.py +0 -0
  94. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/detector_pipeline.py +0 -0
  95. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/parsed_content_provider.py +0 -0
  96. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/payload_window.py +0 -0
  97. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/scan_cache.py +0 -0
  98. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/text_artifact.py +0 -0
  99. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/pipeline/worker_pool.py +0 -0
  100. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/__init__.py +0 -0
  101. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/asset_metadata.py +0 -0
  102. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/atlassian_common.py +0 -0
  103. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/azure_blob_storage/__init__.py +0 -0
  104. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/azure_blob_storage/source.py +0 -0
  105. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/confluence/__init__.py +0 -0
  106. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/confluence/source.py +0 -0
  107. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/custom/__init__.py +0 -0
  108. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/custom/env.py +0 -0
  109. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/databricks/__init__.py +0 -0
  110. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/delta_lake/__init__.py +0 -0
  111. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/delta_lake/source.py +0 -0
  112. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/dependencies.py +0 -0
  113. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/dropbox/__init__.py +0 -0
  114. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/dropbox/auth.py +0 -0
  115. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/dropbox/source.py +0 -0
  116. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/elasticsearch/__init__.py +0 -0
  117. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/elasticsearch/source.py +0 -0
  118. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/email/__init__.py +0 -0
  119. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/email/source.py +0 -0
  120. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/git/__init__.py +0 -0
  121. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/git/source.py +0 -0
  122. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/google_cloud_storage/__init__.py +0 -0
  123. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/google_cloud_storage/source.py +0 -0
  124. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/google_workspace/__init__.py +0 -0
  125. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/google_workspace/source.py +0 -0
  126. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/hive/__init__.py +0 -0
  127. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/hugging_face/__init__.py +0 -0
  128. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/hugging_face/source.py +0 -0
  129. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/iceberg/__init__.py +0 -0
  130. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/iceberg/source.py +0 -0
  131. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/jira/__init__.py +0 -0
  132. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/jira/source.py +0 -0
  133. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/kafka/__init__.py +0 -0
  134. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/kafka/rest.py +0 -0
  135. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/kafka/source.py +0 -0
  136. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/local_folder/__init__.py +0 -0
  137. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/local_folder/source.py +0 -0
  138. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/meilisearch/__init__.py +0 -0
  139. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/meilisearch/source.py +0 -0
  140. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/microsoft_365/__init__.py +0 -0
  141. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/microsoft_365/source.py +0 -0
  142. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/mongodb/__init__.py +0 -0
  143. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/mongodb/source.py +0 -0
  144. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/mssql/__init__.py +0 -0
  145. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/mysql/__init__.py +0 -0
  146. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/neo4j/__init__.py +0 -0
  147. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/neo4j/source.py +0 -0
  148. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/notion/__init__.py +0 -0
  149. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/notion/client.py +0 -0
  150. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/notion/source.py +0 -0
  151. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/object_storage/base.py +0 -0
  152. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/opensearch/__init__.py +0 -0
  153. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/opensearch/source.py +0 -0
  154. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/oracle/__init__.py +0 -0
  155. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/postgresql/__init__.py +0 -0
  156. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/powerbi/__init__.py +0 -0
  157. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/recipe_normalizer.py +0 -0
  158. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/reddit/__init__.py +0 -0
  159. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/reddit/source.py +0 -0
  160. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/s3_client.py +0 -0
  161. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/s3_compatible_storage/README.md +0 -0
  162. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/s3_compatible_storage/__init__.py +0 -0
  163. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/s3_compatible_storage/source.py +0 -0
  164. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/sandbox/__init__.py +0 -0
  165. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/sandbox/source.py +0 -0
  166. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/search_engine_base.py +0 -0
  167. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/servicedesk/__init__.py +0 -0
  168. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/servicedesk/source.py +0 -0
  169. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/slack/__init__.py +0 -0
  170. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/slack/source.py +0 -0
  171. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/snowflake/__init__.py +0 -0
  172. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/sqlite/__init__.py +0 -0
  173. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/sqlite/source.py +0 -0
  174. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/tableau/__init__.py +0 -0
  175. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/wordpress/__init__.py +0 -0
  176. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/wordpress/source.py +0 -0
  177. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/youtube/__init__.py +0 -0
  178. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/sources/youtube/source.py +0 -0
  179. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/telemetry.py +0 -0
  180. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/__init__.py +0 -0
  181. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/archive_extraction.py +0 -0
  182. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/content_extraction.py +0 -0
  183. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/dependency_groups.py +0 -0
  184. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/embedded_files.py +0 -0
  185. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/external_video.py +0 -0
  186. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/file_metadata.py +0 -0
  187. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/file_parser.py +0 -0
  188. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/file_to_images.py +0 -0
  189. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/hashing.py +0 -0
  190. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/legacy_office.py +0 -0
  191. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/payload.py +0 -0
  192. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/range_reader.py +0 -0
  193. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/resources.py +0 -0
  194. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/source_files.py +0 -0
  195. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/torch_runtime.py +0 -0
  196. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/transcription.py +0 -0
  197. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/uv_sync.py +0 -0
  198. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/validation.py +0 -0
  199. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/src/utils/video_processing.py +0 -0
  200. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/__init__.py +0 -0
  201. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/_lakehouse_fakes.py +0 -0
  202. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/conftest.py +0 -0
  203. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/__init__.py +0 -0
  204. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
  205. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/conftest.py +0 -0
  206. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/content/__init__.py +0 -0
  207. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/__init__.py +0 -0
  208. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/conftest.py +0 -0
  209. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/test_gliner2_classification.py +0 -0
  210. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/test_invoice_extraction.py +0 -0
  211. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/test_llm_runner.py +0 -0
  212. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/test_pipeline_integration.py +0 -0
  213. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/test_regex_runner.py +0 -0
  214. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/test_runner_hardening.py +0 -0
  215. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/custom/test_transformer_runners.py +0 -0
  216. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/__init__.py +0 -0
  217. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/conftest.py +0 -0
  218. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/sample_invoice.pdf +0 -0
  219. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/test_pii_chunking.py +0 -0
  220. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/test_pii_detector.py +0 -0
  221. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
  222. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
  223. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/pii/test_pii_severity.py +0 -0
  224. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/secrets/__init__.py +0 -0
  225. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/secrets/test_secrets_detector.py +0 -0
  226. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
  227. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_base_detector.py +0 -0
  228. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
  229. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_detector_catalog_commercial.py +0 -0
  230. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_detector_pipeline_types.py +0 -0
  231. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_detector_schema_examples.py +0 -0
  232. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_detector_types.py +0 -0
  233. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_phase2_detectors.py +0 -0
  234. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/test_registry.py +0 -0
  235. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/threat/__init__.py +0 -0
  236. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/threat/test_code_security_detector.py +0 -0
  237. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/detectors/threat/test_yara_detector.py +0 -0
  238. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
  239. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/integration/test_wordpress_links_assets.py +0 -0
  240. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/pipeline/test_detector_outcomes.py +0 -0
  241. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/pipeline/test_detector_pipeline.py +0 -0
  242. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/pipeline/test_worker_pool.py +0 -0
  243. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_arrow_ipc_payloads.py +0 -0
  244. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_assets_metadata_catalog.py +0 -0
  245. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_azure_blob_storage_source.py +0 -0
  246. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_base_source_attachment.py +0 -0
  247. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_base_source_sampling.py +0 -0
  248. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_byte_valued_fields.py +0 -0
  249. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_config.py +0 -0
  250. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_confluence_source.py +0 -0
  251. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_custom_extractor.py +0 -0
  252. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_custom_source.py +0 -0
  253. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_databricks_source.py +0 -0
  254. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_delta_lake_source.py +0 -0
  255. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_dependency_groups.py +0 -0
  256. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_dropbox_auth_command.py +0 -0
  257. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_dropbox_source.py +0 -0
  258. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_elasticsearch_source.py +0 -0
  259. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_email_source.py +0 -0
  260. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_file_evaluation_runner.py +0 -0
  261. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_git_source.py +0 -0
  262. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_google_cloud_storage_source.py +0 -0
  263. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_google_workspace_source.py +0 -0
  264. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_hashing.py +0 -0
  265. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_hive_source.py +0 -0
  266. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_hugging_face_source.py +0 -0
  267. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_iceberg_source.py +0 -0
  268. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_jira_source.py +0 -0
  269. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_kafka_source.py +0 -0
  270. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_local_folder_source.py +0 -0
  271. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_main.py +0 -0
  272. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_meilisearch_source.py +0 -0
  273. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_microsoft_365_source.py +0 -0
  274. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_mongodb_source.py +0 -0
  275. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_mssql_source.py +0 -0
  276. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_mysql_source.py +0 -0
  277. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_neo4j_source.py +0 -0
  278. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_notebook_execute.py +0 -0
  279. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_notebook_files.py +0 -0
  280. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_notebook_groups.py +0 -0
  281. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_notebook_packages.py +0 -0
  282. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_notebook_serialize.py +0 -0
  283. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_notebook_templates.py +0 -0
  284. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_notion_source.py +0 -0
  285. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_object_storage_download_efficiency.py +0 -0
  286. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_opensearch_source.py +0 -0
  287. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_oracle_source.py +0 -0
  288. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_outputs.py +0 -0
  289. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_payload_window.py +0 -0
  290. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_payload_window_parquet.py +0 -0
  291. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_postgresql_source.py +0 -0
  292. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_powerbi_source.py +0 -0
  293. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_range_reading.py +0 -0
  294. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_recipe_normalizer.py +0 -0
  295. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_reddit_source.py +0 -0
  296. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_rest_backpressure.py +0 -0
  297. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_runtime_packages.py +0 -0
  298. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_s3_compatible_storage_source.py +0 -0
  299. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_sampling_automatic.py +0 -0
  300. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_sandbox_source.py +0 -0
  301. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_scan_cache.py +0 -0
  302. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_sdk_completions.py +0 -0
  303. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_servicedesk_source.py +0 -0
  304. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_slack_source.py +0 -0
  305. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_snowflake_source.py +0 -0
  306. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_source_dependency_groups.py +0 -0
  307. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_source_strategy_conformance.py +0 -0
  308. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_sqlite_source.py +0 -0
  309. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_tableau_source.py +0 -0
  310. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_tabular_automatic_sampling.py +0 -0
  311. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_tabular_utils.py +0 -0
  312. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_torch_runtime.py +0 -0
  313. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_uv_sync.py +0 -0
  314. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_wordpress_source.py +0 -0
  315. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_youtube_source.py +0 -0
  316. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/test_youtube_source_integration.py +0 -0
  317. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_archive_extraction.py +0 -0
  318. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_content_extraction.py +0 -0
  319. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_embedded_files.py +0 -0
  320. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_file_metadata.py +0 -0
  321. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_file_parser.py +0 -0
  322. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_file_to_images.py +0 -0
  323. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_legacy_office.py +0 -0
  324. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_payload.py +0 -0
  325. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_transcription.py +0 -0
  326. {classifyre_cli-0.4.129 → classifyre_cli-0.4.131}/tests/utils/test_video_processing.py +0 -0
@@ -0,0 +1,3 @@
1
+ $ uv sync
2
+ Resolved 323 packages in 413ms
3
+ Checked 51 packages in 7ms
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: classifyre-cli
3
- Version: 0.4.129
3
+ Version: 0.4.131
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.4.129",
3
+ "version": "0.4.131",
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.4.129"
3
+ version = "0.4.131"
4
4
  description = "Classifyre CLI — scan and classify unstructured data sources"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -114,6 +114,13 @@ custom = [
114
114
  regex = [
115
115
  "google-re2>=1.1",
116
116
  ]
117
+ # Column-level lineage for dialects whose catalog only hands back the view SQL.
118
+ # Kept out of the base image: a warehouse connector needs it, a folder scan
119
+ # never does, and it is loaded through require_module like every other optional
120
+ # source dependency.
121
+ lineage = [
122
+ "sqlglot>=25.0.0",
123
+ ]
117
124
  llm = [
118
125
  "litellm>=1.86.2",
119
126
  # Pure-wheel PDF renderer (permissive license, no system binaries) used to
@@ -80,6 +80,9 @@ INCLUDED_GROUPS = (
80
80
  "kafka",
81
81
  "delta-lake",
82
82
  "iceberg",
83
+ # sqlglot: pure Python and small, and the thing a connector author reaches
84
+ # for when they want to derive column lineage from a query they already have.
85
+ "lineage",
83
86
  )
84
87
 
85
88
  #: Everything else in `[dependency-groups]`, listed rather than inferred so that
@@ -33,10 +33,19 @@ from src.notebook.contract import ( # noqa: E402
33
33
  from src.notebook.sdk import ( # noqa: E402
34
34
  Asset,
35
35
  Context,
36
+ FieldMapping,
37
+ FlowType,
36
38
  NotebookFile,
37
39
  ParsedContent,
40
+ Ref,
41
+ contains,
42
+ flow,
38
43
  pages,
39
44
  parse,
45
+ references,
46
+ same_as,
47
+ urn_for,
48
+ uses,
40
49
  )
41
50
 
42
51
  OUTPUT_PATH = (
@@ -94,8 +103,22 @@ def _snippet(name: str, func: Any) -> str:
94
103
  ]
95
104
  if not required:
96
105
  return f"{name}()"
106
+ # Keyword-only parameters are spelled `name=` in the snippet. `flow` makes
107
+ # both its ends keyword-only precisely so a reversed lineage edge cannot be
108
+ # written by accident; a snippet that filled them positionally would hand
109
+ # the author code that does not run.
110
+ keyword_only = {
111
+ parameter_name
112
+ for parameter_name, parameter in signature.parameters.items()
113
+ if parameter.kind is inspect.Parameter.KEYWORD_ONLY
114
+ }
97
115
  placeholders = ", ".join(
98
- f"${{{index}:{parameter}}}" for index, parameter in enumerate(required, start=1)
116
+ (
117
+ f"{parameter}=${{{index}:{parameter}}}"
118
+ if parameter in keyword_only
119
+ else f"${{{index}:{parameter}}}"
120
+ )
121
+ for index, parameter in enumerate(required, start=1)
99
122
  )
100
123
  return f"{name}({placeholders})"
101
124
 
@@ -166,7 +189,21 @@ def _fields(cls: type) -> list[dict[str, Any]]:
166
189
  #: Module-level helpers a notebook can call directly. Offered alongside `ctx`
167
190
  #: and `Asset` because `namespace()` pre-binds them, so they are as available as
168
191
  #: the import line says they are.
169
- MODULE_FUNCTIONS = (("parse", parse), ("pages", pages))
192
+ #:
193
+ #: The relationship builders are here rather than left to be typed from memory
194
+ #: for the same reason they exist at all: an author who cannot see that `flow`
195
+ #: and `contains` are different functions will reach for `links` and flatten the
196
+ #: distinction back out.
197
+ MODULE_FUNCTIONS = (
198
+ ("parse", parse),
199
+ ("pages", pages),
200
+ ("flow", flow),
201
+ ("contains", contains),
202
+ ("references", references),
203
+ ("same_as", same_as),
204
+ ("uses", uses),
205
+ ("urn_for", urn_for),
206
+ )
170
207
 
171
208
 
172
209
  def _function_entry(name: str, func: Any) -> dict[str, Any]:
@@ -201,6 +238,27 @@ def build() -> dict[str, Any]:
201
238
  "documentation": _summary(Asset),
202
239
  "insertText": "Asset",
203
240
  },
241
+ {
242
+ "label": "Ref",
243
+ "kind": "class",
244
+ "detail": "Ref.asset(id) | Ref.urn(urn)",
245
+ "documentation": _summary(Ref),
246
+ "insertText": "Ref",
247
+ },
248
+ {
249
+ "label": "FieldMapping",
250
+ "kind": "class",
251
+ "detail": "FieldMapping(downstream, upstreams, transform)",
252
+ "documentation": _summary(FieldMapping),
253
+ "insertText": "FieldMapping",
254
+ },
255
+ {
256
+ "label": "FlowType",
257
+ "kind": "class",
258
+ "detail": "TRANSFORM | VIEW | COPY | WRITE | EXPORT | SEND",
259
+ "documentation": _summary(FlowType),
260
+ "insertText": "FlowType",
261
+ },
204
262
  *(_function_entry(name, func) for name, func in MODULE_FUNCTIONS),
205
263
  ],
206
264
  "objects": {
@@ -222,6 +280,10 @@ def build() -> dict[str, Any]:
222
280
  "fields": _fields(NotebookFile),
223
281
  "members": _members(NotebookFile),
224
282
  },
283
+ "FieldMapping": {
284
+ "documentation": _summary(FieldMapping),
285
+ "fields": _fields(FieldMapping),
286
+ },
225
287
  },
226
288
  "contract": {
227
289
  "required": list(REQUIRED_FUNCTIONS),
@@ -0,0 +1,37 @@
1
+ """Typed relationship edges, and the platform names that let them cross systems."""
2
+
3
+ from .edges import (
4
+ ContainmentType,
5
+ Edge,
6
+ EdgeClass,
7
+ FieldMapping,
8
+ FieldTransform,
9
+ FlowType,
10
+ Method,
11
+ Ref,
12
+ ReferenceType,
13
+ UsageType,
14
+ contains,
15
+ flow,
16
+ references,
17
+ same_as,
18
+ uses,
19
+ )
20
+
21
+ __all__ = [
22
+ "ContainmentType",
23
+ "Edge",
24
+ "EdgeClass",
25
+ "FieldMapping",
26
+ "FieldTransform",
27
+ "FlowType",
28
+ "Method",
29
+ "Ref",
30
+ "ReferenceType",
31
+ "UsageType",
32
+ "contains",
33
+ "flow",
34
+ "references",
35
+ "same_as",
36
+ "uses",
37
+ ]
@@ -0,0 +1,409 @@
1
+ """The typed relationship vocabulary a connector emits.
2
+
3
+ Everything a connector knows about how two assets relate arrives here, and the
4
+ only thing this module really enforces is the distinction that a flat list of
5
+ links cannot make: **what does traversing this edge mean?**
6
+
7
+ Four classes, and only one of them is lineage:
8
+
9
+ ``FLOW`` the values in B come from A. The only class where "what breaks
10
+ if I change this" is a meaningful question.
11
+ ``CONTAINMENT`` A is a part of B; dropping B drops A. Column in table, chart in
12
+ dashboard, member in archive. Used to *collapse* a graph, never
13
+ to add a hop to it.
14
+ ``IDENTITY`` A and B are the same real thing seen from two systems. Used to
15
+ *merge* nodes.
16
+ ``REFERENCE`` everything about meaning or navigation -- foreign keys, "see
17
+ also", glossary terms. Propagates nothing.
18
+ ``USAGE`` who touched it. An edge weight, not a path.
19
+
20
+ Flattening these into one "links" list is what produces lineage graphs that are
21
+ technically correct and unreadable: PII tags travelling downstream through a
22
+ join, or a dbt model showing up as an extra hop in every path.
23
+
24
+ **Direction.** A ``FLOW`` edge points *the way the data moves*: upstream to
25
+ downstream, so the arrow on screen follows the data and a downstream traversal
26
+ is an outward one. :func:`flow` takes both ends as keyword-only arguments
27
+ because reversing them is the easiest mistake in the whole subject and the
28
+ hardest to notice afterwards.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ from collections.abc import Iterable, Sequence
34
+ from dataclasses import dataclass, field
35
+ from enum import StrEnum
36
+ from typing import Any
37
+
38
+ from ..utils.urn import Urn
39
+
40
+ __all__ = [
41
+ "ContainmentType",
42
+ "Edge",
43
+ "EdgeClass",
44
+ "FieldMapping",
45
+ "FieldTransform",
46
+ "FlowType",
47
+ "Method",
48
+ "Ref",
49
+ "ReferenceType",
50
+ "UsageType",
51
+ "contains",
52
+ "edge_from_payload",
53
+ "edge_to_payload",
54
+ "flow",
55
+ "references",
56
+ "same_as",
57
+ "uses",
58
+ ]
59
+
60
+
61
+ class EdgeClass(StrEnum):
62
+ FLOW = "FLOW"
63
+ CONTAINMENT = "CONTAINMENT"
64
+ IDENTITY = "IDENTITY"
65
+ REFERENCE = "REFERENCE"
66
+ USAGE = "USAGE"
67
+
68
+
69
+ class FlowType(StrEnum):
70
+ """Subtypes of lineage, named so they do not imply a direction.
71
+
72
+ A verb would: "DERIVED_FROM" reads backwards on an edge that points
73
+ downstream. These are the *nature* of the derivation instead, and the
74
+ direction is carried by the edge itself.
75
+ """
76
+
77
+ TRANSFORM = "TRANSFORM" # computed from, with real logic in between
78
+ VIEW = "VIEW" # a view over its base tables
79
+ COPY = "COPY" # replica, CDC mirror, CLONE -- identity-preserving
80
+ WRITE = "WRITE" # a process produced this
81
+ EXPORT = "EXPORT" # left the system
82
+ SEND = "SEND" # delivered to a recipient
83
+
84
+
85
+ class ContainmentType(StrEnum):
86
+ CONTAINS = "CONTAINS"
87
+ ATTACHED_TO = "ATTACHED_TO"
88
+
89
+
90
+ class ReferenceType(StrEnum):
91
+ REFERENCES = "REFERENCES"
92
+ MENTIONS = "MENTIONS"
93
+ FOREIGN_KEY = "FOREIGN_KEY"
94
+
95
+
96
+ class UsageType(StrEnum):
97
+ OWNS = "OWNS"
98
+ ACCESSED = "ACCESSED"
99
+ READS = "READS"
100
+ EXECUTED = "EXECUTED"
101
+
102
+
103
+ class Method(StrEnum):
104
+ """How the edge was derived -- which is how much it should be trusted.
105
+
106
+ Ordered strongest first. A runtime observation saw the data move; a SQL
107
+ parse only read the intent; a heuristic guessed. Keeping this per-edge is
108
+ what lets two connectors disagree about the same pair without either of
109
+ them having to win at write time.
110
+ """
111
+
112
+ RUNTIME_OBSERVED = "RUNTIME_OBSERVED"
113
+ SYSTEM_CATALOG = "SYSTEM_CATALOG"
114
+ SQL_PARSED = "SQL_PARSED"
115
+ HEURISTIC = "HEURISTIC"
116
+ MANUAL = "MANUAL"
117
+
118
+
119
+ #: Default confidence per method. A connector may override, but should not have
120
+ #: to think about it for the ordinary case.
121
+ _METHOD_CONFIDENCE: dict[Method, float] = {
122
+ Method.RUNTIME_OBSERVED: 1.0,
123
+ Method.SYSTEM_CATALOG: 0.95,
124
+ Method.SQL_PARSED: 0.8,
125
+ Method.HEURISTIC: 0.5,
126
+ Method.MANUAL: 1.0,
127
+ }
128
+
129
+
130
+ class FieldTransform(StrEnum):
131
+ IDENTITY = "IDENTITY" # copied through unchanged
132
+ TRANSFORMED = "TRANSFORMED" # an expression combined the upstreams
133
+ AGGREGATED = "AGGREGATED" # SUM/COUNT/... over a group
134
+ INDIRECT = "INDIRECT" # influenced the rows, not the values
135
+
136
+
137
+ @dataclass(frozen=True)
138
+ class FieldMapping:
139
+ """One column-level dependency inside a dataset-level flow edge.
140
+
141
+ ``downstream=None`` records an *indirect* dependency -- a column that shaped
142
+ which rows came out (an ``ORDER BY``, a ``WHERE``, a join key) without
143
+ feeding any particular output column. Keeping it against the dataset instead
144
+ of against every output column is what stops indirect dependencies from
145
+ fanning out across the whole mapping, and it is the one thing warehouse
146
+ lineage tables have no way to say at all.
147
+ """
148
+
149
+ downstream: str | None
150
+ upstreams: Sequence[str] = ()
151
+ transform: str | None = None
152
+ type: FieldTransform = FieldTransform.TRANSFORMED
153
+
154
+ def __post_init__(self) -> None:
155
+ cleaned = tuple(str(name).strip() for name in self.upstreams if str(name).strip())
156
+ object.__setattr__(self, "upstreams", cleaned)
157
+ if self.downstream is not None:
158
+ object.__setattr__(self, "downstream", str(self.downstream).strip() or None)
159
+ if self.downstream is None and self.type is not FieldTransform.INDIRECT:
160
+ # A mapping with no output column is only meaningful as an indirect
161
+ # dependency; silently keeping it as TRANSFORMED would render as a
162
+ # column-to-nothing arrow in the UI.
163
+ object.__setattr__(self, "type", FieldTransform.INDIRECT)
164
+
165
+ def to_payload(self) -> dict[str, Any]:
166
+ return {
167
+ "downstream": self.downstream,
168
+ "upstreams": list(self.upstreams),
169
+ "transform": self.transform,
170
+ "type": str(self.type),
171
+ }
172
+
173
+
174
+ @dataclass(frozen=True)
175
+ class Ref:
176
+ """One end of an edge.
177
+
178
+ ``asset`` names something this run produced, by its hash. ``urn`` names an
179
+ object by what its *platform* calls it -- which is how a connector points at
180
+ a table in a system it does not scan. An unresolved URN is kept and stitched
181
+ when the other system is eventually ingested, rather than dropped.
182
+ """
183
+
184
+ kind: str
185
+ value: str
186
+
187
+ @staticmethod
188
+ def asset(asset_hash: str) -> Ref:
189
+ value = str(asset_hash or "").strip()
190
+ if not value:
191
+ raise ValueError("Ref.asset() needs an asset hash")
192
+ return Ref("asset", value)
193
+
194
+ @staticmethod
195
+ def urn(urn: str | Urn) -> Ref:
196
+ return Ref("urn", str(Urn.parse(urn)))
197
+
198
+ @staticmethod
199
+ def finding(finding_id: str) -> Ref:
200
+ value = str(finding_id or "").strip()
201
+ if not value:
202
+ raise ValueError("Ref.finding() needs a finding id")
203
+ return Ref("finding", value)
204
+
205
+
206
+ @dataclass(frozen=True)
207
+ class Edge:
208
+ """A directed, classified relationship between two graph entities."""
209
+
210
+ frm: Ref
211
+ to: Ref
212
+ edge_class: EdgeClass
213
+ relation_type: str
214
+ confidence: float = 1.0
215
+ method: Method = Method.SYSTEM_CATALOG
216
+ fields: tuple[FieldMapping, ...] = ()
217
+ via: Ref | None = None
218
+ evidence: dict[str, Any] = field(default_factory=dict)
219
+
220
+ @property
221
+ def granularity(self) -> str:
222
+ return "FIELD" if self.fields else "DATASET"
223
+
224
+ def to_ingest(self) -> Any:
225
+ """Convert to the wire model the REST sink posts.
226
+
227
+ Imported lazily: this module is part of the connector-facing SDK and
228
+ must not drag the HTTP sink into a notebook's import graph.
229
+ """
230
+ from ..outputs.rest import IngestEdge
231
+
232
+ payload: dict[str, Any] = {
233
+ "from_type": "asset" if self.frm.kind != "finding" else "finding",
234
+ "to_type": "asset" if self.to.kind != "finding" else "finding",
235
+ "relation_type": str(self.relation_type),
236
+ "relation_class": str(self.edge_class),
237
+ "granularity": self.granularity,
238
+ "method": str(self.method),
239
+ "confidence": round(float(self.confidence), 2),
240
+ }
241
+ _apply_endpoint(payload, self.frm, "from")
242
+ _apply_endpoint(payload, self.to, "to")
243
+ if self.fields:
244
+ payload["field_mappings"] = [mapping.to_payload() for mapping in self.fields]
245
+ if self.evidence:
246
+ payload["evidence"] = dict(self.evidence)
247
+ if self.via is not None:
248
+ payload["via_id"] = self.via.value if self.via.kind == "asset" else None
249
+ payload["via_urn"] = self.via.value if self.via.kind == "urn" else None
250
+ return IngestEdge(**payload)
251
+
252
+
253
+ def edge_to_payload(edge: Edge) -> dict[str, Any]:
254
+ """Plain-JSON form, for crossing the notebook subprocess boundary."""
255
+ return {
256
+ "from": {"kind": edge.frm.kind, "value": edge.frm.value},
257
+ "to": {"kind": edge.to.kind, "value": edge.to.value},
258
+ "class": str(edge.edge_class),
259
+ "relationType": str(edge.relation_type),
260
+ "confidence": edge.confidence,
261
+ "method": str(edge.method),
262
+ "fields": [mapping.to_payload() for mapping in edge.fields],
263
+ "evidence": dict(edge.evidence),
264
+ "via": None if edge.via is None else {"kind": edge.via.kind, "value": edge.via.value},
265
+ }
266
+
267
+
268
+ def edge_from_payload(payload: dict[str, Any]) -> Edge:
269
+ """Rebuild an Edge on the parent side of the notebook boundary."""
270
+
271
+ def ref(value: Any) -> Ref | None:
272
+ if not isinstance(value, dict):
273
+ return None
274
+ kind = str(value.get("kind") or "asset")
275
+ text = str(value.get("value") or "")
276
+ return Ref(kind, text) if text else None
277
+
278
+ frm = ref(payload.get("from"))
279
+ to = ref(payload.get("to"))
280
+ if frm is None or to is None:
281
+ raise ValueError("A relationship needs both ends")
282
+
283
+ return Edge(
284
+ frm=frm,
285
+ to=to,
286
+ edge_class=EdgeClass(str(payload.get("class") or EdgeClass.REFERENCE)),
287
+ relation_type=str(payload.get("relationType") or "REFERENCES"),
288
+ confidence=float(payload.get("confidence") or 1.0),
289
+ method=Method(str(payload.get("method") or Method.SYSTEM_CATALOG)),
290
+ fields=tuple(
291
+ FieldMapping(
292
+ downstream=item.get("downstream"),
293
+ upstreams=item.get("upstreams") or [],
294
+ transform=item.get("transform"),
295
+ type=FieldTransform(str(item.get("type") or FieldTransform.TRANSFORMED)),
296
+ )
297
+ for item in payload.get("fields") or []
298
+ if isinstance(item, dict)
299
+ ),
300
+ via=ref(payload.get("via")),
301
+ evidence=dict(payload.get("evidence") or {}),
302
+ )
303
+
304
+
305
+ def _apply_endpoint(payload: dict[str, Any], ref: Ref, side: str) -> None:
306
+ if ref.kind == "urn":
307
+ payload[f"{side}_urn"] = ref.value
308
+ else:
309
+ payload[f"{side}_hash"] = ref.value
310
+
311
+
312
+ def _confidence(method: Method, override: float | None) -> float:
313
+ if override is not None:
314
+ return max(0.0, min(1.0, float(override)))
315
+ return _METHOD_CONFIDENCE.get(method, 1.0)
316
+
317
+
318
+ # ── Builders ─────────────────────────────────────────────────────────────
319
+ #
320
+ # One per class. The class is never a free-text argument, so a connector cannot
321
+ # accidentally file a containment relation as lineage -- it would have to call a
322
+ # different function to do it.
323
+
324
+
325
+ def flow(
326
+ *,
327
+ upstream: Ref,
328
+ downstream: Ref,
329
+ type: FlowType = FlowType.TRANSFORM,
330
+ fields: Iterable[FieldMapping] | None = None,
331
+ via: Ref | None = None,
332
+ sql: str | None = None,
333
+ method: Method = Method.SYSTEM_CATALOG,
334
+ confidence: float | None = None,
335
+ evidence: dict[str, Any] | None = None,
336
+ ) -> Edge:
337
+ """Data moves from ``upstream`` into ``downstream``.
338
+
339
+ Both ends are keyword-only on purpose: a reversed lineage edge is silently
340
+ wrong rather than loudly broken, and it is the mistake everyone makes once.
341
+ """
342
+ detail = dict(evidence or {})
343
+ if sql:
344
+ detail["sql"] = sql
345
+ return Edge(
346
+ frm=upstream,
347
+ to=downstream,
348
+ edge_class=EdgeClass.FLOW,
349
+ relation_type=str(type),
350
+ confidence=_confidence(method, confidence),
351
+ method=method,
352
+ fields=tuple(fields or ()),
353
+ via=via,
354
+ evidence=detail,
355
+ )
356
+
357
+
358
+ def contains(parent: Ref, child: Ref, *, type: ContainmentType = ContainmentType.CONTAINS) -> Edge:
359
+ """``child`` is a part of ``parent`` -- structural, not temporal."""
360
+ return Edge(
361
+ frm=parent,
362
+ to=child,
363
+ edge_class=EdgeClass.CONTAINMENT,
364
+ relation_type=str(type),
365
+ method=Method.SYSTEM_CATALOG,
366
+ confidence=1.0,
367
+ )
368
+
369
+
370
+ def same_as(a: Ref, b: Ref, *, method: Method = Method.SYSTEM_CATALOG) -> Edge:
371
+ """The same real-world object, seen from two systems."""
372
+ return Edge(
373
+ frm=a,
374
+ to=b,
375
+ edge_class=EdgeClass.IDENTITY,
376
+ relation_type="SAME_AS",
377
+ method=method,
378
+ confidence=_confidence(method, None),
379
+ )
380
+
381
+
382
+ def references(
383
+ frm: Ref,
384
+ to: Ref,
385
+ *,
386
+ type: ReferenceType = ReferenceType.REFERENCES,
387
+ method: Method = Method.SYSTEM_CATALOG,
388
+ ) -> Edge:
389
+ """``frm`` points at ``to``. No data moves -- a foreign key is not lineage."""
390
+ return Edge(
391
+ frm=frm,
392
+ to=to,
393
+ edge_class=EdgeClass.REFERENCE,
394
+ relation_type=str(type),
395
+ method=method,
396
+ confidence=_confidence(method, None),
397
+ )
398
+
399
+
400
+ def uses(actor: Ref, target: Ref, *, type: UsageType = UsageType.ACCESSED) -> Edge:
401
+ """``actor`` touched ``target``. Answers "who", not "where did this come from"."""
402
+ return Edge(
403
+ frm=actor,
404
+ to=target,
405
+ edge_class=EdgeClass.USAGE,
406
+ relation_type=str(type),
407
+ method=Method.RUNTIME_OBSERVED,
408
+ confidence=1.0,
409
+ )