pyobs-gui 2.5.0__tar.gz → 2.5.2__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 (175) hide show
  1. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/PKG-INFO +1 -1
  2. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/base.py +10 -2
  3. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/camerawidget.py +1 -1
  4. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/spectrographwidget.py +1 -1
  5. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/videograbwidget.py +1 -1
  6. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyproject.toml +1 -1
  7. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/index.md +4 -0
  8. pyobs_gui-2.5.2/tests/test_basewidget_discard.py +44 -0
  9. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/uv.lock +28 -28
  10. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.claude/skills/verify/SKILL.md +0 -0
  11. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.github/dependabot.yml +0 -0
  12. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.github/workflows/dependabot-automerge.yml +0 -0
  13. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.github/workflows/pypi.yml +0 -0
  14. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.github/workflows/pyrefly.yml +0 -0
  15. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.github/workflows/ruff.yml +0 -0
  16. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.gitignore +0 -0
  17. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.pre-commit-config.yaml +0 -0
  18. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/.readthedocs.yml +0 -0
  19. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/CHANGELOG.rst +0 -0
  20. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/CLAUDE.md +0 -0
  21. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/LICENSE +0 -0
  22. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/README.md +0 -0
  23. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/bin/mypyuic5 +0 -0
  24. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/docs/Makefile +0 -0
  25. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/docs/make.bat +0 -0
  26. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/docs/requirements.txt +0 -0
  27. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/docs/source/_static/pyobs.gif +0 -0
  28. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/docs/source/conf.py +0 -0
  29. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/docs/source/index.rst +0 -0
  30. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/mypy.ini +0 -0
  31. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/__init__.py +0 -0
  32. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/__main__.py +0 -0
  33. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/_nuitka_astropy_patch.py +0 -0
  34. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/accounts.py +0 -0
  35. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/acquisitionwidget.py +0 -0
  36. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/autofocuswidget.py +0 -0
  37. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/autoguidingwidget.py +0 -0
  38. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/commandinputwidget.py +0 -0
  39. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/compassmovewidget.py +0 -0
  40. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/coolingwidget.py +0 -0
  41. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/datadisplaywidget.py +0 -0
  42. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/eventswidget.py +0 -0
  43. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/filterwidget.py +0 -0
  44. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/fitsheaderswidget.py +0 -0
  45. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/focuswidget.py +0 -0
  46. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/gui.py +0 -0
  47. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/login.py +0 -0
  48. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/loginwindow.py +0 -0
  49. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/logmodel.py +0 -0
  50. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/mainwindow.py +0 -0
  51. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/modewidget.py +0 -0
  52. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/modifiedmixin.py +0 -0
  53. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/modulegui.py +0 -0
  54. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/movestack.py +0 -0
  55. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/nowheelfilter.py +0 -0
  56. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/__init__.py +0 -0
  57. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/acquisitionwidget.ui +0 -0
  58. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/acquisitionwidget_ui.py +0 -0
  59. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/autofocuswidget.ui +0 -0
  60. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/autofocuswidget_ui.py +0 -0
  61. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/autoguidingwidget.ui +0 -0
  62. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/autoguidingwidget_ui.py +0 -0
  63. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/camerawidget.ui +0 -0
  64. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/camerawidget_ui.py +0 -0
  65. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/compassmoveplugin.py +0 -0
  66. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/compassmovewidget.py +0 -0
  67. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/compassmovewidget.ui +0 -0
  68. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/compassmovewidget_ui.py +0 -0
  69. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/compile.sh +0 -0
  70. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/coolingwidget.ui +0 -0
  71. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/coolingwidget_ui.py +0 -0
  72. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/datadisplayplugin.py +0 -0
  73. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/datadisplaywidget.ui +0 -0
  74. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/datadisplaywidget_ui.py +0 -0
  75. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/designer.sh +0 -0
  76. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/eventswidget.ui +0 -0
  77. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/eventswidget_ui.py +0 -0
  78. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/filterwidget.ui +0 -0
  79. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/filterwidget_ui.py +0 -0
  80. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/fitsheaderswidget.ui +0 -0
  81. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/fitsheaderswidget_ui.py +0 -0
  82. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/focuswidget.ui +0 -0
  83. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/focuswidget_ui.py +0 -0
  84. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/mainwindow.ui +0 -0
  85. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/mainwindow_ui.py +0 -0
  86. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/modewidget.ui +0 -0
  87. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/modewidget_ui.py +0 -0
  88. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/arrow-alt-circle-down-solid.svg +0 -0
  89. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/arrow-alt-circle-left-solid.svg +0 -0
  90. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/arrow-alt-circle-right-solid.svg +0 -0
  91. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/arrow-alt-circle-up-solid.svg +0 -0
  92. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/arrow-up-right-from-square-solid.svg +0 -0
  93. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/arrows-to-eye-solid.svg +0 -0
  94. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/chart-line-solid.svg +0 -0
  95. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/circle-question-solid.svg +0 -0
  96. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/cloud-sun-solid.svg +0 -0
  97. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/edit-solid.svg +0 -0
  98. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/search-solid.svg +0 -0
  99. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources/undo-solid.svg +0 -0
  100. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources.qrc +0 -0
  101. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/resources_rc.py +0 -0
  102. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/roboticwidget.ui +0 -0
  103. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/roboticwidget_ui.py +0 -0
  104. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/roofwidget.ui +0 -0
  105. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/roofwidget_ui.py +0 -0
  106. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/schedulewidget.ui +0 -0
  107. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/schedulewidget_ui.py +0 -0
  108. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/shellwidget.ui +0 -0
  109. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/shellwidget_ui.py +0 -0
  110. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/spectrographwidget.ui +0 -0
  111. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/spectrographwidget_ui.py +0 -0
  112. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/telescopewidget.ui +0 -0
  113. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/telescopewidget_ui.py +0 -0
  114. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/temperaturesplotwidget.ui +0 -0
  115. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/temperaturesplotwidget_ui.py +0 -0
  116. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/temperatureswidget.ui +0 -0
  117. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/temperatureswidget_ui.py +0 -0
  118. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/videograbwidget.ui +0 -0
  119. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/videograbwidget_ui.py +0 -0
  120. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/videowidget.ui +0 -0
  121. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/videowidget_ui.py +0 -0
  122. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/weatherwidget.ui +0 -0
  123. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/qt/weatherwidget_ui.py +0 -0
  124. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/roboticwidget.py +0 -0
  125. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/roofwidget.py +0 -0
  126. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/schedulewidget.py +0 -0
  127. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/shellwidget.py +0 -0
  128. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/statuswidget.py +0 -0
  129. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/structuredconfigwidget.py +0 -0
  130. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/telescopewidget.py +0 -0
  131. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/temperaturesplotwidget.py +0 -0
  132. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/temperatureswidget.py +0 -0
  133. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/utils.py +0 -0
  134. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/videowidget.py +0 -0
  135. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/watchedlabel.py +0 -0
  136. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pyobs_gui/weatherwidget.py +0 -0
  137. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/pysidedeploy.spec +0 -0
  138. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-08-20-gui-remote-call-error-handling.md +0 -0
  139. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-08-21-gui-widget-startup-responsiveness.md +0 -0
  140. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-08-28-gui-main-vs-sidebar-widgets.md +0 -0
  141. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-08-28-structuredconfig-widget.md +0 -0
  142. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-08-31-irobotic-widgets.md +0 -0
  143. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-09-01-gui-video-widget-split.md +0 -0
  144. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-09-14-fitswidget-toolbar-overflow.md +0 -0
  145. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/specs/2026-09-14-stacked-widget-scroll-fallback.md +0 -0
  146. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/spike_standalone/main.py +0 -0
  147. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/spike_standalone/pysidedeploy.spec +0 -0
  148. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/acquisition.yaml +0 -0
  149. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/autofocus.yaml +0 -0
  150. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/camera.yaml +0 -0
  151. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/full.yaml +0 -0
  152. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/guiding.yaml +0 -0
  153. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/mode.yaml +0 -0
  154. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/robotic.yaml +0 -0
  155. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/roof.yaml +0 -0
  156. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/spectrograph.yaml +0 -0
  157. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/structuredconfig.yaml +0 -0
  158. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/telescope.yaml +0 -0
  159. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/telescope_acl.yaml +0 -0
  160. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/telescope_acl_denied.yaml +0 -0
  161. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/test/video.yaml +0 -0
  162. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/__init__.py +0 -0
  163. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/conftest.py +0 -0
  164. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_accounts.py +0 -0
  165. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_camerawidget.py +0 -0
  166. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_loginwindow.py +0 -0
  167. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_mainwindow_startup.py +0 -0
  168. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_modewidget.py +0 -0
  169. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_multiwidget_pages.py +0 -0
  170. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_roboticwidget.py +0 -0
  171. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_schedulewidget.py +0 -0
  172. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_spectrographwidget.py +0 -0
  173. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_structuredconfigwidget.py +0 -0
  174. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_videograbwidget.py +0 -0
  175. {pyobs_gui-2.5.0 → pyobs_gui-2.5.2}/tests/test_videowidget.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyobs-gui
3
- Version: 2.5.0
3
+ Version: 2.5.2
4
4
  Summary: A remote GUI for pyobs
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -61,6 +61,9 @@ class BaseWindow:
61
61
  self.vfs: VirtualFileSystem | dict[str, Any] | None = None
62
62
  self._base_widgets: list[BaseWidget] = []
63
63
 
64
+ # every child widget opened via _open_child(), so BaseWidget.discard() can discard them too
65
+ self._opened_children: list[BaseWidget] = []
66
+
64
67
  @property
65
68
  def comm(self) -> Comm:
66
69
  if self._comm is None:
@@ -142,6 +145,8 @@ class BaseWindow:
142
145
  await self._open_child(widget)
143
146
 
144
147
  async def _open_child(self, widget: BaseWidget) -> None:
148
+ # track before opening, so a child failing halfway through open() still gets discarded
149
+ self._opened_children.append(widget)
145
150
  await widget.open(modules=self.modules, vfs=self.vfs, comm=self.comm, observer=self.observer)
146
151
 
147
152
 
@@ -269,7 +274,8 @@ class BaseWidget(BaseWindow, QtWidgets.QWidget): # type: ignore
269
274
 
270
275
  async def discard(self) -> None:
271
276
  """Tear down everything this widget registered with comm, and recursively discard
272
- its sidebar widgets.
277
+ its child widgets: sidebar widgets and everything opened via _open_child(), e.g. an
278
+ embedded DataDisplayWidget. Safe to call more than once.
273
279
 
274
280
  Must be called (e.g. from mainwindow._client_disconnected) whenever a widget is
275
281
  removed from the UI -- otherwise a handler registered via register_event() lingers
@@ -289,7 +295,9 @@ class BaseWidget(BaseWindow, QtWidgets.QWidget): # type: ignore
289
295
  await self.comm.unregister_event(event_class, handler)
290
296
  self._registered_event_handlers.clear()
291
297
 
292
- for widget in self.sidebar_widgets:
298
+ # sidebar widgets are opened via _open_child() as well, so skip duplicates
299
+ children = list(dict.fromkeys([*self._opened_children, *self.sidebar_widgets]))
300
+ for widget in children:
293
301
  await widget.discard()
294
302
 
295
303
  def showEvent(self, event: QtGui.QShowEvent) -> None:
@@ -85,7 +85,7 @@ class CameraWidget(BaseWidget, Ui_CameraWidget):
85
85
  ) -> None:
86
86
  """Open module."""
87
87
  await BaseWidget.open(self, modules=modules, comm=comm, observer=observer, vfs=vfs)
88
- await self.datadisplay.open(modules=modules, comm=comm, observer=observer, vfs=vfs)
88
+ await self._open_child(self.datadisplay)
89
89
 
90
90
  # set exposure types
91
91
  image_types = sorted([it.name for it in ImageType])
@@ -47,7 +47,7 @@ class SpectrographWidget(BaseWidget, Ui_SpectrographWidget):
47
47
  ) -> None:
48
48
  """Open module."""
49
49
  await BaseWidget.open(self, modules=modules, comm=comm, observer=observer, vfs=vfs)
50
- await self.datadisplay.open(modules=modules, comm=comm, observer=observer, vfs=vfs)
50
+ await self._open_child(self.datadisplay)
51
51
 
52
52
  self.butAbort.setVisible(await self.comm.has_proxy(self.module, IAbortable))
53
53
 
@@ -59,7 +59,7 @@ class VideoGrabWidget(BaseWidget, Ui_VideoGrabWidget):
59
59
  ) -> None:
60
60
  """Open module."""
61
61
  await BaseWidget.open(self, modules=modules, comm=comm, observer=observer, vfs=vfs)
62
- await self.datadisplay.open(modules=modules, comm=comm, observer=observer, vfs=vfs)
62
+ await self._open_child(self.datadisplay)
63
63
 
64
64
  async def _init(self) -> None:
65
65
  # get interfaces for visibility checks
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyobs-gui"
3
- version = "2.5.0"
3
+ version = "2.5.2"
4
4
  description = "A remote GUI for pyobs"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
6
  requires-python = ">=3.11"
@@ -92,3 +92,7 @@ ADRs that concern `pyobs-gui` live in `pyobs-core`'s `specs/` tree instead (`spe
92
92
  - `pyobs-core/specs/plans/2026-08-21-basevideo-http-token-auth.md` — **proposed**. Shared-token
93
93
  auth + browser login for `BaseVideo` (pyobs-core side); pyobs-gui side is a one-header change
94
94
  in `VideoWidget`'s raw-socket GET (design: `pyobs-core/specs/design/basevideo-http-auth.md`).
95
+ - `pyobs-core/specs/design/basevideo-live-view.md`: **proposed** (2026-09-29). User-selectable
96
+ live view in `VideoWidget`: MJPEG with server-side stretch for slow links, or `/video.raw` with
97
+ client-side stretch/cuts and crop on zoom. Plan: `pyobs-core/specs/plans/2026-09-29-basevideo-frame-buffer-redesign.md`
98
+ (phase 6).
@@ -0,0 +1,44 @@
1
+ from typing import Any
2
+ from unittest.mock import AsyncMock, MagicMock
3
+
4
+ import pytest
5
+ from pyobs.events import NewImageEvent
6
+
7
+ from pyobs_gui.base import BaseWidget
8
+
9
+
10
+ class ChildWidget(BaseWidget):
11
+ async def open(self, **kwargs: Any) -> None:
12
+ await BaseWidget.open(self, **kwargs)
13
+ await self.register_event(NewImageEvent, self._on_new_image)
14
+
15
+ async def _on_new_image(self, event: Any, sender: str) -> bool:
16
+ return True
17
+
18
+
19
+ class ParentWidget(BaseWidget):
20
+ def __init__(self, **kwargs: Any):
21
+ BaseWidget.__init__(self, **kwargs)
22
+ # like CameraWidget.datadisplay: an embedded child, not a sidebar widget
23
+ self.child = ChildWidget()
24
+
25
+ async def open(self, **kwargs: Any) -> None:
26
+ await BaseWidget.open(self, **kwargs)
27
+ await self._open_child(self.child)
28
+
29
+
30
+ @pytest.mark.asyncio
31
+ async def test_discard_unregisters_handlers_of_embedded_children() -> None:
32
+ comm = MagicMock()
33
+ comm.register_event = AsyncMock()
34
+ comm.unregister_event = AsyncMock()
35
+ widget = ParentWidget()
36
+ await widget.open(modules=["camera"], comm=comm)
37
+
38
+ await widget.discard()
39
+
40
+ comm.unregister_event.assert_awaited_once_with(NewImageEvent, widget.child._on_new_image)
41
+
42
+ # discarding again doesn't unregister twice
43
+ await widget.discard()
44
+ assert comm.unregister_event.await_count == 1
@@ -2623,15 +2623,15 @@ wheels = [
2623
2623
 
2624
2624
  [[package]]
2625
2625
  name = "pandas-stubs"
2626
- version = "3.0.5.260730"
2626
+ version = "3.0.5.260914"
2627
2627
  source = { registry = "https://pypi.org/simple" }
2628
2628
  dependencies = [
2629
2629
  { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
2630
2630
  { name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
2631
2631
  ]
2632
- sdist = { url = "https://files.pythonhosted.org/packages/c2/d2/dea4a3a56b7b5f69c5fbca9f14625fcf28e1a39a657e9833d4a10bcac593/pandas_stubs-3.0.5.260730.tar.gz", hash = "sha256:f70a232c57d93a5a2c81f8a53953e10891a5374bc92652277deb325e2e4d0ff3", size = 114631, upload-time = "2026-07-30T14:31:42.271Z" }
2632
+ sdist = { url = "https://files.pythonhosted.org/packages/c1/93/8948ae6c1e1e3d6833596fd266f7be2d27c1451b8be094975ad42c5e842e/pandas_stubs-3.0.5.260914.tar.gz", hash = "sha256:3f6fc1f147f68fd89c007105e7c94a948acb4ecd7eb20dc1c02e153c4ed5c250", size = 117622, upload-time = "2026-09-14T16:42:35.065Z" }
2633
2633
  wheels = [
2634
- { url = "https://files.pythonhosted.org/packages/60/c2/959caec5c46f484b5f8bb6def4b0cf7a45ba6acda26f12b75142d98cc5ae/pandas_stubs-3.0.5.260730-py3-none-any.whl", hash = "sha256:60e90e3e1eda6937e337e243cbe6217e151c11137cd7eddf832af537c7310bfd", size = 174807, upload-time = "2026-07-30T14:31:41.17Z" },
2634
+ { url = "https://files.pythonhosted.org/packages/9a/cb/5ad79e02a556cc23fed5816de0109fa8af660c66cfa5f4af74c3e8d4cd26/pandas_stubs-3.0.5.260914-py3-none-any.whl", hash = "sha256:39a1300c5c5c55fdf609e3476805decce5d5015539a4dcb683449f8feaeee2fb", size = 177344, upload-time = "2026-09-14T16:42:33.771Z" },
2635
2635
  ]
2636
2636
 
2637
2637
  [[package]]
@@ -3263,7 +3263,7 @@ wheels = [
3263
3263
 
3264
3264
  [[package]]
3265
3265
  name = "pyobs-core"
3266
- version = "2.8.7"
3266
+ version = "2.10.7"
3267
3267
  source = { registry = "https://pypi.org/simple" }
3268
3268
  dependencies = [
3269
3269
  { name = "aiohttp" },
@@ -3295,14 +3295,14 @@ dependencies = [
3295
3295
  { name = "tenacity" },
3296
3296
  { name = "typing-extensions" },
3297
3297
  ]
3298
- sdist = { url = "https://files.pythonhosted.org/packages/fd/74/8e30782e34d412e069a6c38393a7e347477294f6c3dae470dcac9aa03783/pyobs_core-2.8.7.tar.gz", hash = "sha256:001751b076fc3af72baac28d600ccee2fdb5a3d3cad38bbf1cae36832aa8cbde", size = 411818, upload-time = "2026-09-09T12:31:51.561Z" }
3298
+ sdist = { url = "https://files.pythonhosted.org/packages/7e/db/b197375974c8436095fdfa53db228e1e8b323f12183433ee960c99e96cef/pyobs_core-2.10.7.tar.gz", hash = "sha256:2bd17b1ea609692c6f8925f2d80afc515ac2209b807205ffaa9129730321116e", size = 424175, upload-time = "2026-09-23T18:20:53.969Z" }
3299
3299
  wheels = [
3300
- { url = "https://files.pythonhosted.org/packages/df/0b/b8a4ef5652f95b26a94567b0f2bb59ab9b304f612059eb216031b8ad0ff6/pyobs_core-2.8.7-py3-none-any.whl", hash = "sha256:ea8ad9d5fca01585977e31e7ab66b2978ea24ea895d69610ba857647dafac096", size = 641759, upload-time = "2026-09-09T12:31:49.673Z" },
3300
+ { url = "https://files.pythonhosted.org/packages/52/4e/ebdb9836eae127a70ab9fd618eb7782394158592cb35b5a97ff133cfa757/pyobs_core-2.10.7-py3-none-any.whl", hash = "sha256:14cfea0a0633ba31593323ff0bff919d9510b987369e0cc3f758a948f9dd396e", size = 656647, upload-time = "2026-09-23T18:20:52.624Z" },
3301
3301
  ]
3302
3302
 
3303
3303
  [[package]]
3304
3304
  name = "pyobs-gui"
3305
- version = "2.5.0"
3305
+ version = "2.5.2"
3306
3306
  source = { editable = "." }
3307
3307
  dependencies = [
3308
3308
  { name = "astroplan" },
@@ -3788,27 +3788,27 @@ wheels = [
3788
3788
 
3789
3789
  [[package]]
3790
3790
  name = "ruff"
3791
- version = "0.16.7"
3792
- source = { registry = "https://pypi.org/simple" }
3793
- sdist = { url = "https://files.pythonhosted.org/packages/82/bb/5a449b9162e49b139d72f61672bd3ac1d790221f796d3304e2241fff4c58/ruff-0.16.7.tar.gz", hash = "sha256:5f71d004ac1263b22fa39462ac5ae618a4b77d58981af2cc79bf79a29c12b1a6", size = 4924184, upload-time = "2026-09-10T18:04:06.336Z" }
3794
- wheels = [
3795
- { url = "https://files.pythonhosted.org/packages/e3/b2/c80aeeb7f9e469c0d63a85d2f1ab6e1ebfbe10ea7a8d2438b7e09e3ff09e/ruff-0.16.7-py3-none-linux_armv6l.whl", hash = "sha256:727307773e7c7f9181d3ed3a2484186e56c1fa1874255911c74585eb2c7c19f9", size = 10048917, upload-time = "2026-09-10T18:03:30.28Z" },
3796
- { url = "https://files.pythonhosted.org/packages/7b/96/20bb7bcae008004df52afcb7ac83432d4a467f2c17b672fe46d26be231c5/ruff-0.16.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9d61c258deabf58f34c67bd4bb4d939c7f2e6b5f0e59c1cdd1cf771b11cde929", size = 10242929, upload-time = "2026-09-10T18:03:32.706Z" },
3797
- { url = "https://files.pythonhosted.org/packages/90/b2/f184b0d5abec02db69cfd7e49b688ae0237554528ca777136c613bf36bee/ruff-0.16.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ab81118df8945e0193d0240712aa4496573595b75185c3636ed825592a0f728", size = 9847245, upload-time = "2026-09-10T18:03:34.509Z" },
3798
- { url = "https://files.pythonhosted.org/packages/eb/2d/db1633a641866ed801e34cc6b60ef236c5e16f9b2124ab1d49cc24a5fe4f/ruff-0.16.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c196c968874fc8019da8e7163de7a1a370f111e2309b4b7dfea0fce950198d0", size = 9961780, upload-time = "2026-09-10T18:03:36.618Z" },
3799
- { url = "https://files.pythonhosted.org/packages/4d/98/edea21e1a3e38dbbc3bf6bb068b863b3b06184cf8533a4c7dbbe208a89d5/ruff-0.16.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac8c3bd0a7e10ad31e6ce51e7a99f3cb772e69aecdd6b9ea7e99b362f62a62c0", size = 9866337, upload-time = "2026-09-10T18:03:38.805Z" },
3800
- { url = "https://files.pythonhosted.org/packages/0b/11/a15e60d4c87b214646f116ca9d204475bf993ee1047459bc9a360fd4d6d1/ruff-0.16.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d3988edde000b5c75dc1b3f584708da9bc990de069c18909142580fec1af9", size = 10562512, upload-time = "2026-09-10T18:03:40.71Z" },
3801
- { url = "https://files.pythonhosted.org/packages/29/42/eaff4c9b6d0c7cdf56df313a17e89ae854f5bbc0b0c8f9cce19be0ab7a8f/ruff-0.16.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce05b62b770a8217c4646a9c4139fca00efe8fe5d71f87df2b243ff20d4584d1", size = 11302938, upload-time = "2026-09-10T18:03:42.607Z" },
3802
- { url = "https://files.pythonhosted.org/packages/5d/43/c75aa59a4ec181fe2ec06cab30e198c1c6d107229a9f008ae3a7c16cabd8/ruff-0.16.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af1b576fddb9d9ef2ececfb5fadcd6a624b25070ed85e3cfcfe449fc3ff6a7b9", size = 10840857, upload-time = "2026-09-10T18:03:44.604Z" },
3803
- { url = "https://files.pythonhosted.org/packages/21/33/81f3da371942ea031105ba679d8d6e28ec1660ccd690a45f42d381161356/ruff-0.16.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce7f8f22df67c93ed96c717f9128eadb797144ac2bad475cf536f31d6100c55", size = 10370001, upload-time = "2026-09-10T18:03:46.706Z" },
3804
- { url = "https://files.pythonhosted.org/packages/fa/0b/6345fb4dbf6dd0ed1cfe5d18391dc9c3f59cc81622a7b0a65b84b3e730ba/ruff-0.16.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:06d0e93d04f392996435ebd600c153f65b47d73fbec2415aa99c5ee5756b3a5f", size = 10548735, upload-time = "2026-09-10T18:03:48.658Z" },
3805
- { url = "https://files.pythonhosted.org/packages/3f/4d/c5576adf511f92a328e5569dda190ecdd430da51f1a649f3a4a2fd73e21e/ruff-0.16.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:142151a5e7b93c1b11111337142f89dd2fbfee92161225c99a97222f22e32656", size = 10108496, upload-time = "2026-09-10T18:03:50.563Z" },
3806
- { url = "https://files.pythonhosted.org/packages/ff/8c/667d83c16199a17a56adc6b0bd4c3beb5b767a2babcd16a56f76f9be7fd6/ruff-0.16.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e6651f97a342d8b35d54d8991544ca22169b86dc54111cb604666940c431b750", size = 9860136, upload-time = "2026-09-10T18:03:52.621Z" },
3807
- { url = "https://files.pythonhosted.org/packages/99/75/78d401106731999a1dd20cc5a6961e37e1eb9397a3b589f73f3a5ce146a3/ruff-0.16.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ef140c6eb935fa9a84c9c607dfb2cb1b85843c192e79265b0c54f35f557ea8e5", size = 10286290, upload-time = "2026-09-10T18:03:55.207Z" },
3808
- { url = "https://files.pythonhosted.org/packages/68/49/56f9c3a8b755df93a0ad318b2147bf4ef5dae9a7e5ec61c460109c67957f/ruff-0.16.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:53e39506a730fadeee0d998ed5946f30671f0db240c6c7c73bdabbe33604bb6f", size = 10745048, upload-time = "2026-09-10T18:03:57.299Z" },
3809
- { url = "https://files.pythonhosted.org/packages/5f/ea/7f9b938a63ece4bec677ad7f9f7fa02df3383db1949ed93a382441c09a87/ruff-0.16.7-py3-none-win32.whl", hash = "sha256:2ea3470fcebcbc5df2fb0c6f3b90333fa9084c534e0111c038fa4a6ab9f1c4b7", size = 10059082, upload-time = "2026-09-10T18:03:59.632Z" },
3810
- { url = "https://files.pythonhosted.org/packages/39/11/480a6973a927aa653e1cead6a6416008640e03a99d05b34c0434b8c6c366/ruff-0.16.7-py3-none-win_amd64.whl", hash = "sha256:7ac26aca826e9e21d0f1cb25b54ac660760a9fdd094d3e4df9848232be98cfc6", size = 10593368, upload-time = "2026-09-10T18:04:01.999Z" },
3811
- { url = "https://files.pythonhosted.org/packages/8b/4b/51327018d056f0dad2c2238f26d1fb0f53707a9d91b75dea6d1b3039f136/ruff-0.16.7-py3-none-win_arm64.whl", hash = "sha256:aab7f39e2c9df6c596216070f98eef1207b94f8516cca20c808826974971855b", size = 10412401, upload-time = "2026-09-10T18:04:04.098Z" },
3791
+ version = "0.16.9"
3792
+ source = { registry = "https://pypi.org/simple" }
3793
+ sdist = { url = "https://files.pythonhosted.org/packages/96/bf/c935ca98e73fe8ce65b87ef08a280c0c1e85295d569228c15e87d8fdfaf1/ruff-0.16.9.tar.gz", hash = "sha256:12b625c6cfba78d285d9f48eda5f053374f1e53cb10ef17342a383750db99161", size = 4948764, upload-time = "2026-09-24T20:37:49.416Z" }
3794
+ wheels = [
3795
+ { url = "https://files.pythonhosted.org/packages/0d/26/df51322b52ee1ada7eff2d071ea09d11d5c2d1dcc9f02594f5785c4d1635/ruff-0.16.9-py3-none-linux_armv6l.whl", hash = "sha256:95e6f022090368ab3b824c36276839c53b2adf1a3f4c09fefc33dfc400f6da96", size = 10082922, upload-time = "2026-09-24T20:37:13.045Z" },
3796
+ { url = "https://files.pythonhosted.org/packages/a5/27/7bf51f5a7aa375e9f339280a303aab44ca75dc1525f1cdc5991761685b0f/ruff-0.16.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5f27be168556594a86d2f415db0cf43f5291917849318f873c7e2791f7a8c67", size = 10236360, upload-time = "2026-09-24T20:37:16.049Z" },
3797
+ { url = "https://files.pythonhosted.org/packages/b6/63/09659283f92f02dff45809da194a70da2f688d87c55d8875c4fae3536072/ruff-0.16.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1632eb1d6197f33bd00b1acbc5b71009e89a8895c158e2d2b03a834fac964ab6", size = 9892940, upload-time = "2026-09-24T20:37:17.957Z" },
3798
+ { url = "https://files.pythonhosted.org/packages/24/58/98de1b72ec172f5f8f1731236fe21585b3998bf7dfe9fcc44ae9ba626012/ruff-0.16.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3f951b14d865d5952c89d40a5ca07e87abe24fa5453299878411e127748fb1c", size = 10032114, upload-time = "2026-09-24T20:37:19.942Z" },
3799
+ { url = "https://files.pythonhosted.org/packages/c8/7d/f1e17c54ab59d4bad1dce8ee3e22a7a1d0ef4745240decacdcf3832b5bb2/ruff-0.16.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:447fc07e1573afff7cb02803462b12b6c8ece7cf10e2cd78565fa6d7a1c0bf8d", size = 9910227, upload-time = "2026-09-24T20:37:21.872Z" },
3800
+ { url = "https://files.pythonhosted.org/packages/34/19/436f647a65075bbd3bab2668b3bdaa5120559b294694018cdcefabbbf30b/ruff-0.16.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a3e039a6a40ed976c491722b60e0ae4a4aa1a86057f540ee7a37a5d19ae9120", size = 10547484, upload-time = "2026-09-24T20:37:24.229Z" },
3801
+ { url = "https://files.pythonhosted.org/packages/03/59/38430a6bc2f6d8095447ac39625cf8b6e9344a47e6c26225c8ba1bff3ffb/ruff-0.16.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4684dded7db60aa57cb118fa158630f5feade4af5782903b6053484bdf9bd129", size = 11412367, upload-time = "2026-09-24T20:37:26.307Z" },
3802
+ { url = "https://files.pythonhosted.org/packages/c8/bd/bbb6d7fc7f208c8b8c50dd5dc8206e4cfdb1e7a8fb852606a3adf370c880/ruff-0.16.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d29c934357e45642fda2f34c0b1f4025b4a6c01e15e4bf0016879d60078a142c", size = 10869787, upload-time = "2026-09-24T20:37:28.35Z" },
3803
+ { url = "https://files.pythonhosted.org/packages/bc/b8/9c543074918061abbefc3bd139bee22de35abedb00dde0f2d27288838962/ruff-0.16.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a21713e629d3e5bdb2f5c2def1cc7f04f47fa8e1a7eb0571b4a28e1da64bc728", size = 10406494, upload-time = "2026-09-24T20:37:30.624Z" },
3804
+ { url = "https://files.pythonhosted.org/packages/35/7a/5a8851bd146e7ccf8fd4b003f6c75c11f8fbdb0e60673b45097986b6bf41/ruff-0.16.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7baa24ef5fc8e77aa93879e1d3f43754a01ae488e869f1ae30cf431afd4d2452", size = 10590083, upload-time = "2026-09-24T20:37:32.439Z" },
3805
+ { url = "https://files.pythonhosted.org/packages/87/f0/4c3467188f23f806960b46fa76575a7cd0514c9ba90562650b71efc96980/ruff-0.16.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a41aac6230aadfaa133bdfa1614488531ffa3e0837567ae04c0da2058a9c0f9e", size = 10119151, upload-time = "2026-09-24T20:37:34.581Z" },
3806
+ { url = "https://files.pythonhosted.org/packages/15/34/5a4def5adea572ce6aea0bb64f21f928ee317b80e0db747d7979b01d7261/ruff-0.16.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c2529fb5896d49115b0e9aa8f887490b34bbe76baf879ec2264ac59406869ce7", size = 9911544, upload-time = "2026-09-24T20:37:36.796Z" },
3807
+ { url = "https://files.pythonhosted.org/packages/62/5d/d15ebea7499eef9373318c0ee6ca127832927c6529731f6d48e18dce7ca9/ruff-0.16.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:41e3870277694177429b56406d65dfbdb2c2802c52b715edaf6a0b829c69d4ee", size = 10269884, upload-time = "2026-09-24T20:37:38.857Z" },
3808
+ { url = "https://files.pythonhosted.org/packages/d1/56/c5d3cd119ded7a3c7aba0e961b69cb3df701c91662c98ad694467d060ce1/ruff-0.16.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8adbe4e58af167f767d7b2ba5e83c42e878350796cf78c2f5e14ab9903a92588", size = 10749366, upload-time = "2026-09-24T20:37:41.042Z" },
3809
+ { url = "https://files.pythonhosted.org/packages/ac/fe/734ec7527029ac757ecf821f143c9f3fcf69149c21f53a044a899b430f5a/ruff-0.16.9-py3-none-win32.whl", hash = "sha256:0e1dbc2073624dee6618d41d0098690a7244654af746704b64759e12b6b6b385", size = 10152355, upload-time = "2026-09-24T20:37:43.025Z" },
3810
+ { url = "https://files.pythonhosted.org/packages/14/21/26e4643629b3ebb44f0a06f9c9a53058d63d989415f63a9a3c28e2ee7f22/ruff-0.16.9-py3-none-win_amd64.whl", hash = "sha256:6bd40fec8cd4c8a3d4dd589bd8ad4e6320c13c29234159bfd959a40d529d597b", size = 10592965, upload-time = "2026-09-24T20:37:44.944Z" },
3811
+ { url = "https://files.pythonhosted.org/packages/51/60/5fb1a39dbb5ae314d5f59bc7348a63c1d5c20f3cd83914c4b5cb0be31d2d/ruff-0.16.9-py3-none-win_arm64.whl", hash = "sha256:ed1a252039200f57a59eebc063b54beabea67bfbaaca0eeaa7f54b5fbcda2284", size = 10458649, upload-time = "2026-09-24T20:37:46.882Z" },
3812
3812
  ]
3813
3813
 
3814
3814
  [[package]]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes