aignostics 0.2.231__tar.gz → 0.2.233__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 (202) hide show
  1. {aignostics-0.2.231 → aignostics-0.2.233}/PKG-INFO +14 -3
  2. {aignostics-0.2.231 → aignostics-0.2.233}/examples/notebook.py +1 -1
  3. {aignostics-0.2.231 → aignostics-0.2.233}/pyproject.toml +30 -21
  4. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/_service.py +47 -35
  5. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/__init__.py +2 -1
  6. aignostics-0.2.233/src/aignostics/utils/_di.py +113 -0
  7. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_gui.py +6 -0
  8. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/_cli.py +35 -0
  9. aignostics-0.2.231/src/aignostics/utils/_di.py +0 -76
  10. {aignostics-0.2.231 → aignostics-0.2.233}/.gitignore +0 -0
  11. {aignostics-0.2.231 → aignostics-0.2.233}/LICENSE +0 -0
  12. {aignostics-0.2.231 → aignostics-0.2.233}/README.md +0 -0
  13. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/.openapi-generator/FILES +0 -0
  14. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/.openapi-generator/VERSION +0 -0
  15. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/api/public_api.py +0 -0
  16. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/api_client.py +0 -0
  17. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/api_response.py +0 -0
  18. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/configuration.py +0 -0
  19. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/exceptions.py +0 -0
  20. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/__init__.py +0 -0
  21. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/application_read_response.py +0 -0
  22. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/application_read_short_response.py +0 -0
  23. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/application_version.py +0 -0
  24. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/artifact_output.py +0 -0
  25. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/artifact_state.py +0 -0
  26. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/artifact_termination_reason.py +0 -0
  27. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/auth0_organization.py +0 -0
  28. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/auth0_user.py +0 -0
  29. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/custom_metadata_update_request.py +0 -0
  30. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/http_validation_error.py +0 -0
  31. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/input_artifact.py +0 -0
  32. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/input_artifact_creation_request.py +0 -0
  33. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/item_creation_request.py +0 -0
  34. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/item_output.py +0 -0
  35. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/item_result_read_response.py +0 -0
  36. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/item_state.py +0 -0
  37. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/item_termination_reason.py +0 -0
  38. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/me_read_response.py +0 -0
  39. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/organization_read_response.py +0 -0
  40. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/output_artifact.py +0 -0
  41. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py +0 -0
  42. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/output_artifact_scope.py +0 -0
  43. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/output_artifact_visibility.py +0 -0
  44. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/run_creation_request.py +0 -0
  45. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/run_creation_response.py +0 -0
  46. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/run_item_statistics.py +0 -0
  47. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/run_output.py +0 -0
  48. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/run_read_response.py +0 -0
  49. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/run_state.py +0 -0
  50. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/run_termination_reason.py +0 -0
  51. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/user_read_response.py +0 -0
  52. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/validation_error.py +0 -0
  53. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/validation_error_loc_inner.py +0 -0
  54. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/models/version_read_response.py +0 -0
  55. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/aignx/codegen/rest.py +0 -0
  56. {aignostics-0.2.231 → aignostics-0.2.233}/codegen/out/docs/PublicApi.md +0 -0
  57. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-macos-15-intel/extracted/aignostics.app/Contents/Frameworks/boto3/examples/cloudfront.rst +0 -0
  58. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-macos-15-intel/extracted/aignostics.app/Contents/Frameworks/boto3/examples/s3.rst +0 -0
  59. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-macos-latest/extracted/aignostics.app/Contents/Frameworks/boto3/examples/cloudfront.rst +0 -0
  60. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-macos-latest/extracted/aignostics.app/Contents/Frameworks/boto3/examples/s3.rst +0 -0
  61. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-ubuntu-24.04-arm/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  62. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-ubuntu-24.04-arm/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  63. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-ubuntu-latest/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  64. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-ubuntu-latest/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  65. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-windows-latest/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  66. {aignostics-0.2.231 → aignostics-0.2.233}/dist_native_all/dist_native-windows-latest/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  67. {aignostics-0.2.231 → aignostics-0.2.233}/examples/__init__.py +0 -0
  68. {aignostics-0.2.231 → aignostics-0.2.233}/examples/notebook.ipynb +0 -0
  69. {aignostics-0.2.231 → aignostics-0.2.233}/examples/script.py +0 -0
  70. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/CLAUDE.md +0 -0
  71. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/__init__.py +0 -0
  72. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/CLAUDE.md +0 -0
  73. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/__init__.py +0 -0
  74. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_cli.py +0 -0
  75. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_download.py +0 -0
  76. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/_frame.py +0 -0
  77. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/_page_application_describe.py +0 -0
  78. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/_page_application_run_describe.py +0 -0
  79. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/_page_builder.py +0 -0
  80. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/_page_index.py +0 -0
  81. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/_utils.py +0 -0
  82. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/.gitignore +0 -0
  83. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/canceled.lottie +0 -0
  84. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/done.lottie +0 -0
  85. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/empty.lottie +0 -0
  86. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/error.lottie +0 -0
  87. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/error_alt.lottie +0 -0
  88. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/home-card-1.png +0 -0
  89. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/home-card-2.png +0 -0
  90. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/home-card-3.png +0 -0
  91. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/home-card-4.png +0 -0
  92. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/home-card-5.png +0 -0
  93. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/image-not-found.png +0 -0
  94. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/pending.lottie +0 -0
  95. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/processing.lottie +0 -0
  96. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/ruo.png +0 -0
  97. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_gui/assets/ruo.svg +0 -0
  98. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_models.py +0 -0
  99. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_service.py +0 -0
  100. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_settings.py +0 -0
  101. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/application/_utils.py +0 -0
  102. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/CLAUDE.md +0 -0
  103. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/__init__.py +0 -0
  104. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/_cli.py +0 -0
  105. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/_gui.py +0 -0
  106. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/_service.py +0 -0
  107. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/_settings.py +0 -0
  108. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/assets/Google-Cloud-logo.png +0 -0
  109. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/bucket/assets/Google-Cloud-logo.svg +0 -0
  110. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/cli.py +0 -0
  111. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/constants.py +0 -0
  112. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/dataset/CLAUDE.md +0 -0
  113. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/dataset/__init__.py +0 -0
  114. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/dataset/_cli.py +0 -0
  115. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/dataset/_gui.py +0 -0
  116. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/dataset/_service.py +0 -0
  117. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/dataset/assets/NIH-IDC-logo.svg +0 -0
  118. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/CLAUDE.md +0 -0
  119. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/__init__.py +0 -0
  120. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/_error.py +0 -0
  121. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/_frame.py +0 -0
  122. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/_theme.py +0 -0
  123. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 +0 -0
  124. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/assets/cat.lottie +0 -0
  125. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/gui/assets/logo.png +0 -0
  126. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/notebook/CLAUDE.md +0 -0
  127. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/notebook/__init__.py +0 -0
  128. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/notebook/_gui.py +0 -0
  129. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/notebook/_notebook.py +0 -0
  130. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/notebook/_service.py +0 -0
  131. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/notebook/assets/python.lottie +0 -0
  132. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/CLAUDE.md +0 -0
  133. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/__init__.py +0 -0
  134. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_authentication.py +0 -0
  135. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_cli.py +0 -0
  136. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_client.py +0 -0
  137. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_constants.py +0 -0
  138. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_messages.py +0 -0
  139. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_operation_cache.py +0 -0
  140. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_sdk_metadata.py +0 -0
  141. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_service.py +0 -0
  142. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_settings.py +0 -0
  143. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/_utils.py +0 -0
  144. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/resources/__init__.py +0 -0
  145. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/resources/applications.py +0 -0
  146. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/resources/runs.py +0 -0
  147. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/platform/resources/utils.py +0 -0
  148. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/CLAUDE.md +0 -0
  149. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/__init__.py +0 -0
  150. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/_cli.py +0 -0
  151. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/_gui.py +0 -0
  152. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/_settings.py +0 -0
  153. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/assets/download.lottie +0 -0
  154. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/assets/microscope.lottie +0 -0
  155. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/assets/update.lottie +0 -0
  156. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/scripts/add.groovy +0 -0
  157. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/scripts/annotate.groovy +0 -0
  158. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/scripts/inspect.groovy +0 -0
  159. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/qupath/scripts/test.groovy +0 -0
  160. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/CLAUDE.md +0 -0
  161. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/__init__.py +0 -0
  162. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/_cli.py +0 -0
  163. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/_exceptions.py +0 -0
  164. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/_gui.py +0 -0
  165. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/_service.py +0 -0
  166. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/_settings.py +0 -0
  167. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/system/assets/system.lottie +0 -0
  168. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/bottle.py +0 -0
  169. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/idc_index.py +0 -0
  170. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/__init__.py +0 -0
  171. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/argumentsparse.py +0 -0
  172. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/constants.py +0 -0
  173. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/py.typed +0 -0
  174. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/showinfm.py +0 -0
  175. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/system/__init__.py +0 -0
  176. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/system/linux.py +0 -0
  177. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/system/tools.py +0 -0
  178. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/system/urivalidate.py +0 -0
  179. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/third_party/showinfm/system/windows.py +0 -0
  180. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/CLAUDE.md +0 -0
  181. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_cli.py +0 -0
  182. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_console.py +0 -0
  183. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_constants.py +0 -0
  184. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_fs.py +0 -0
  185. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_health.py +0 -0
  186. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_log.py +0 -0
  187. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_notebook.py +0 -0
  188. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_process.py +0 -0
  189. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_sentry.py +0 -0
  190. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_service.py +0 -0
  191. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_settings.py +0 -0
  192. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/_user_agent.py +0 -0
  193. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/utils/boot.py +0 -0
  194. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/CLAUDE.md +0 -0
  195. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/__init__.py +0 -0
  196. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/_gui.py +0 -0
  197. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/_openslide_handler.py +0 -0
  198. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/_pydicom_handler.py +0 -0
  199. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/_service.py +0 -0
  200. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/_utils.py +0 -0
  201. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics/wsi/assets/fallback.png +0 -0
  202. {aignostics-0.2.231 → aignostics-0.2.233}/src/aignostics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aignostics
3
- Version: 0.2.231
3
+ Version: 0.2.233
4
4
  Summary: 🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library.
5
5
  Project-URL: Homepage, https://aignostics.readthedocs.io/en/latest/
6
6
  Project-URL: Documentation, https://aignostics.readthedocs.io/en/latest/
@@ -49,6 +49,7 @@ Classifier: Programming Language :: Python :: 3.13
49
49
  Classifier: Programming Language :: Python :: 3.14
50
50
  Classifier: Typing :: Typed
51
51
  Requires-Python: <3.15,>=3.11
52
+ Requires-Dist: aiohttp>=3.12.14
52
53
  Requires-Dist: boto3<2,>=1.42.4
53
54
  Requires-Dist: certifi>=2025.11.12
54
55
  Requires-Dist: crc32c<3,>=2.8
@@ -59,7 +60,8 @@ Requires-Dist: duckdb<=2,>=1.4.2
59
60
  Requires-Dist: fastapi[all,standard]>=0.123.10
60
61
  Requires-Dist: fastparquet>=2024.11.0; python_version < '3.14'
61
62
  Requires-Dist: google-cloud-storage<4,>=3.6.0
62
- Requires-Dist: highdicom<1,>=0.26.1
63
+ Requires-Dist: h11>=0.16.0
64
+ Requires-Dist: highdicom<1,>=0.26.1; python_version < '3.14'
63
65
  Requires-Dist: html-sanitizer<3,>=2.6.0
64
66
  Requires-Dist: httpx<1,>=0.28.1
65
67
  Requires-Dist: humanize<5,>=4.14.0
@@ -68,11 +70,13 @@ Requires-Dist: ijson<4,>=3.4.0.post0
68
70
  Requires-Dist: jsf<1,>=0.11.2
69
71
  Requires-Dist: jsonschema[format-nongpl]<5,>=4.25.1
70
72
  Requires-Dist: loguru<1,>=0.7.3
73
+ Requires-Dist: lxml>=6.0.2
71
74
  Requires-Dist: nicegui[native]<4,>=3.4.0
72
75
  Requires-Dist: openslide-bin<5,>=4.0.0.10
73
76
  Requires-Dist: openslide-python<2,>=1.4.3
74
77
  Requires-Dist: packaging<26,>=25.0
75
78
  Requires-Dist: pandas<3,>=2.3.3
79
+ Requires-Dist: pillow>=11.3.0
76
80
  Requires-Dist: platformdirs<5,>=4.3.2
77
81
  Requires-Dist: platformdirs<5,>=4.5.1
78
82
  Requires-Dist: procrastinate>=3.5.3
@@ -85,23 +89,30 @@ Requires-Dist: pywin32<312,>=311; sys_platform == 'win32'
85
89
  Requires-Dist: pyyaml<7,>=6.0.3
86
90
  Requires-Dist: requests-oauthlib<3,>=2.0.0
87
91
  Requires-Dist: requests<3,>=2.32.5
92
+ Requires-Dist: rfc3987; sys_platform == 'never'
88
93
  Requires-Dist: s5cmd<1,>=0.3.3
89
94
  Requires-Dist: semver<4,>=3.0.4
90
95
  Requires-Dist: sentry-sdk<3,>=2.47.0
91
96
  Requires-Dist: shapely<3,>=2.1.2
97
+ Requires-Dist: starlette>=0.47.2
98
+ Requires-Dist: starlette>=0.49.1
92
99
  Requires-Dist: tenacity<10,>=9.1.2
100
+ Requires-Dist: tornado>=6.5.0
93
101
  Requires-Dist: tqdm<5,>=4.67.1
94
102
  Requires-Dist: truststore<1,>=0.10.4
95
103
  Requires-Dist: typer<1,>=0.20.0
96
104
  Requires-Dist: uptime<4,>=3.0.1
97
105
  Requires-Dist: urllib3<3,>=2.6.1
106
+ Requires-Dist: urllib3>=2.5.0
98
107
  Requires-Dist: wsidicom<1,>=0.28.1
99
108
  Provides-Extra: jupyter
109
+ Requires-Dist: jupyter-core>=5.8.1; extra == 'jupyter'
100
110
  Requires-Dist: jupyter<2,>=1.1.1; extra == 'jupyter'
111
+ Requires-Dist: jupyterlab>=4.4.9; extra == 'jupyter'
101
112
  Provides-Extra: marimo
102
113
  Requires-Dist: cloudpathlib<1,>=0.23.0; extra == 'marimo'
103
114
  Requires-Dist: ipython<10,>=9.8.0; extra == 'marimo'
104
- Requires-Dist: marimo<1,>=0.18.3; extra == 'marimo'
115
+ Requires-Dist: marimo<1,>=0.18.4; extra == 'marimo'
105
116
  Requires-Dist: matplotlib<4,>=3.10.7; extra == 'marimo'
106
117
  Requires-Dist: shapely<3,>=2.1.0; extra == 'marimo'
107
118
  Provides-Extra: pyinstaller
@@ -2,7 +2,7 @@
2
2
  # requires-python = ">=3.13"
3
3
  # dependencies = [
4
4
  # "marimo",
5
- # "aignostics==0.2.231",
5
+ # "aignostics==0.2.233",
6
6
  # ]
7
7
  # ///
8
8
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aignostics"
3
- version = "0.2.231"
3
+ version = "0.2.233"
4
4
  description = "🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -71,6 +71,7 @@ classifiers = [
71
71
  "Natural Language :: English",
72
72
  ]
73
73
 
74
+ # WARNING: The upper bound of requires-python is *not* respected by uvx
74
75
  requires-python = ">=3.11, <3.15"
75
76
 
76
77
  dependencies = [
@@ -96,7 +97,7 @@ dependencies = [
96
97
  "duckdb>=1.4.2,<=2",
97
98
  "google-cloud-storage>=3.6.0,<4",
98
99
  "crc32c>=2.8,<3", # TODO(Helmut): Remove and back to google_crc32c when that supports Python 3.14
99
- "highdicom>=0.26.1,<1",
100
+ "highdicom>=0.26.1,<1; python_version < '3.14'", # transitive dependency pyjpegls not yet supporting Python 3.14
100
101
  "html-sanitizer>=2.6.0,<3",
101
102
  "httpx>=0.28.1,<1",
102
103
  "idc-index-data==23.0.1",
@@ -124,15 +125,32 @@ dependencies = [
124
125
  "truststore>=0.10.4,<1",
125
126
  "urllib3>=2.6.1,<3",
126
127
  "wsidicom>=0.28.1,<1",
128
+ # Transitive overrides
129
+ # WARNING: one cannot negate or downgrade a dependency required here. use override-dependencies for that.
130
+ "rfc3987; sys_platform == 'never'", # GPLv3
131
+ "h11>=0.16.0", # CVE-2025-43859
132
+ "tornado>=6.5.0", # CVE-2025-47287
133
+ "urllib3>=2.5.0", # CVE-2025-50181, CVE-2025-50182,
134
+ "pillow>=11.3.0", # CVE-2025-48379,
135
+ "aiohttp>=3.12.14", # CVE-2025-53643
136
+ "starlette>=0.47.2", # CVE-2025-54121
137
+ "starlette>=0.49.1", # GHSA-7f5h-v6xp-fcq8
138
+ "lxml>=6.0.2", # For python 3.14 pre-built wheels
127
139
  ]
128
140
 
129
141
  [project.optional-dependencies]
130
142
  pyinstaller = ["pyinstaller>=6.14.0,<7"]
131
- jupyter = ["jupyter>=1.1.1,<2"]
143
+ jupyter = [
144
+ "jupyter>=1.1.1,<2",
145
+ # Transitive overrides
146
+ # WARNING: one cannot negate or downgrade a dependency required here. use override-dependencies for that.
147
+ "jupyter-core>=5.8.1", # CVE-2025-30167
148
+ "jupyterlab>=4.4.9", # CVE-2025-59842
149
+ ]
132
150
  marimo = [
133
151
  "cloudpathlib>=0.23.0,<1",
134
152
  "ipython>=9.8.0,<10",
135
- "marimo>=0.18.3,<1",
153
+ "marimo>=0.18.4,<1",
136
154
  "matplotlib>=3.10.7,<4",
137
155
  "shapely>=2.1.0,<3",
138
156
  ]
@@ -185,27 +203,18 @@ dev = [
185
203
  "types-pyyaml>=6.0.12.20250915,<7",
186
204
  "types-requests>=2.32.4.20250913,<3",
187
205
  "watchdog>=6.0.0,<7",
206
+ # Transitive overrides
207
+ # WARNING: one cannot negate or downgrade a dependency required here. use override-dependencies for that.
208
+ "pip>=5.3", # CVE-2025-8869
209
+ "uv>=0.9.7", # CVE-2025-54368, GHSA-w476-p2h3-79g9, GHSA-pqhf-p39g-3x64
210
+ "fonttools>=4.60.2", # CVE-2025-66034 (GHSA-768j-98cg-p3fv), dep of matplotlib
188
211
  ]
189
212
 
190
213
  [tool.uv]
191
214
  required-version = ">=0.9.7" # CVE-2025-54368, GHSA-w476-p2h3-79g9, GHSA-pqhf-p39g-3x64
215
+ # WARNING: override-dependencies is *not* respected by uvx
192
216
  override-dependencies = [ # https://github.com/astral-sh/uv/issues/4422
193
- "rfc3987; sys_platform == 'never'", # GPLv3
194
- "h11>=0.16.0", # CVE-2025-43859
195
- "tornado>=6.5.0", # CVE-2025-47287
196
- "jupyter-core>=5.8.1", # CVE-2025-30167
197
- "urllib3>=2.5.0", # CVE-2025-50181, CVE-2025-50182,
198
- "pillow>=11.3.0", # CVE-2025-48379,
199
- "aiohttp>=3.12.14", # CVE-2025-53643
200
- "starlette>=0.47.2", # CVE-2025-54121
201
- "uv>=0.9.7", # CVE-2025-54368, GHSA-w476-p2h3-79g9, GHSA-pqhf-p39g-3x64
202
- "jupyterlab>=4.4.9", # CVE-2025-59842
203
- "pip>=5.3", # CVE-2025-8869
204
- "starlette>=0.49.1", # GHSA-7f5h-v6xp-fcq8
205
- "fonttools>=4.60.2", # CVE-2025-66034 (GHSA-768j-98cg-p3fv), dep of matplotlib
206
- "pyjpegls; python_version < '3.14'", # No Python 3.14 support yet
207
- "pytest>=9.0.1", # pytest-md-report depends on pytest<9 unnecessarily
208
- "lxml>=6.0.2", # For python 3.14 pre-built wheels
217
+ "pytest>=9.0.1", # pytest-md-report depends on pytest<9 unnecessarily
209
218
  ]
210
219
 
211
220
  [tool.uv.sources]
@@ -386,7 +395,7 @@ source = ["src/"]
386
395
 
387
396
 
388
397
  [tool.bumpversion]
389
- current_version = "0.2.231"
398
+ current_version = "0.2.233"
390
399
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
391
400
  serialize = ["{major}.{minor}.{patch}"]
392
401
  search = "{current_version}"
@@ -42,6 +42,7 @@ QUPATH_SCRIPT_MAX_EXECUTION_TIME = 60 * 60 * 2 # seconds, maximum wait time for
42
42
 
43
43
  PROJECT_FILENAME = "project.qpproj"
44
44
  ANNOTATIONS_BATCH_SIZE = 500000
45
+ JSON_SUFFIX = ".json"
45
46
 
46
47
 
47
48
  class QuPathVersion(BaseModel):
@@ -1124,40 +1125,46 @@ class Service(BaseService):
1124
1125
  progress_callable(progress)
1125
1126
 
1126
1127
  # We communicate via file I/O with the Groovy script running within QuPath
1127
- with (
1128
- tempfile.NamedTemporaryFile(mode="w", suffix=".json", encoding="utf-8") as paths_file,
1129
- tempfile.NamedTemporaryFile(mode="w", suffix=".json", encoding="utf-8") as output_file,
1130
- ):
1131
- try:
1132
- json.dump([str(path.resolve()) for path in paths], paths_file.file)
1133
- paths_file.flush()
1128
+ # Use delete=False to avoid Windows file locking issues - the file must be closed
1129
+ # before QuPath can read it, but NamedTemporaryFile keeps files locked while open on Windows
1130
+ paths_file = tempfile.NamedTemporaryFile(mode="w", suffix=JSON_SUFFIX, encoding="utf-8", delete=False) # noqa: SIM115
1131
+ output_file = tempfile.NamedTemporaryFile(mode="w", suffix=JSON_SUFFIX, encoding="utf-8", delete=False) # noqa: SIM115
1132
+ try:
1133
+ # Write paths and close file so QuPath can read it
1134
+ json.dump([str(path.resolve()) for path in paths], paths_file)
1135
+ paths_file.close()
1136
+ output_file.close()
1134
1137
 
1135
- pid = Service.execute_qupath(
1136
- script=Service._find_groovy_script("add"),
1137
- script_args=[str(project), str(paths_file.name), str(output_file.name)],
1138
- )
1138
+ pid = Service.execute_qupath(
1139
+ script=Service._find_groovy_script("add"),
1140
+ script_args=[str(project), paths_file.name, output_file.name],
1141
+ )
1139
1142
 
1140
- if not pid:
1141
- message = "Failed to execute QuPath script for adding images."
1142
- logger.error(message)
1143
- raise RuntimeError(message) # noqa: TRY301
1144
-
1145
- with Path(output_file.name).open("r", encoding="utf-8") as f:
1146
- result_data = json.load(f)
1147
- added_count = int(result_data.get("added_count", 0))
1148
- errors = result_data.get("errors", [])
1149
- for error in errors:
1150
- logger.warning(f"QuPath add script error: {error}")
1151
-
1152
- if progress_callable:
1153
- progress.status = AddProgressState.COMPLETED
1154
- progress_callable(progress)
1155
-
1156
- return added_count
1157
- except Exception as e:
1158
- message = f"Failed to add images to QuPath project: {e!s}"
1159
- logger.exception(message)
1160
- raise RuntimeError(message) from e
1143
+ if not pid:
1144
+ message = "Failed to execute QuPath script for adding images."
1145
+ logger.error(message)
1146
+ raise RuntimeError(message) # noqa: TRY301
1147
+
1148
+ with Path(output_file.name).open("r", encoding="utf-8") as f:
1149
+ result_data = json.load(f)
1150
+ added_count = int(result_data.get("added_count", 0))
1151
+ errors = result_data.get("errors", [])
1152
+ for error in errors:
1153
+ logger.warning(f"QuPath add script error: {error}")
1154
+
1155
+ if progress_callable:
1156
+ progress.status = AddProgressState.COMPLETED
1157
+ progress_callable(progress)
1158
+
1159
+ return added_count
1160
+ except Exception as e:
1161
+ message = f"Failed to add images to QuPath project: {e!s}"
1162
+ logger.exception(message)
1163
+ raise RuntimeError(message) from e
1164
+ finally:
1165
+ # Clean up temp files
1166
+ Path(paths_file.name).unlink(missing_ok=True)
1167
+ Path(output_file.name).unlink(missing_ok=True)
1161
1168
 
1162
1169
  @staticmethod
1163
1170
  def annotate(
@@ -1246,9 +1253,12 @@ class Service(BaseService):
1246
1253
  RuntimeError: If there is an error inspecting the project.
1247
1254
  """
1248
1255
  # We communicate via file I/O with the Groovy script running within QuPath
1249
- with tempfile.NamedTemporaryFile(mode="w+", suffix=".json", encoding="utf-8") as temp_file:
1250
- output_file = Path(temp_file.name).resolve()
1251
-
1256
+ # Use delete=False to avoid Windows file locking issues - the file must be closed
1257
+ # before QuPath can write to it, but NamedTemporaryFile keeps files locked while open on Windows
1258
+ temp_file = tempfile.NamedTemporaryFile(mode="w+", suffix=JSON_SUFFIX, encoding="utf-8", delete=False) # noqa: SIM115
1259
+ output_file = Path(temp_file.name).resolve()
1260
+ temp_file.close()
1261
+ try:
1252
1262
  pid = Service.execute_qupath(
1253
1263
  quiet=True,
1254
1264
  project=project,
@@ -1267,3 +1277,5 @@ class Service(BaseService):
1267
1277
  raise RuntimeError(message)
1268
1278
 
1269
1279
  return QuPathProject.model_validate_json(output_file.read_text(encoding="utf-8"))
1280
+ finally:
1281
+ output_file.unlink(missing_ok=True)
@@ -22,7 +22,7 @@ from ._constants import (
22
22
  __version__,
23
23
  __version_full__,
24
24
  )
25
- from ._di import load_modules, locate_implementations, locate_subclasses
25
+ from ._di import discover_plugin_packages, load_modules, locate_implementations, locate_subclasses
26
26
  from ._fs import get_user_data_directory, open_user_data_directory, sanitize_path, sanitize_path_component
27
27
  from ._health import Health
28
28
  from ._log import LogSettings
@@ -60,6 +60,7 @@ __all__ = [
60
60
  "__version_full__",
61
61
  "boot",
62
62
  "console",
63
+ "discover_plugin_packages",
63
64
  "get_process_info",
64
65
  "get_user_data_directory",
65
66
  "load_modules",
@@ -0,0 +1,113 @@
1
+ """Module for dynamic import and discovery of implementations and subclasses."""
2
+
3
+ import importlib
4
+ import pkgutil
5
+ from functools import lru_cache
6
+ from importlib.metadata import entry_points
7
+ from inspect import isclass
8
+ from typing import Any
9
+
10
+ from ._constants import __project_name__
11
+
12
+ _implementation_cache: dict[Any, list[Any]] = {}
13
+ _subclass_cache: dict[Any, list[Any]] = {}
14
+
15
+ # Entry point group name for aignostics plugins
16
+ PLUGIN_ENTRY_POINT_GROUP = "aignostics.plugins"
17
+
18
+
19
+ @lru_cache(maxsize=1)
20
+ def discover_plugin_packages() -> tuple[str, ...]:
21
+ """
22
+ Discover plugin packages using entry points.
23
+
24
+ Plugins register themselves in their pyproject.toml:
25
+
26
+ [project.entry-points."aignostics.plugins"]
27
+ my_plugin = "my_plugin"
28
+
29
+ Results are cached after the first call.
30
+
31
+ Returns:
32
+ tuple[str, ...]: Tuple of discovered plugin package names.
33
+ """
34
+ eps = entry_points(group=PLUGIN_ENTRY_POINT_GROUP)
35
+ return tuple(ep.value for ep in eps)
36
+
37
+
38
+ def load_modules() -> None:
39
+ package = importlib.import_module(__project_name__)
40
+ for _, name, _ in pkgutil.iter_modules(package.__path__):
41
+ importlib.import_module(f"{__project_name__}.{name}")
42
+
43
+
44
+ def locate_implementations(_class: type[Any]) -> list[Any]:
45
+ """
46
+ Dynamically discover all instances of some class.
47
+
48
+ Searches in the main project and all plugins registered via entry points.
49
+
50
+ Args:
51
+ _class (type[Any]): Class to search for.
52
+
53
+ Returns:
54
+ list[Any]: List of discovered implementations of the given class.
55
+ """
56
+ if _class in _implementation_cache:
57
+ return _implementation_cache[_class]
58
+
59
+ plugin_packages = discover_plugin_packages()
60
+
61
+ implementations = []
62
+ for package_name in [*plugin_packages, __project_name__]:
63
+ package = importlib.import_module(package_name)
64
+
65
+ for _, name, _ in pkgutil.iter_modules(package.__path__):
66
+ module = importlib.import_module(f"{package_name}.{name}")
67
+ # Check all members of the module
68
+ for member_name in dir(module):
69
+ member = getattr(module, member_name)
70
+ if isinstance(member, _class):
71
+ implementations.append(member)
72
+
73
+ _implementation_cache[_class] = implementations
74
+ return implementations
75
+
76
+
77
+ def locate_subclasses(_class: type[Any]) -> list[Any]:
78
+ """
79
+ Dynamically discover all classes that are subclasses of some type.
80
+
81
+ Searches in the main project and all plugins registered via entry points.
82
+
83
+ Args:
84
+ _class (type[Any]): Parent class of subclasses to search for.
85
+
86
+ Returns:
87
+ list[type[Any]]: List of discovered subclasses of the given class.
88
+ """
89
+ if _class in _subclass_cache:
90
+ return _subclass_cache[_class]
91
+
92
+ plugin_packages = discover_plugin_packages()
93
+
94
+ subclasses = []
95
+ for package_name in [*plugin_packages, __project_name__]:
96
+ try:
97
+ package = importlib.import_module(package_name)
98
+ except ImportError:
99
+ continue
100
+
101
+ for _, name, _ in pkgutil.iter_modules(package.__path__):
102
+ try:
103
+ module = importlib.import_module(f"{package_name}.{name}")
104
+ # Check all members of the module
105
+ for member_name in dir(module):
106
+ member = getattr(module, member_name)
107
+ if isclass(member) and issubclass(member, _class) and member != _class:
108
+ subclasses.append(member)
109
+ except ImportError:
110
+ continue
111
+
112
+ _subclass_cache[_class] = subclasses
113
+ return subclasses
@@ -73,6 +73,12 @@ def gui_run( # noqa: PLR0913, PLR0917
73
73
  native = False
74
74
  show = True
75
75
 
76
+ # On Windows with python 3.14 don't use native mode due to pythonnet not yet
77
+ # supported
78
+ if native and platform.system() == "Windows" and platform.python_version_tuple() >= ("3", "14"):
79
+ native = False
80
+ show = True
81
+
76
82
  gui_register_pages()
77
83
 
78
84
  ui.run(
@@ -12,6 +12,33 @@ from aignostics.utils import console
12
12
  from ._service import Service
13
13
  from ._utils import print_slide_info, print_study_info
14
14
 
15
+ # Python version for highdicom compatibility check
16
+ _PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}"
17
+ _HIGHDICOM_UNSUPPORTED_VERSIONS = {"3.14"}
18
+
19
+
20
+ def _check_highdicom_available() -> bool:
21
+ """Check if highdicom is available (not supported on Python 3.14+).
22
+
23
+ Returns:
24
+ True if highdicom can be imported, False otherwise.
25
+ """
26
+ try:
27
+ from ._pydicom_handler import PydicomHandler # noqa: PLC0415, F401
28
+
29
+ return True
30
+ except ImportError:
31
+ return False
32
+
33
+
34
+ def _print_highdicom_unsupported_error() -> None:
35
+ """Print error message when highdicom is not available."""
36
+ console.print(f"[red]This command requires 'highdicom' which is not available on Python {_PYTHON_VERSION}.[/red]")
37
+ console.print("[yellow]Please run with Python 3.13 or earlier:[/yellow]")
38
+ console.print("[green] uvx -p 3.13 aignostics wsi dicom <command> ...[/green]")
39
+ sys.exit(1)
40
+
41
+
15
42
  cli = typer.Typer(name="wsi", help="Operations on whole slide images.")
16
43
 
17
44
 
@@ -107,6 +134,10 @@ def dicom_inspect(
107
134
  summary: Annotated[bool, typer.Option(help="Show only summary information")] = False,
108
135
  ) -> None: # pylint: disable=W0613
109
136
  """Inspect DICOM files at any hierarchy level."""
137
+ if not _check_highdicom_available():
138
+ _print_highdicom_unsupported_error()
139
+ return
140
+
110
141
  from ._pydicom_handler import PydicomHandler # noqa: PLC0415
111
142
 
112
143
  try:
@@ -139,6 +170,10 @@ def dicom_geojson_import(
139
170
  geojson_path: Annotated[Path, typer.Argument(help="Path to the GeoJSON file", exists=True)],
140
171
  ) -> None: # pylint: disable=W0613
141
172
  """Import GeoJSON annotations into DICOM ANN instance."""
173
+ if not _check_highdicom_available():
174
+ _print_highdicom_unsupported_error()
175
+ return
176
+
142
177
  from ._pydicom_handler import PydicomHandler # noqa: PLC0415
143
178
 
144
179
  try:
@@ -1,76 +0,0 @@
1
- """Module for dynamic import and discovery of implementations and subclasses."""
2
-
3
- import importlib
4
- import pkgutil
5
- from inspect import isclass
6
- from typing import Any
7
-
8
- from ._constants import __project_name__
9
-
10
- _implementation_cache: dict[Any, list[Any]] = {}
11
- _subclass_cache: dict[Any, list[Any]] = {}
12
-
13
-
14
- def load_modules() -> None:
15
- package = importlib.import_module(__project_name__)
16
- for _, name, _ in pkgutil.iter_modules(package.__path__):
17
- importlib.import_module(f"{__project_name__}.{name}")
18
-
19
-
20
- def locate_implementations(_class: type[Any]) -> list[Any]:
21
- """
22
- Dynamically discover all instances of some class.
23
-
24
- Args:
25
- _class (type[Any]): Class to search for.
26
-
27
- Returns:
28
- list[Any]: List of discovered implementations of the given class.
29
- """
30
- if _class in _implementation_cache:
31
- return _implementation_cache[_class]
32
-
33
- implementations = []
34
- package = importlib.import_module(__project_name__)
35
-
36
- for _, name, _ in pkgutil.iter_modules(package.__path__):
37
- module = importlib.import_module(f"{__project_name__}.{name}")
38
- # Check all members of the module
39
- for member_name in dir(module):
40
- member = getattr(module, member_name)
41
- if isinstance(member, _class):
42
- implementations.append(member)
43
-
44
- _implementation_cache[_class] = implementations
45
- return implementations
46
-
47
-
48
- def locate_subclasses(_class: type[Any]) -> list[Any]:
49
- """
50
- Dynamically discover all classes that are subclasses of some type.
51
-
52
- Args:
53
- _class (type[Any]): Parent class of subclasses to search for.
54
-
55
- Returns:
56
- list[type[Any]]: List of discovered subclasses of the given class.
57
- """
58
- if _class in _subclass_cache:
59
- return _subclass_cache[_class]
60
-
61
- subclasses = []
62
- package = importlib.import_module(__project_name__)
63
-
64
- for _, name, _ in pkgutil.iter_modules(package.__path__):
65
- try:
66
- module = importlib.import_module(f"{__project_name__}.{name}")
67
- # Check all members of the module
68
- for member_name in dir(module):
69
- member = getattr(module, member_name)
70
- if isclass(member) and issubclass(member, _class) and member != _class:
71
- subclasses.append(member)
72
- except ImportError:
73
- continue
74
-
75
- _subclass_cache[_class] = subclasses
76
- return subclasses
File without changes
File without changes
File without changes