classifyre-cli 0.6.4__tar.gz → 0.6.5__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 (375) hide show
  1. classifyre_cli-0.6.5/.turbo/turbo-build.log +3 -0
  2. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/PKG-INFO +1 -1
  3. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/package.json +1 -1
  4. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/pyproject.toml +1 -1
  5. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/scripts/generate_sdk_completions.py +12 -0
  6. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/sdk.py +34 -0
  7. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/session.py +5 -0
  8. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/graph/__init__.py +2 -0
  9. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/graph/edges.py +51 -2
  10. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/sdk.py +129 -1
  11. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/urn.py +28 -1
  12. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_sdk.py +1 -0
  13. classifyre_cli-0.6.5/tests/test_notebook_asset_entity.py +82 -0
  14. classifyre_cli-0.6.5/tests/test_semantic_declarations.py +96 -0
  15. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/uv.lock +348 -328
  16. classifyre_cli-0.6.4/.turbo/turbo-build.log +0 -3
  17. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/.gitignore +0 -0
  18. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/.python-version +0 -0
  19. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/README.md +0 -0
  20. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/main.py +0 -0
  21. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/scripts/generate_models.py +0 -0
  22. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/scripts/generate_runtime_packages.py +0 -0
  23. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/__init__.py +0 -0
  24. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/__init__.py +0 -0
  25. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/contract.py +0 -0
  26. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/preview.py +0 -0
  27. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/augmentation/runner.py +0 -0
  28. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/config.py +0 -0
  29. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/__init__.py +0 -0
  30. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/base.py +0 -0
  31. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/broken_links/__init__.py +0 -0
  32. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/broken_links/detector.py +0 -0
  33. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/config.py +0 -0
  34. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/content/__init__.py +0 -0
  35. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/__init__.py +0 -0
  36. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/detector.py +0 -0
  37. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/extractor.py +0 -0
  38. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/__init__.py +0 -0
  39. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_base.py +0 -0
  40. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_custom_detector.py +0 -0
  41. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_factory.py +0 -0
  42. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_gliner2.py +0 -0
  43. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_image_classification.py +0 -0
  44. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_llm.py +0 -0
  45. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_object_detection.py +0 -0
  46. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_regex.py +0 -0
  47. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_tag.py +0 -0
  48. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/runners/_text_classification.py +0 -0
  49. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom/trainer.py +0 -0
  50. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/__init__.py +0 -0
  51. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/contract.py +0 -0
  52. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/files.py +0 -0
  53. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/preview.py +0 -0
  54. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/results.py +0 -0
  55. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/runner.py +0 -0
  56. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/sdk.py +0 -0
  57. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/custom_detector/session.py +0 -0
  58. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/dependencies.py +0 -0
  59. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/engine_version.py +0 -0
  60. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/errors.py +0 -0
  61. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/pii/__init__.py +0 -0
  62. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/pii/detector.py +0 -0
  63. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/secrets/__init__.py +0 -0
  64. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/secrets/detector.py +0 -0
  65. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/threat/__init__.py +0 -0
  66. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/threat/code_security_detector.py +0 -0
  67. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/detectors/threat/yara_detector.py +0 -0
  68. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/file_evaluation/__init__.py +0 -0
  69. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/file_evaluation/asset_fixture.py +0 -0
  70. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/file_evaluation/runner.py +0 -0
  71. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/main.py +0 -0
  72. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/models/generated_detectors.py +0 -0
  73. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/models/generated_input.py +0 -0
  74. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/models/generated_single_asset_scan_results.py +0 -0
  75. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/__init__.py +0 -0
  76. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/child.py +0 -0
  77. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/cli.py +0 -0
  78. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/cohort.py +0 -0
  79. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/contract.py +0 -0
  80. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/execute.py +0 -0
  81. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/files.py +0 -0
  82. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/groups.py +0 -0
  83. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/outputs.py +0 -0
  84. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/packages.py +0 -0
  85. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/protocol.py +0 -0
  86. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/redact.py +0 -0
  87. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/scaffold.py +0 -0
  88. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/notebook/serialize.py +0 -0
  89. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/__init__.py +0 -0
  90. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/base.py +0 -0
  91. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/console.py +0 -0
  92. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/factory.py +0 -0
  93. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/file.py +0 -0
  94. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/outputs/rest.py +0 -0
  95. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/__init__.py +0 -0
  96. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/asset_payload_server.py +0 -0
  97. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/content_provider.py +0 -0
  98. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/detector_breaker.py +0 -0
  99. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/detector_pipeline.py +0 -0
  100. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/parsed_content_provider.py +0 -0
  101. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/payload_window.py +0 -0
  102. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/scan_cache.py +0 -0
  103. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/text_artifact.py +0 -0
  104. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/pipeline/worker_pool.py +0 -0
  105. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/__init__.py +0 -0
  106. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/asset_metadata.py +0 -0
  107. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/atlassian_common.py +0 -0
  108. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/azure_blob_storage/__init__.py +0 -0
  109. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/azure_blob_storage/source.py +0 -0
  110. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/base.py +0 -0
  111. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/confluence/__init__.py +0 -0
  112. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/confluence/source.py +0 -0
  113. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/__init__.py +0 -0
  114. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/env.py +0 -0
  115. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/runner.py +0 -0
  116. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/custom/source.py +0 -0
  117. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/databricks/__init__.py +0 -0
  118. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/databricks/source.py +0 -0
  119. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/delta_lake/__init__.py +0 -0
  120. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/delta_lake/source.py +0 -0
  121. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dependencies.py +0 -0
  122. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dropbox/__init__.py +0 -0
  123. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dropbox/auth.py +0 -0
  124. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/dropbox/source.py +0 -0
  125. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/elasticsearch/__init__.py +0 -0
  126. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/elasticsearch/source.py +0 -0
  127. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/email/__init__.py +0 -0
  128. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/email/source.py +0 -0
  129. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/git/__init__.py +0 -0
  130. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/git/source.py +0 -0
  131. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_cloud_storage/__init__.py +0 -0
  132. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_cloud_storage/source.py +0 -0
  133. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_workspace/__init__.py +0 -0
  134. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/google_workspace/source.py +0 -0
  135. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hive/__init__.py +0 -0
  136. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hive/source.py +0 -0
  137. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hugging_face/__init__.py +0 -0
  138. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/hugging_face/source.py +0 -0
  139. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/iceberg/__init__.py +0 -0
  140. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/iceberg/source.py +0 -0
  141. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/jira/__init__.py +0 -0
  142. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/jira/source.py +0 -0
  143. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/kafka/__init__.py +0 -0
  144. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/kafka/rest.py +0 -0
  145. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/kafka/source.py +0 -0
  146. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/lakehouse_base.py +0 -0
  147. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/local_folder/__init__.py +0 -0
  148. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/local_folder/source.py +0 -0
  149. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/meilisearch/__init__.py +0 -0
  150. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/meilisearch/source.py +0 -0
  151. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/microsoft_365/__init__.py +0 -0
  152. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/microsoft_365/source.py +0 -0
  153. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mongodb/__init__.py +0 -0
  154. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mongodb/source.py +0 -0
  155. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mssql/__init__.py +0 -0
  156. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mssql/source.py +0 -0
  157. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mysql/__init__.py +0 -0
  158. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/mysql/source.py +0 -0
  159. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/neo4j/__init__.py +0 -0
  160. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/neo4j/source.py +0 -0
  161. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/notion/__init__.py +0 -0
  162. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/notion/client.py +0 -0
  163. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/notion/source.py +0 -0
  164. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/object_storage/base.py +0 -0
  165. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/opensearch/__init__.py +0 -0
  166. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/opensearch/source.py +0 -0
  167. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/oracle/__init__.py +0 -0
  168. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/oracle/source.py +0 -0
  169. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/postgresql/__init__.py +0 -0
  170. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/postgresql/source.py +0 -0
  171. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/powerbi/__init__.py +0 -0
  172. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/powerbi/source.py +0 -0
  173. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/recipe_normalizer.py +0 -0
  174. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/reddit/__init__.py +0 -0
  175. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/reddit/source.py +0 -0
  176. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_client.py +0 -0
  177. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_compatible_storage/README.md +0 -0
  178. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_compatible_storage/__init__.py +0 -0
  179. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/s3_compatible_storage/source.py +0 -0
  180. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sandbox/__init__.py +0 -0
  181. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sandbox/source.py +0 -0
  182. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/search_engine_base.py +0 -0
  183. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/servicedesk/__init__.py +0 -0
  184. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/servicedesk/source.py +0 -0
  185. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/slack/__init__.py +0 -0
  186. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/slack/source.py +0 -0
  187. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/snowflake/__init__.py +0 -0
  188. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/snowflake/source.py +0 -0
  189. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sqlite/__init__.py +0 -0
  190. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/sqlite/source.py +0 -0
  191. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tableau/__init__.py +0 -0
  192. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tableau/source.py +0 -0
  193. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tabular_base.py +0 -0
  194. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/tabular_utils.py +0 -0
  195. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/wordpress/__init__.py +0 -0
  196. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/wordpress/source.py +0 -0
  197. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/youtube/__init__.py +0 -0
  198. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/sources/youtube/source.py +0 -0
  199. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/telemetry.py +0 -0
  200. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/__init__.py +0 -0
  201. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/archive_extraction.py +0 -0
  202. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/content_extraction.py +0 -0
  203. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/dependency_groups.py +0 -0
  204. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/embedded_files.py +0 -0
  205. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/external_video.py +0 -0
  206. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/file_metadata.py +0 -0
  207. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/file_parser.py +0 -0
  208. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/file_to_images.py +0 -0
  209. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/hashing.py +0 -0
  210. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/legacy_office.py +0 -0
  211. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/payload.py +0 -0
  212. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/range_reader.py +0 -0
  213. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/redaction.py +0 -0
  214. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/resources.py +0 -0
  215. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/source_files.py +0 -0
  216. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/sql_lineage.py +0 -0
  217. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/torch_runtime.py +0 -0
  218. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/transcription.py +0 -0
  219. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/uv_sync.py +0 -0
  220. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/validation.py +0 -0
  221. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/src/utils/video_processing.py +0 -0
  222. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/__init__.py +0 -0
  223. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/_lakehouse_fakes.py +0 -0
  224. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/conftest.py +0 -0
  225. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/__init__.py +0 -0
  226. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
  227. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/conftest.py +0 -0
  228. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/content/__init__.py +0 -0
  229. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/__init__.py +0 -0
  230. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/conftest.py +0 -0
  231. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_gliner2_classification.py +0 -0
  232. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_invoice_extraction.py +0 -0
  233. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_llm_runner.py +0 -0
  234. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_pipeline_integration.py +0 -0
  235. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_regex_runner.py +0 -0
  236. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_runner_hardening.py +0 -0
  237. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/custom/test_transformer_runners.py +0 -0
  238. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/__init__.py +0 -0
  239. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/conftest.py +0 -0
  240. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/sample_invoice.pdf +0 -0
  241. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_chunking.py +0 -0
  242. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_detector.py +0 -0
  243. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
  244. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
  245. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/pii/test_pii_severity.py +0 -0
  246. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/secrets/__init__.py +0 -0
  247. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/secrets/test_secrets_detector.py +0 -0
  248. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
  249. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_base_detector.py +0 -0
  250. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
  251. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_custom_detector_session.py +0 -0
  252. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_catalog_commercial.py +0 -0
  253. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_pipeline_types.py +0 -0
  254. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_schema_examples.py +0 -0
  255. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_detector_types.py +0 -0
  256. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_phase2_detectors.py +0 -0
  257. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_registry.py +0 -0
  258. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/test_tag_runner.py +0 -0
  259. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/threat/__init__.py +0 -0
  260. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/threat/test_code_security_detector.py +0 -0
  261. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/detectors/threat/test_yara_detector.py +0 -0
  262. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
  263. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/integration/test_wordpress_links_assets.py +0 -0
  264. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_detector_breaker.py +0 -0
  265. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_detector_outcomes.py +0 -0
  266. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_detector_pipeline.py +0 -0
  267. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/pipeline/test_worker_pool.py +0 -0
  268. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_arrow_ipc_payloads.py +0 -0
  269. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_assets_metadata_catalog.py +0 -0
  270. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_contract.py +0 -0
  271. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_edges.py +0 -0
  272. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_patch.py +0 -0
  273. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_scan_cache.py +0 -0
  274. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_session.py +0 -0
  275. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_augmentation_tags_end_to_end.py +0 -0
  276. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_azure_blob_storage_source.py +0 -0
  277. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_base_source_attachment.py +0 -0
  278. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_base_source_sampling.py +0 -0
  279. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_byte_valued_fields.py +0 -0
  280. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_config.py +0 -0
  281. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_confluence_source.py +0 -0
  282. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_end_to_end.py +0 -0
  283. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_evaluation.py +0 -0
  284. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_notebook.py +0 -0
  285. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_detector_templates.py +0 -0
  286. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_extractor.py +0 -0
  287. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_source.py +0 -0
  288. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_source_relationships.py +0 -0
  289. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_custom_source_tags_end_to_end.py +0 -0
  290. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_databricks_source.py +0 -0
  291. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_delta_lake_source.py +0 -0
  292. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_dependency_groups.py +0 -0
  293. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_detector_scope.py +0 -0
  294. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_dropbox_auth_command.py +0 -0
  295. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_dropbox_source.py +0 -0
  296. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_elasticsearch_source.py +0 -0
  297. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_email_source.py +0 -0
  298. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_file_evaluation_runner.py +0 -0
  299. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_git_source.py +0 -0
  300. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_google_cloud_storage_source.py +0 -0
  301. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_google_workspace_source.py +0 -0
  302. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_graph_edges.py +0 -0
  303. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_hashing.py +0 -0
  304. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_hive_source.py +0 -0
  305. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_hugging_face_source.py +0 -0
  306. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_iceberg_source.py +0 -0
  307. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_jira_source.py +0 -0
  308. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_kafka_source.py +0 -0
  309. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_lineage_loss_reporting.py +0 -0
  310. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_local_folder_source.py +0 -0
  311. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_main.py +0 -0
  312. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_meilisearch_source.py +0 -0
  313. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_microsoft_365_source.py +0 -0
  314. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_mongodb_source.py +0 -0
  315. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_mssql_source.py +0 -0
  316. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_mysql_source.py +0 -0
  317. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_neo4j_source.py +0 -0
  318. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_asset_extract.py +0 -0
  319. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_asset_tags.py +0 -0
  320. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_cohort.py +0 -0
  321. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_execute.py +0 -0
  322. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_files.py +0 -0
  323. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_groups.py +0 -0
  324. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_log_levels.py +0 -0
  325. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_packages.py +0 -0
  326. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_query_assets.py +0 -0
  327. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_sdk_exports.py +0 -0
  328. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_serialize.py +0 -0
  329. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notebook_templates.py +0 -0
  330. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_notion_source.py +0 -0
  331. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_object_storage_download_efficiency.py +0 -0
  332. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_opensearch_source.py +0 -0
  333. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_oracle_source.py +0 -0
  334. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_outputs.py +0 -0
  335. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_payload_window.py +0 -0
  336. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_payload_window_parquet.py +0 -0
  337. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_postgresql_source.py +0 -0
  338. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_powerbi_source.py +0 -0
  339. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_range_reading.py +0 -0
  340. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_recipe_normalizer.py +0 -0
  341. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_reddit_source.py +0 -0
  342. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_rest_backpressure.py +0 -0
  343. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_runtime_packages.py +0 -0
  344. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_s3_compatible_storage_source.py +0 -0
  345. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sampling_automatic.py +0 -0
  346. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sandbox_source.py +0 -0
  347. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_scan_cache.py +0 -0
  348. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sdk_completions.py +0 -0
  349. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_secret_redaction.py +0 -0
  350. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_servicedesk_source.py +0 -0
  351. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_slack_source.py +0 -0
  352. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_snowflake_source.py +0 -0
  353. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_source_dependency_groups.py +0 -0
  354. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_source_strategy_conformance.py +0 -0
  355. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_sqlite_source.py +0 -0
  356. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tableau_source.py +0 -0
  357. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tabular_automatic_sampling.py +0 -0
  358. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tabular_lineage.py +0 -0
  359. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_tabular_utils.py +0 -0
  360. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_torch_runtime.py +0 -0
  361. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_urn.py +0 -0
  362. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_uv_sync.py +0 -0
  363. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_wordpress_source.py +0 -0
  364. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_youtube_source.py +0 -0
  365. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/test_youtube_source_integration.py +0 -0
  366. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_archive_extraction.py +0 -0
  367. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_content_extraction.py +0 -0
  368. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_embedded_files.py +0 -0
  369. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_file_metadata.py +0 -0
  370. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_file_parser.py +0 -0
  371. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_file_to_images.py +0 -0
  372. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_legacy_office.py +0 -0
  373. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_payload.py +0 -0
  374. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_transcription.py +0 -0
  375. {classifyre_cli-0.6.4 → classifyre_cli-0.6.5}/tests/utils/test_video_processing.py +0 -0
@@ -0,0 +1,3 @@
1
+ $ uv sync
2
+ Resolved 325 packages in 219ms
3
+ Checked 51 packages in 1ms
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: classifyre-cli
3
- Version: 0.6.4
3
+ Version: 0.6.5
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.4",
3
+ "version": "0.6.5",
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.4"
3
+ version = "0.6.5"
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
@@ -35,6 +35,7 @@ from src.notebook.contract import ( # noqa: E402
35
35
  from src.notebook.sdk import ( # noqa: E402
36
36
  Asset,
37
37
  Context,
38
+ Entity,
38
39
  FieldMapping,
39
40
  FlowType,
40
41
  NotebookFile,
@@ -259,6 +260,13 @@ def build() -> dict[str, Any]:
259
260
  "documentation": _summary(Tag),
260
261
  "insertText": "Tag",
261
262
  },
263
+ {
264
+ "label": "Entity",
265
+ "kind": "class",
266
+ "detail": 'Entity(name, type="ORGANIZATION", identifiers={...})',
267
+ "documentation": _summary(Entity),
268
+ "insertText": "Entity",
269
+ },
262
270
  {
263
271
  "label": "Ref",
264
272
  "kind": "class",
@@ -304,6 +312,10 @@ def build() -> dict[str, Any]:
304
312
  "documentation": _summary(Tag),
305
313
  "fields": _fields(Tag),
306
314
  },
315
+ "Entity": {
316
+ "documentation": _summary(Entity),
317
+ "fields": _fields(Entity),
318
+ },
307
319
  "AugmentedAsset": {
308
320
  "documentation": _summary(augmentation_sdk.AugmentedAsset),
309
321
  "members": _members(augmentation_sdk.AugmentedAsset),
@@ -44,11 +44,13 @@ from ..graph.edges import (
44
44
  FlowType,
45
45
  Ref,
46
46
  contains,
47
+ edge_to_payload,
47
48
  flow,
48
49
  references,
49
50
  same_as,
50
51
  uses,
51
52
  )
53
+ from ..graph.edges import means as means_edge
52
54
  from ..notebook.files import ParsedContent, pages, parse
53
55
  from ..notebook.sdk import (
54
56
  MODULE_NAME,
@@ -141,6 +143,7 @@ class AugmentedAsset:
141
143
  self._added_metadata: dict[str, Any] = {}
142
144
  self._tags: dict[str, str] = {}
143
145
  self._links: list[str] = []
146
+ self._means: list[dict[str, Any]] = []
144
147
  self._new_urn: str | None = None
145
148
  self.warnings: list[str] = []
146
149
 
@@ -313,6 +316,33 @@ class AugmentedAsset:
313
316
  if target not in self._links:
314
317
  self._links.append(target)
315
318
 
319
+ def means(
320
+ self,
321
+ key: str,
322
+ *,
323
+ evidence: Mapping[str, Any] | None = None,
324
+ confidence: float | None = None,
325
+ ) -> None:
326
+ """Declare that this asset is about a glossary concept or entity, by key.
327
+
328
+ Additive, like ``tag()`` and ``link()``: a DECLARED semantic link. The
329
+ term need not exist yet; the declaration links once it is defined. An
330
+ invalid key is skipped with a warning.
331
+ """
332
+ try:
333
+ edge = means_edge(
334
+ Ref.asset(self._hash),
335
+ Ref.term(str(key or "")),
336
+ evidence=dict(evidence or {}),
337
+ confidence=confidence,
338
+ )
339
+ except ValueError as exc:
340
+ self.warnings.append(f"Ignoring asset.means({key!r}): {exc}")
341
+ return
342
+ payload = edge_to_payload(edge)
343
+ if payload not in self._means:
344
+ self._means.append(payload)
345
+
316
346
  def set_urn(self, urn: str) -> None:
317
347
  """Set the asset's cross-system identity, only if the connector left it empty.
318
348
 
@@ -337,6 +367,7 @@ class AugmentedAsset:
337
367
  "metadata": dict(self._added_metadata),
338
368
  "tags": dict(self._tags),
339
369
  "links": list(self._links),
370
+ "means": list(self._means),
340
371
  "urn": self._new_urn,
341
372
  "warnings": list(self.warnings),
342
373
  }
@@ -418,6 +449,7 @@ def build_augmentation_module(context: AugmentContext) -> types.ModuleType:
418
449
  module.flow = flow # type: ignore[attr-defined]
419
450
  module.contains = contains # type: ignore[attr-defined]
420
451
  module.references = references # type: ignore[attr-defined]
452
+ module.means = means_edge # type: ignore[attr-defined]
421
453
  module.same_as = same_as # type: ignore[attr-defined]
422
454
  module.uses = uses # type: ignore[attr-defined]
423
455
  module.urn_for = urn_for # type: ignore[attr-defined]
@@ -439,6 +471,7 @@ def build_augmentation_module(context: AugmentContext) -> types.ModuleType:
439
471
  "contains",
440
472
  "ctx",
441
473
  "flow",
474
+ "means",
442
475
  "pages",
443
476
  "parse",
444
477
  "references",
@@ -470,6 +503,7 @@ def augmentation_namespace(context: AugmentContext) -> dict[str, Any]:
470
503
  "flow": flow,
471
504
  "contains": contains,
472
505
  "references": references,
506
+ "means": means_edge,
473
507
  "same_as": same_as,
474
508
  "uses": uses,
475
509
  "urn_for": urn_for,
@@ -463,6 +463,11 @@ class AugmentationSession:
463
463
 
464
464
  self._ingest_edges(patch, asset_hash=asset_hash or None)
465
465
 
466
+ # asset.means(): declarations, already built as MEANS edges by the SDK.
467
+ declared = patch.get("means")
468
+ if isinstance(declared, list) and declared:
469
+ self._ingest_edges({"edges": declared}, asset_hash=asset_hash or None)
470
+
466
471
  for message in patch.get("warnings") or []:
467
472
  self._warn(f"Augmentation on {getattr(asset, 'name', asset_hash)}: {message}")
468
473
 
@@ -13,6 +13,7 @@ from .edges import (
13
13
  UsageType,
14
14
  contains,
15
15
  flow,
16
+ means,
16
17
  references,
17
18
  same_as,
18
19
  uses,
@@ -31,6 +32,7 @@ __all__ = [
31
32
  "UsageType",
32
33
  "contains",
33
34
  "flow",
35
+ "means",
34
36
  "references",
35
37
  "same_as",
36
38
  "uses",
@@ -35,7 +35,7 @@ from dataclasses import dataclass, field
35
35
  from enum import StrEnum
36
36
  from typing import Any, ClassVar
37
37
 
38
- from ..utils.urn import Urn
38
+ from ..utils.urn import Urn, UrnError
39
39
 
40
40
  __all__ = [
41
41
  "ContainmentType",
@@ -189,6 +189,10 @@ class _RefMeta(type):
189
189
  "uri": "Ref.urn(<a urn like 'company://at/firmenbuchnummer/606601k'>)",
190
190
  "url": "Ref.urn(<a urn like 'company://at/firmenbuchnummer/606601k'>)",
191
191
  "external": "Ref.urn(<a urn naming the object in its own system>)",
192
+ "terms": "Ref.term(<a glossary key like 'gmbh'>)",
193
+ "concept": "Ref.term(<a glossary key like 'gmbh'>)",
194
+ "glossary": "Ref.term(<a glossary key like 'gmbh'>)",
195
+ "meaning": "Ref.term(<a glossary key like 'gmbh'>)",
192
196
  }
193
197
 
194
198
  def __getattr__(cls, name: str) -> Any:
@@ -196,7 +200,8 @@ class _RefMeta(type):
196
200
  hint = f" Did you mean {suggestion}?" if suggestion else ""
197
201
  raise AttributeError(
198
202
  f"Ref has no '{name}'. An edge endpoint is one of "
199
- f"Ref.asset(<asset id>), Ref.urn(<urn>) or Ref.finding(<finding id>)."
203
+ f"Ref.asset(<asset id>), Ref.urn(<urn>), Ref.finding(<finding id>) "
204
+ f"or Ref.term(<glossary key>)."
200
205
  f"{hint}"
201
206
  )
202
207
 
@@ -232,6 +237,18 @@ class Ref(metaclass=_RefMeta):
232
237
  raise ValueError("Ref.finding() needs a finding id")
233
238
  return Ref("finding", value)
234
239
 
240
+ @staticmethod
241
+ def term(key: str) -> Ref:
242
+ """A glossary concept or entity by key: ``Ref("urn", "term://glossary/<key>")``.
243
+
244
+ The term need not exist yet: an unknown key is kept as a pending
245
+ reference and links once someone defines it.
246
+ """
247
+ try:
248
+ return Ref("urn", str(Urn.term(key)))
249
+ except UrnError as error:
250
+ raise ValueError(str(error)) from error
251
+
235
252
 
236
253
  @dataclass(frozen=True)
237
254
  class Edge:
@@ -450,6 +467,38 @@ def references(
450
467
  )
451
468
 
452
469
 
470
+ #: Relation type of a meaning declaration (C12), class REFERENCE.
471
+ MEANS = "MEANS"
472
+
473
+
474
+ def means(
475
+ subject: Ref,
476
+ term: Ref | str,
477
+ *,
478
+ confidence: float | None = None,
479
+ evidence: dict[str, Any] | None = None,
480
+ method: Method = Method.SYSTEM_CATALOG,
481
+ ) -> Edge:
482
+ """``subject`` is about a glossary concept or entity (a DECLARED link).
483
+
484
+ ``subject`` is an asset, a URN or a finding; ``term`` is ``Ref.term(key)``
485
+ or the bare key. ``evidence`` says where the meaning came from, e.g.
486
+ ``{"field": "Rechtsform", "value": "GES"}``.
487
+ """
488
+ target = Ref.term(term) if isinstance(term, str) else term
489
+ if target.kind != "urn" or not target.value.startswith("term://"):
490
+ raise ValueError("means() points at a glossary term: pass Ref.term(<key>) or the key")
491
+ return Edge(
492
+ frm=subject,
493
+ to=target,
494
+ edge_class=EdgeClass.REFERENCE,
495
+ relation_type=MEANS,
496
+ method=method,
497
+ confidence=_confidence(method, confidence),
498
+ evidence=dict(evidence or {}),
499
+ )
500
+
501
+
453
502
  def uses(actor: Ref, target: Ref, *, type: UsageType = UsageType.ACCESSED) -> Edge:
454
503
  """``actor`` touched ``target``. Answers "who", not "where did this come from"."""
455
504
  return Edge(
@@ -42,6 +42,7 @@ from ..graph.edges import (
42
42
  UsageType,
43
43
  contains,
44
44
  flow,
45
+ means,
45
46
  references,
46
47
  same_as,
47
48
  uses,
@@ -101,6 +102,112 @@ class Tag:
101
102
  object.__setattr__(self, "severity", normalized)
102
103
 
103
104
 
105
+ #: What kind of named thing an entity is. Decides which finding labels its name
106
+ #: is matched under: a PERSON is found among person names, never among
107
+ #: organisations.
108
+ ENTITY_TYPES = ("PERSON", "ORGANIZATION", "LOCATION", "REFERENCE", "OTHER")
109
+
110
+ #: The asset-metadata key a declared entity travels under.
111
+ ENTITY_METADATA_KEY = "_entity"
112
+
113
+
114
+ @dataclass(frozen=True)
115
+ class Entity:
116
+ """The named thing a record *is*: a company, a person, an account.
117
+
118
+ A register or a customer master does not merely mention ACME Holding GmbH --
119
+ its record is ACME. Saying so::
120
+
121
+ Asset(
122
+ id=fn,
123
+ urn=urn_for("firmenbuch", fn),
124
+ entity=Entity(
125
+ type="ORGANIZATION",
126
+ name="ACME Holding GmbH",
127
+ identifiers={"vat": "ATU12345678", "fn": "123456a"},
128
+ aliases=["ACME Holding"],
129
+ ),
130
+ )
131
+
132
+ creates the entity in the glossary (or updates it), anchored to this
133
+ record, and from then on every document in every other source that carries
134
+ that name or one of those identifiers is a mention of it -- the e-mail that
135
+ quotes the VAT number, the contract that names the company.
136
+
137
+ ``identifiers`` are keyed by the *label a detector finds them under*
138
+ (``iban``, ``email``, a custom detector's label): that label is what links
139
+ a finding to the entity, so an identifier under a label nothing detects
140
+ links nothing. ``key`` pins the glossary key, for a source that already
141
+ refers to the entity with ``Ref.term(key)``.
142
+
143
+ The asset needs a ``urn`` (or the entity a ``key``): it is what makes the
144
+ declaration the same entity on the next scan instead of a new one. A
145
+ declared entity is the register's word and is approved from the start; what
146
+ an operator changes afterwards is kept -- a later scan only adds.
147
+ """
148
+
149
+ name: str
150
+ type: str = "OTHER"
151
+ key: str | None = None
152
+ identifiers: Mapping[str, str] = field(default_factory=dict)
153
+ aliases: tuple[str, ...] | list[str] = field(default_factory=tuple)
154
+ attributes: Mapping[str, Any] = field(default_factory=dict)
155
+
156
+ def __post_init__(self) -> None:
157
+ name = str(self.name or "").strip()
158
+ if not name:
159
+ raise ValueError("Entity.name is required and must be a non-empty string")
160
+ object.__setattr__(self, "name", name)
161
+ kind = str(self.type or "OTHER").strip().upper()
162
+ if kind not in ENTITY_TYPES:
163
+ raise ValueError(
164
+ f"Entity.type must be one of {', '.join(ENTITY_TYPES)}, got {self.type!r}"
165
+ )
166
+ object.__setattr__(self, "type", kind)
167
+ key = str(self.key).strip().lower() if self.key is not None else ""
168
+ object.__setattr__(self, "key", key or None)
169
+ if not isinstance(self.identifiers, Mapping):
170
+ raise TypeError(
171
+ 'Entity.identifiers must map a label to a value, e.g. {"vat": "ATU12345678"}'
172
+ )
173
+ object.__setattr__(
174
+ self,
175
+ "identifiers",
176
+ {
177
+ str(label).strip(): str(value).strip()
178
+ for label, value in self.identifiers.items()
179
+ if str(label).strip() and value is not None and str(value).strip()
180
+ },
181
+ )
182
+ if isinstance(self.aliases, str | bytes):
183
+ raise TypeError("Entity.aliases must be a list of names, not a single string")
184
+ object.__setattr__(
185
+ self,
186
+ "aliases",
187
+ tuple(
188
+ dict.fromkeys(str(alias).strip() for alias in self.aliases if str(alias).strip())
189
+ ),
190
+ )
191
+ if not isinstance(self.attributes, Mapping):
192
+ raise TypeError("Entity.attributes must be a mapping")
193
+ object.__setattr__(self, "attributes", dict(self.attributes))
194
+
195
+ def to_metadata(self) -> dict[str, Any]:
196
+ """The declaration as it travels on the asset. Empty parts are left out,
197
+ so adding a field here never changes the checksum of an asset that does
198
+ not use it."""
199
+ payload: dict[str, Any] = {"name": self.name, "type": self.type}
200
+ if self.key:
201
+ payload["key"] = self.key
202
+ if self.identifiers:
203
+ payload["identifiers"] = dict(sorted(self.identifiers.items()))
204
+ if self.aliases:
205
+ payload["aliases"] = list(self.aliases)
206
+ if self.attributes:
207
+ payload["attributes"] = dict(self.attributes)
208
+ return payload
209
+
210
+
104
211
  @dataclass
105
212
  class Asset:
106
213
  """One thing worth scanning.
@@ -168,6 +275,10 @@ class Asset:
168
275
  #: chose it (``metadata["_cohort"]``), which is how the platform measures
169
276
  #: each band's yield. Never part of the asset's checksum.
170
277
  cohort: CohortItem | None = None
278
+ #: The named thing this record *is* -- see :class:`Entity`. Recorded as
279
+ #: ``metadata["_entity"]``, so it is part of the checksum only for assets
280
+ #: that declare one. Needs ``urn`` (or ``Entity.key``).
281
+ entity: Entity | None = None
171
282
 
172
283
  def __post_init__(self) -> None:
173
284
  self.id = str(self.id).strip()
@@ -206,6 +317,17 @@ class Asset:
206
317
  "key": self.cohort.key,
207
318
  },
208
319
  }
320
+ if self.entity is not None:
321
+ if not isinstance(self.entity, Entity):
322
+ raise TypeError("Asset.entity must be an Entity(...)")
323
+ if not (self.urn and str(self.urn).strip()) and not self.entity.key:
324
+ # Without either, the next scan could not tell this is the same
325
+ # entity and would create it again.
326
+ raise ValueError(
327
+ "Asset.entity needs Asset.urn (build one with urn_for(...)) or "
328
+ "Entity.key, so the next scan updates the same entity"
329
+ )
330
+ self.metadata = {**self.metadata, ENTITY_METADATA_KEY: self.entity.to_metadata()}
209
331
  if not isinstance(self.extract, bool):
210
332
  raise TypeError(f"Asset.extract must be True or False (got {self.extract!r})")
211
333
  reason = str(self.reference_reason).strip() if self.reference_reason is not None else ""
@@ -843,6 +965,8 @@ def build_module(context: Context) -> types.ModuleType:
843
965
  module.flow = flow # type: ignore[attr-defined]
844
966
  module.contains = contains # type: ignore[attr-defined]
845
967
  module.references = references # type: ignore[attr-defined]
968
+ module.means = means # type: ignore[attr-defined]
969
+ module.Entity = Entity # type: ignore[attr-defined]
846
970
  module.same_as = same_as # type: ignore[attr-defined]
847
971
  module.uses = uses # type: ignore[attr-defined]
848
972
  module.urn_for = urn_for # type: ignore[attr-defined]
@@ -860,6 +984,7 @@ def build_module(context: Context) -> types.ModuleType:
860
984
  "Asset",
861
985
  "Context",
862
986
  "ContainmentType",
987
+ "Entity",
863
988
  "FieldMapping",
864
989
  "FieldTransform",
865
990
  "FlowType",
@@ -873,6 +998,7 @@ def build_module(context: Context) -> types.ModuleType:
873
998
  "contains",
874
999
  "ctx",
875
1000
  "flow",
1001
+ "means",
876
1002
  "pages",
877
1003
  "parse",
878
1004
  "references",
@@ -903,6 +1029,7 @@ def namespace(context: Context) -> dict[str, Any]:
903
1029
  "__builtins__": __builtins__,
904
1030
  "Asset": Asset,
905
1031
  "Tag": Tag,
1032
+ "Entity": Entity,
906
1033
  "Ref": Ref,
907
1034
  "FieldMapping": FieldMapping,
908
1035
  "FlowType": FlowType,
@@ -910,6 +1037,7 @@ def namespace(context: Context) -> dict[str, Any]:
910
1037
  "flow": flow,
911
1038
  "contains": contains,
912
1039
  "references": references,
1040
+ "means": means,
913
1041
  "same_as": same_as,
914
1042
  "uses": uses,
915
1043
  "urn_for": urn_for,
@@ -959,7 +1087,7 @@ def iter_relationships(value: Any) -> Iterable[Edge]:
959
1087
  else:
960
1088
  raise TypeError(
961
1089
  "relationships() must yield edges built with flow(), contains(), "
962
- "references(), same_as() or uses() -- got "
1090
+ "references(), means(), same_as() or uses() -- got "
963
1091
  f"{type(item).__name__}. These are not interchangeable: only "
964
1092
  "flow() is lineage, and only it answers 'what breaks if this changes'."
965
1093
  )
@@ -20,11 +20,22 @@ character for character -- a test in each language pins the same table of cases.
20
20
  from __future__ import annotations
21
21
 
22
22
  import logging
23
+ import re
23
24
  from dataclasses import dataclass
24
25
  from enum import StrEnum
25
26
  from typing import Any
26
27
 
27
- __all__ = ["CasePolicy", "Urn", "UrnError", "normalize_urn", "normalize_urn_or_none"]
28
+ __all__ = [
29
+ "TERM_KEY_PATTERN",
30
+ "CasePolicy",
31
+ "Urn",
32
+ "UrnError",
33
+ "normalize_urn",
34
+ "normalize_urn_or_none",
35
+ ]
36
+
37
+ #: A glossary term key (contract C8), the same pattern as the API's.
38
+ TERM_KEY_PATTERN = re.compile(r"[a-z0-9][a-z0-9._-]{0,99}")
28
39
 
29
40
 
30
41
  logger = logging.getLogger(__name__)
@@ -95,6 +106,10 @@ _register(_Platform("powerbi"))
95
106
  _register(_Platform("kafka"))
96
107
  _register(_Platform("file"))
97
108
 
109
+ # Glossary terms (contract C8): ``term://glossary/<key>``. Keys are lower-case
110
+ # by definition, so both halves fold.
111
+ _register(_Platform("term", path_case=CasePolicy.LOWER))
112
+
98
113
  _DEFAULT_PLATFORM = _Platform("", authority_case=CasePolicy.LOWER, path_case=CasePolicy.PRESERVE)
99
114
 
100
115
 
@@ -254,6 +269,18 @@ class Urn:
254
269
  def kafka(cls, bootstrap: str, topic: str) -> Urn:
255
270
  return cls.of("kafka", bootstrap, topic)
256
271
 
272
+ @classmethod
273
+ def term(cls, key: str) -> Urn:
274
+ """A glossary term by its key (C8): ``term://glossary/<key>``."""
275
+ value = (key or "").strip().lower()
276
+ if not TERM_KEY_PATTERN.fullmatch(value):
277
+ raise UrnError(
278
+ f"Not a glossary term key: {key!r}. Keys are lower-case letters, digits, "
279
+ "'.', '_' and '-', starting with a letter or digit, at most 100 characters "
280
+ "(e.g. 'gmbh', 'bank-account-iban')."
281
+ )
282
+ return cls.of("term", "glossary", value)
283
+
257
284
 
258
285
  def _host_port(host: str, port: int | str | None) -> str:
259
286
  """``host:port``, or bare host when no port was configured."""
@@ -72,6 +72,7 @@ def test_empty_writes_are_warnings_not_errors() -> None:
72
72
  "metadata": {},
73
73
  "tags": {},
74
74
  "links": [],
75
+ "means": [],
75
76
  "urn": None,
76
77
  "warnings": asset.warnings,
77
78
  }
@@ -0,0 +1,82 @@
1
+ """A record that *is* a named thing declares it (G5 R12)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+
7
+ from src.notebook.sdk import ENTITY_METADATA_KEY, Asset, Context, Entity, build_module, namespace
8
+
9
+
10
+ def test_entity_is_importable_from_classifyre() -> None:
11
+ module = build_module(Context())
12
+ assert module.Entity is Entity
13
+ assert "Entity" in module.__all__
14
+ assert namespace(Context())["Entity"] is Entity
15
+
16
+
17
+ def test_a_declared_entity_travels_in_the_asset_metadata() -> None:
18
+ asset = Asset(
19
+ id="123456a",
20
+ urn="firmenbuch://at/fn/123456a",
21
+ entity=Entity(
22
+ type="organization",
23
+ name=" ACME Holding GmbH ",
24
+ identifiers={"vat": "ATU12345678", "fn": "123456a"},
25
+ aliases=["ACME Holding", "ACME Holding", " "],
26
+ ),
27
+ )
28
+
29
+ assert asset.metadata[ENTITY_METADATA_KEY] == {
30
+ "name": "ACME Holding GmbH",
31
+ "type": "ORGANIZATION",
32
+ # Sorted, so the same declaration always hashes to the same checksum.
33
+ "identifiers": {"fn": "123456a", "vat": "ATU12345678"},
34
+ "aliases": ["ACME Holding"],
35
+ }
36
+
37
+
38
+ def test_an_asset_without_an_entity_keeps_its_metadata_untouched() -> None:
39
+ # The declaration joins the checksum basis only when set, so existing
40
+ # corpora are not re-checksummed for a feature they do not use.
41
+ asset = Asset(id="a", metadata={"k": "v"})
42
+ assert asset.metadata == {"k": "v"}
43
+
44
+
45
+ def test_the_notebooks_own_metadata_is_kept() -> None:
46
+ asset = Asset(
47
+ id="a",
48
+ urn="crm://customer/1",
49
+ metadata={"segment": "retail"},
50
+ entity=Entity(name="Jane Doe", type="PERSON"),
51
+ )
52
+ assert asset.metadata["segment"] == "retail"
53
+ assert asset.metadata[ENTITY_METADATA_KEY] == {"name": "Jane Doe", "type": "PERSON"}
54
+
55
+
56
+ def test_a_key_stands_in_for_a_urn() -> None:
57
+ asset = Asset(id="a", entity=Entity(name="ACME", key="ACME-Holding"))
58
+ assert asset.metadata[ENTITY_METADATA_KEY]["key"] == "acme-holding"
59
+
60
+
61
+ def test_an_entity_needs_a_urn_or_a_key() -> None:
62
+ # Without either the next scan would create the entity again.
63
+ with pytest.raises(ValueError, match="urn"):
64
+ Asset(id="a", entity=Entity(name="ACME"))
65
+
66
+
67
+ def test_blank_identifiers_are_dropped() -> None:
68
+ entity = Entity(name="ACME", identifiers={"vat": " ", "": "x", "iban": " AT61 "})
69
+ assert entity.identifiers == {"iban": "AT61"}
70
+
71
+
72
+ def test_bad_shapes_are_rejected_with_what_was_wanted() -> None:
73
+ with pytest.raises(ValueError, match="non-empty"):
74
+ Entity(name=" ")
75
+ with pytest.raises(ValueError, match="one of"):
76
+ Entity(name="ACME", type="COMPANY")
77
+ with pytest.raises(TypeError, match="label"):
78
+ Entity(name="ACME", identifiers=["ATU12345678"]) # type: ignore[arg-type]
79
+ with pytest.raises(TypeError, match="list of names"):
80
+ Entity(name="ACME", aliases="ACME Holding")
81
+ with pytest.raises(TypeError, match="Entity"):
82
+ Asset(id="a", urn="x://a", entity={"name": "ACME"}) # type: ignore[arg-type]