aignostics 0.2.232__tar.gz → 0.2.234__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 (203) hide show
  1. {aignostics-0.2.232 → aignostics-0.2.234}/PKG-INFO +1 -1
  2. {aignostics-0.2.232 → aignostics-0.2.234}/examples/notebook.py +1 -1
  3. {aignostics-0.2.232 → aignostics-0.2.234}/pyproject.toml +2 -2
  4. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/_frame.py +35 -0
  5. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/_service.py +47 -35
  6. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/__init__.py +7 -1
  7. aignostics-0.2.234/src/aignostics/utils/_di.py +113 -0
  8. aignostics-0.2.234/src/aignostics/utils/_nav.py +162 -0
  9. aignostics-0.2.232/src/aignostics/utils/_di.py +0 -76
  10. {aignostics-0.2.232 → aignostics-0.2.234}/.gitignore +0 -0
  11. {aignostics-0.2.232 → aignostics-0.2.234}/LICENSE +0 -0
  12. {aignostics-0.2.232 → aignostics-0.2.234}/README.md +0 -0
  13. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/.openapi-generator/FILES +0 -0
  14. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/.openapi-generator/VERSION +0 -0
  15. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/api/public_api.py +0 -0
  16. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/api_client.py +0 -0
  17. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/api_response.py +0 -0
  18. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/configuration.py +0 -0
  19. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/exceptions.py +0 -0
  20. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/__init__.py +0 -0
  21. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/application_read_response.py +0 -0
  22. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/application_read_short_response.py +0 -0
  23. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/application_version.py +0 -0
  24. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/artifact_output.py +0 -0
  25. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/artifact_state.py +0 -0
  26. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/artifact_termination_reason.py +0 -0
  27. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/auth0_organization.py +0 -0
  28. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/auth0_user.py +0 -0
  29. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/custom_metadata_update_request.py +0 -0
  30. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/http_validation_error.py +0 -0
  31. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/input_artifact.py +0 -0
  32. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/input_artifact_creation_request.py +0 -0
  33. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/item_creation_request.py +0 -0
  34. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/item_output.py +0 -0
  35. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/item_result_read_response.py +0 -0
  36. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/item_state.py +0 -0
  37. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/item_termination_reason.py +0 -0
  38. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/me_read_response.py +0 -0
  39. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/organization_read_response.py +0 -0
  40. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/output_artifact.py +0 -0
  41. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py +0 -0
  42. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/output_artifact_scope.py +0 -0
  43. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/output_artifact_visibility.py +0 -0
  44. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/run_creation_request.py +0 -0
  45. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/run_creation_response.py +0 -0
  46. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/run_item_statistics.py +0 -0
  47. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/run_output.py +0 -0
  48. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/run_read_response.py +0 -0
  49. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/run_state.py +0 -0
  50. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/run_termination_reason.py +0 -0
  51. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/user_read_response.py +0 -0
  52. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/validation_error.py +0 -0
  53. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/validation_error_loc_inner.py +0 -0
  54. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/models/version_read_response.py +0 -0
  55. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/aignx/codegen/rest.py +0 -0
  56. {aignostics-0.2.232 → aignostics-0.2.234}/codegen/out/docs/PublicApi.md +0 -0
  57. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-macos-15-intel/extracted/aignostics.app/Contents/Frameworks/boto3/examples/cloudfront.rst +0 -0
  58. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-macos-15-intel/extracted/aignostics.app/Contents/Frameworks/boto3/examples/s3.rst +0 -0
  59. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-macos-latest/extracted/aignostics.app/Contents/Frameworks/boto3/examples/cloudfront.rst +0 -0
  60. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-macos-latest/extracted/aignostics.app/Contents/Frameworks/boto3/examples/s3.rst +0 -0
  61. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-ubuntu-24.04-arm/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  62. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-ubuntu-24.04-arm/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  63. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-ubuntu-latest/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  64. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-ubuntu-latest/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  65. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-windows-latest/extracted/aignostics/_internal/boto3/examples/cloudfront.rst +0 -0
  66. {aignostics-0.2.232 → aignostics-0.2.234}/dist_native_all/dist_native-windows-latest/extracted/aignostics/_internal/boto3/examples/s3.rst +0 -0
  67. {aignostics-0.2.232 → aignostics-0.2.234}/examples/__init__.py +0 -0
  68. {aignostics-0.2.232 → aignostics-0.2.234}/examples/notebook.ipynb +0 -0
  69. {aignostics-0.2.232 → aignostics-0.2.234}/examples/script.py +0 -0
  70. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/CLAUDE.md +0 -0
  71. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/__init__.py +0 -0
  72. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/CLAUDE.md +0 -0
  73. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/__init__.py +0 -0
  74. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_cli.py +0 -0
  75. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_download.py +0 -0
  76. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/_frame.py +0 -0
  77. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/_page_application_describe.py +0 -0
  78. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/_page_application_run_describe.py +0 -0
  79. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/_page_builder.py +0 -0
  80. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/_page_index.py +0 -0
  81. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/_utils.py +0 -0
  82. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/.gitignore +0 -0
  83. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/canceled.lottie +0 -0
  84. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/done.lottie +0 -0
  85. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/empty.lottie +0 -0
  86. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/error.lottie +0 -0
  87. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/error_alt.lottie +0 -0
  88. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/home-card-1.png +0 -0
  89. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/home-card-2.png +0 -0
  90. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/home-card-3.png +0 -0
  91. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/home-card-4.png +0 -0
  92. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/home-card-5.png +0 -0
  93. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/image-not-found.png +0 -0
  94. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/pending.lottie +0 -0
  95. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/processing.lottie +0 -0
  96. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/ruo.png +0 -0
  97. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_gui/assets/ruo.svg +0 -0
  98. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_models.py +0 -0
  99. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_service.py +0 -0
  100. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_settings.py +0 -0
  101. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/application/_utils.py +0 -0
  102. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/CLAUDE.md +0 -0
  103. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/__init__.py +0 -0
  104. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/_cli.py +0 -0
  105. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/_gui.py +0 -0
  106. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/_service.py +0 -0
  107. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/_settings.py +0 -0
  108. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/assets/Google-Cloud-logo.png +0 -0
  109. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/bucket/assets/Google-Cloud-logo.svg +0 -0
  110. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/cli.py +0 -0
  111. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/constants.py +0 -0
  112. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/dataset/CLAUDE.md +0 -0
  113. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/dataset/__init__.py +0 -0
  114. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/dataset/_cli.py +0 -0
  115. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/dataset/_gui.py +0 -0
  116. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/dataset/_service.py +0 -0
  117. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/dataset/assets/NIH-IDC-logo.svg +0 -0
  118. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/CLAUDE.md +0 -0
  119. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/__init__.py +0 -0
  120. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/_error.py +0 -0
  121. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/_theme.py +0 -0
  122. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 +0 -0
  123. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/assets/cat.lottie +0 -0
  124. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/gui/assets/logo.png +0 -0
  125. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/notebook/CLAUDE.md +0 -0
  126. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/notebook/__init__.py +0 -0
  127. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/notebook/_gui.py +0 -0
  128. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/notebook/_notebook.py +0 -0
  129. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/notebook/_service.py +0 -0
  130. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/notebook/assets/python.lottie +0 -0
  131. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/CLAUDE.md +0 -0
  132. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/__init__.py +0 -0
  133. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_authentication.py +0 -0
  134. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_cli.py +0 -0
  135. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_client.py +0 -0
  136. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_constants.py +0 -0
  137. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_messages.py +0 -0
  138. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_operation_cache.py +0 -0
  139. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_sdk_metadata.py +0 -0
  140. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_service.py +0 -0
  141. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_settings.py +0 -0
  142. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/_utils.py +0 -0
  143. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/resources/__init__.py +0 -0
  144. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/resources/applications.py +0 -0
  145. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/resources/runs.py +0 -0
  146. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/platform/resources/utils.py +0 -0
  147. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/CLAUDE.md +0 -0
  148. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/__init__.py +0 -0
  149. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/_cli.py +0 -0
  150. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/_gui.py +0 -0
  151. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/_settings.py +0 -0
  152. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/assets/download.lottie +0 -0
  153. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/assets/microscope.lottie +0 -0
  154. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/assets/update.lottie +0 -0
  155. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/scripts/add.groovy +0 -0
  156. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/scripts/annotate.groovy +0 -0
  157. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/scripts/inspect.groovy +0 -0
  158. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/qupath/scripts/test.groovy +0 -0
  159. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/CLAUDE.md +0 -0
  160. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/__init__.py +0 -0
  161. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/_cli.py +0 -0
  162. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/_exceptions.py +0 -0
  163. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/_gui.py +0 -0
  164. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/_service.py +0 -0
  165. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/_settings.py +0 -0
  166. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/system/assets/system.lottie +0 -0
  167. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/bottle.py +0 -0
  168. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/idc_index.py +0 -0
  169. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/__init__.py +0 -0
  170. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/argumentsparse.py +0 -0
  171. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/constants.py +0 -0
  172. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/py.typed +0 -0
  173. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/showinfm.py +0 -0
  174. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/system/__init__.py +0 -0
  175. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/system/linux.py +0 -0
  176. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/system/tools.py +0 -0
  177. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/system/urivalidate.py +0 -0
  178. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/third_party/showinfm/system/windows.py +0 -0
  179. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/CLAUDE.md +0 -0
  180. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_cli.py +0 -0
  181. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_console.py +0 -0
  182. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_constants.py +0 -0
  183. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_fs.py +0 -0
  184. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_gui.py +0 -0
  185. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_health.py +0 -0
  186. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_log.py +0 -0
  187. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_notebook.py +0 -0
  188. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_process.py +0 -0
  189. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_sentry.py +0 -0
  190. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_service.py +0 -0
  191. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_settings.py +0 -0
  192. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/_user_agent.py +0 -0
  193. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/utils/boot.py +0 -0
  194. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/CLAUDE.md +0 -0
  195. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/__init__.py +0 -0
  196. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/_cli.py +0 -0
  197. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/_gui.py +0 -0
  198. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/_openslide_handler.py +0 -0
  199. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/_pydicom_handler.py +0 -0
  200. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/_service.py +0 -0
  201. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/_utils.py +0 -0
  202. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics/wsi/assets/fallback.png +0 -0
  203. {aignostics-0.2.232 → aignostics-0.2.234}/src/aignostics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aignostics
3
- Version: 0.2.232
3
+ Version: 0.2.234
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/
@@ -2,7 +2,7 @@
2
2
  # requires-python = ">=3.13"
3
3
  # dependencies = [
4
4
  # "marimo",
5
- # "aignostics==0.2.232",
5
+ # "aignostics==0.2.234",
6
6
  # ]
7
7
  # ///
8
8
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aignostics"
3
- version = "0.2.232"
3
+ version = "0.2.234"
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 = [
@@ -395,7 +395,7 @@ source = ["src/"]
395
395
 
396
396
 
397
397
  [tool.bumpversion]
398
- current_version = "0.2.232"
398
+ current_version = "0.2.234"
399
399
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
400
400
  serialize = ["{major}.{minor}.{patch}"]
401
401
  search = "{current_version}"
@@ -20,6 +20,11 @@ FLAT_COLOR_WHITE = "flat color=white"
20
20
 
21
21
  HEALTH_UPDATE_INTERVAL = 30
22
22
  USERINFO_UPDATE_INTERVAL = 60 * 60
23
+ PROPS_CLICKABLE = "clickable"
24
+ PROPS_AVATAR = "avatar"
25
+ CLASSES_FULL_WIDTH = "w-full"
26
+ CLASSES_FULL_HEIGHT = "h-full"
27
+ CLASSES_FULL_SIZE = f"{CLASSES_FULL_WIDTH} {CLASSES_FULL_HEIGHT}"
23
28
 
24
29
 
25
30
  @contextmanager
@@ -47,9 +52,38 @@ def frame( # noqa: C901, PLR0915
47
52
  from aignostics.platform import Service as PlatformService # noqa: PLC0415
48
53
  from aignostics.platform import UserInfo, settings # noqa: PLC0415
49
54
  from aignostics.system import Service as SystemService # noqa: PLC0415
55
+ from aignostics.utils import NavItem, gui_get_nav_groups # noqa: PLC0415
50
56
 
51
57
  theme()
52
58
 
59
+ def _nav_item(icon: str, label: str, target: str, marker: str, new_tab: bool = True) -> None:
60
+ """Create a navigation item with icon and link."""
61
+ with ui.item().props(PROPS_CLICKABLE).classes(CLASSES_FULL_WIDTH):
62
+ with ui.item_section().props(PROPS_AVATAR):
63
+ ui.icon(icon, color="primary")
64
+ with ui.item_section():
65
+ ui.link(label, target, new_tab=new_tab).mark(marker)
66
+
67
+ def _render_nav_item(item: NavItem) -> None:
68
+ """Render a single NavItem."""
69
+ _nav_item(item.icon, item.label, item.target, item.marker or "", item.new_tab)
70
+
71
+ def _render_nav_groups() -> None:
72
+ """Render all navigation groups from discovered NavBuilders."""
73
+ nav_groups = gui_get_nav_groups()
74
+ for group in nav_groups:
75
+ if group.use_expansion:
76
+ with (
77
+ ui.expansion(group.name, icon=group.icon, group="nav").classes(CLASSES_FULL_WIDTH),
78
+ ui.list().props("dense").classes(CLASSES_FULL_WIDTH),
79
+ ):
80
+ for item in group.items:
81
+ _render_nav_item(item)
82
+ else:
83
+ # Render items flat without expansion
84
+ for item in group.items:
85
+ _render_nav_item(item)
86
+
53
87
  user_info: UserInfo | None = None
54
88
  launchpad_healthy: bool | None = None
55
89
 
@@ -247,6 +281,7 @@ def frame( # noqa: C901, PLR0915
247
281
  ui.label("Manage Cloud Bucket").classes(
248
282
  "font-bold" if context.client.page.path == "/bucket" else "font-normal"
249
283
  )
284
+ _render_nav_groups()
250
285
  with ui.item(on_click=lambda _: ui.navigate.to("/system")).props("clickable"):
251
286
  with ui.item_section().props("avatar"):
252
287
  health_icon()
@@ -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,10 +22,11 @@ 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
29
+ from ._nav import BaseNavBuilder, NavGroup, NavItem, gui_get_nav_groups
29
30
  from ._process import SUBPROCESS_CREATION_FLAGS, ProcessInfo, get_process_info
30
31
  from ._service import BaseService
31
32
  from ._settings import UNHIDE_SENSITIVE_INFO, OpaqueSettings, load_settings, strip_to_none_before_validator
@@ -35,9 +36,12 @@ from .boot import boot
35
36
  __all__ = [
36
37
  "SUBPROCESS_CREATION_FLAGS",
37
38
  "UNHIDE_SENSITIVE_INFO",
39
+ "BaseNavBuilder",
38
40
  "BaseService",
39
41
  "Health",
40
42
  "LogSettings",
43
+ "NavGroup",
44
+ "NavItem",
41
45
  "OpaqueSettings",
42
46
  "ProcessInfo",
43
47
  "__author_email__",
@@ -60,8 +64,10 @@ __all__ = [
60
64
  "__version_full__",
61
65
  "boot",
62
66
  "console",
67
+ "discover_plugin_packages",
63
68
  "get_process_info",
64
69
  "get_user_data_directory",
70
+ "gui_get_nav_groups",
65
71
  "load_modules",
66
72
  "load_settings",
67
73
  "locate_implementations",
@@ -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
@@ -0,0 +1,162 @@
1
+ """Navigation infrastructure for NiceGUI sidebar.
2
+
3
+ This module provides:
4
+ - NavItem: Individual navigation item dataclass
5
+ - NavGroup: Group of navigation items
6
+ - BaseNavBuilder: Abstract base class for module navigation builders
7
+ - gui_get_nav_groups: Collect and sort navigation groups from all NavBuilders
8
+ """
9
+
10
+ from abc import ABC, abstractmethod
11
+ from dataclasses import dataclass, field
12
+
13
+ from ._di import locate_subclasses
14
+
15
+
16
+ @dataclass
17
+ class NavItem:
18
+ """Navigation item for sidebar.
19
+
20
+ Attributes:
21
+ icon: Material icon name (e.g., 'waving_hand', 'settings').
22
+ label: Display label for the navigation item.
23
+ target: URL path or external URL for the link.
24
+ marker: Test marker for the item. Auto-generated from label if None.
25
+ new_tab: Whether to open the link in a new tab. Defaults to False (same tab).
26
+ """
27
+
28
+ icon: str
29
+ label: str
30
+ target: str
31
+ marker: str | None = None
32
+ new_tab: bool = False
33
+
34
+ def __post_init__(self) -> None:
35
+ """Auto-generate marker from label if not provided."""
36
+ if self.marker is None:
37
+ # Convert label to SCREAMING_SNAKE_CASE marker
38
+ self.marker = "LINK_" + self.label.upper().replace(" ", "_").replace("(", "").replace(")", "")
39
+
40
+
41
+ @dataclass
42
+ class NavGroup:
43
+ """Group of navigation items from a NavBuilder.
44
+
45
+ Used internally for rendering navigation in the sidebar.
46
+ """
47
+
48
+ name: str
49
+ icon: str = "folder"
50
+ items: list[NavItem] = field(default_factory=list)
51
+ position: int = 1000
52
+ use_expansion: bool = True
53
+
54
+
55
+ class BaseNavBuilder(ABC):
56
+ """Base class for navigation builders.
57
+
58
+ Each module should have ONE NavBuilder that defines its navigation items.
59
+ NavBuilders are auto-discovered and used to populate the sidebar.
60
+
61
+ Example:
62
+ class NavBuilder(BaseNavBuilder):
63
+ @staticmethod
64
+ def get_nav_name() -> str:
65
+ return "My Module"
66
+
67
+ @staticmethod
68
+ def get_nav_items() -> list[NavItem]:
69
+ return [
70
+ NavItem(icon="home", label="Home", target="/my-module"),
71
+ NavItem(icon="settings", label="Settings", target="/my-module/settings"),
72
+ ]
73
+
74
+ @staticmethod
75
+ def get_nav_position() -> int:
76
+ return 200 # Lower = higher in sidebar
77
+ """
78
+
79
+ @staticmethod
80
+ @abstractmethod
81
+ def get_nav_name() -> str:
82
+ """Return the display name for this module's navigation group.
83
+
84
+ Returns:
85
+ str: Display name shown in sidebar (e.g., 'Hello World', 'System').
86
+ """
87
+
88
+ @staticmethod
89
+ @abstractmethod
90
+ def get_nav_items() -> list[NavItem]:
91
+ """Return navigation items for the sidebar.
92
+
93
+ Items are rendered in list order within the module's group.
94
+
95
+ Returns:
96
+ list[NavItem]: Navigation items for this module.
97
+ """
98
+
99
+ @staticmethod
100
+ def get_nav_position() -> int:
101
+ """Return position in sidebar (lower = higher).
102
+
103
+ Convention:
104
+ - 100-199: Core demo/example pages
105
+ - 200-499: Feature modules
106
+ - 500-799: Default (unspecified)
107
+ - 800-899: System/admin pages
108
+ - 900-999: External links (API docs, status, repo)
109
+
110
+ Returns:
111
+ int: Position value. Defaults to 1000.
112
+ """
113
+ return 1000
114
+
115
+ @staticmethod
116
+ def get_nav_icon() -> str:
117
+ """Return the icon for the navigation group expansion panel.
118
+
119
+ Uses Material Icons names. See: https://fonts.google.com/icons
120
+
121
+ Returns:
122
+ str: Material icon name. Defaults to 'folder'.
123
+ """
124
+ return "folder"
125
+
126
+ @staticmethod
127
+ def get_nav_use_expansion() -> bool:
128
+ """Return whether to render items in an expansion panel.
129
+
130
+ If True, items are grouped in a collapsible expansion with get_nav_name().
131
+ If False, items are rendered flat without grouping.
132
+
133
+ Returns:
134
+ bool: Use expansion panel. Defaults to True.
135
+ """
136
+ return True
137
+
138
+
139
+ def gui_get_nav_groups() -> list[NavGroup]:
140
+ """Collect navigation groups from all NavBuilders.
141
+
142
+ Returns:
143
+ list[NavGroup]: Navigation groups sorted by position.
144
+ """
145
+ nav_builders = locate_subclasses(BaseNavBuilder)
146
+ groups: list[NavGroup] = []
147
+
148
+ for nav_builder in nav_builders:
149
+ items = nav_builder.get_nav_items()
150
+ if items: # Only include builders with nav items
151
+ groups.append(
152
+ NavGroup(
153
+ name=nav_builder.get_nav_name(),
154
+ icon=nav_builder.get_nav_icon(),
155
+ items=items,
156
+ position=nav_builder.get_nav_position(),
157
+ use_expansion=nav_builder.get_nav_use_expansion(),
158
+ )
159
+ )
160
+
161
+ # Sort by position (lower = higher in sidebar)
162
+ return sorted(groups, key=lambda g: g.position)
@@ -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