pyobs-core 1.53.0__tar.gz → 2.0.0.dev3__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 (455) hide show
  1. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/PKG-INFO +1 -1
  2. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/application.py +12 -3
  3. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/comm.py +205 -21
  4. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/commlogging.py +5 -2
  5. pyobs_core-2.0.0.dev3/pyobs/comm/local/localcomm.py +157 -0
  6. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/proxy.py +63 -1
  7. pyobs_core-2.0.0.dev3/pyobs/comm/xmpp/rpc.py +303 -0
  8. pyobs_core-2.0.0.dev3/pyobs/comm/xmpp/serializer.py +338 -0
  9. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +6 -19
  10. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xmppclient.py +0 -1
  11. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xmppcomm.py +319 -6
  12. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/event.py +2 -1
  13. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/log.py +18 -2
  14. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/modules/getfitsheaders.py +2 -4
  15. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/spilled_light.py +3 -3
  16. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAutoFocus.py +5 -2
  17. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IBinning.py +12 -18
  18. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IConfig.py +6 -24
  19. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ICooling.py +11 -13
  20. pyobs_core-2.0.0.dev3/pyobs/interfaces/IExposure.py +26 -0
  21. pyobs_core-2.0.0.dev3/pyobs/interfaces/IExposureTime.py +35 -0
  22. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFilters.py +9 -16
  23. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFocuser.py +10 -18
  24. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IGain.py +10 -18
  25. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IImageFormat.py +11 -18
  26. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IImageType.py +9 -9
  27. pyobs_core-2.0.0.dev3/pyobs/interfaces/IMode.py +40 -0
  28. pyobs_core-2.0.0.dev3/pyobs/interfaces/IModule.py +26 -0
  29. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IMotion.py +13 -12
  30. pyobs_core-2.0.0.dev3/pyobs/interfaces/IMultiFiber.py +45 -0
  31. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IOffsetsAltAz.py +13 -11
  32. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IOffsetsRaDec.py +13 -11
  33. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingAltAz.py +13 -11
  34. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingHGS.py +13 -11
  35. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingHelioprojective.py +13 -11
  36. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingRaDec.py +13 -11
  37. pyobs_core-2.0.0.dev3/pyobs/interfaces/IReady.py +21 -0
  38. pyobs_core-2.0.0.dev3/pyobs/interfaces/IRotation.py +28 -0
  39. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IRunning.py +9 -0
  40. pyobs_core-2.0.0.dev3/pyobs/interfaces/ITemperatures.py +30 -0
  41. pyobs_core-2.0.0.dev3/pyobs/interfaces/IVideo.py +19 -0
  42. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IWindow.py +12 -16
  43. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/__init__.py +0 -1
  44. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/interface.py +2 -0
  45. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/camerasettings.py +3 -6
  46. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/fitsheader.py +14 -11
  47. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/follow.py +3 -4
  48. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/motionstatus.py +9 -12
  49. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/waitformotion.py +12 -12
  50. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/weatheraware.py +3 -5
  51. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/basecamera.py +26 -38
  52. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/basespectrograph.py +0 -8
  53. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/basevideo.py +5 -21
  54. pyobs_core-2.0.0.dev3/pyobs/modules/camera/dummycamera.py +328 -0
  55. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/dummyspectrograph.py +0 -11
  56. pyobs_core-2.0.0.dev3/pyobs/modules/camera/dummyvideo.py +69 -0
  57. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/pipelinecamera.py +1 -10
  58. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/flatfield.py +39 -63
  59. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/pointing.py +5 -10
  60. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/scheduler.py +11 -15
  61. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/focus/focusmodel.py +20 -35
  62. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/focus/focusseries.py +52 -56
  63. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/module.py +51 -6
  64. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/_base.py +2 -6
  65. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/_baseguiding.py +8 -9
  66. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/acquisition.py +37 -38
  67. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/autoguiding.py +18 -27
  68. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/scienceframeguiding.py +0 -16
  69. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/pointing.py +7 -8
  70. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/baseroof.py +8 -11
  71. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/dummyroof.py +3 -0
  72. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/telescope/basetelescope.py +16 -27
  73. pyobs_core-2.0.0.dev3/pyobs/modules/telescope/dummytelescope.py +302 -0
  74. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/autonomouswarning.py +17 -13
  75. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/dummymode.py +12 -35
  76. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/kiosk.py +19 -19
  77. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/stellarium.py +7 -8
  78. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/telegram.py +10 -13
  79. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/trigger.py +2 -4
  80. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/object.py +21 -9
  81. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/helioprojectiveradialarget.py +1 -1
  82. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/darkbias.py +13 -16
  83. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/pointing.py +9 -12
  84. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/skyflats.py +21 -24
  85. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/selector.py +14 -9
  86. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/autofocus.py +16 -22
  87. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/imaging.py +60 -74
  88. pyobs_core-2.0.0.dev3/pyobs/robotic/scripts/utils/callmodule.py +86 -0
  89. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/autofocus.py +30 -41
  90. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/default.py +56 -69
  91. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/exptime/stellarexptime.py +23 -16
  92. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/flatfielder.py +72 -60
  93. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/enums.py +36 -1
  94. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/logging/context.py +1 -1
  95. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/parallel.py +15 -3
  96. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/shellcommand.py +10 -11
  97. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyproject.toml +8 -1
  98. pyobs_core-1.53.0/pyobs/comm/dbus/__init__.py +0 -3
  99. pyobs_core-1.53.0/pyobs/comm/dbus/dbuscomm.py +0 -591
  100. pyobs_core-1.53.0/pyobs/comm/dbus/patch.py +0 -112
  101. pyobs_core-1.53.0/pyobs/comm/local/localcomm.py +0 -98
  102. pyobs_core-1.53.0/pyobs/comm/mock/__init__.py +0 -3
  103. pyobs_core-1.53.0/pyobs/comm/mock/mockcomm.py +0 -85
  104. pyobs_core-1.53.0/pyobs/comm/xmpp/rpc.py +0 -274
  105. pyobs_core-1.53.0/pyobs/interfaces/IExposure.py +0 -33
  106. pyobs_core-1.53.0/pyobs/interfaces/IExposureTime.py +0 -43
  107. pyobs_core-1.53.0/pyobs/interfaces/ILatLon.py +0 -38
  108. pyobs_core-1.53.0/pyobs/interfaces/IMode.py +0 -62
  109. pyobs_core-1.53.0/pyobs/interfaces/IModule.py +0 -40
  110. pyobs_core-1.53.0/pyobs/interfaces/IMultiFiber.py +0 -73
  111. pyobs_core-1.53.0/pyobs/interfaces/IReady.py +0 -22
  112. pyobs_core-1.53.0/pyobs/interfaces/IRotation.py +0 -23
  113. pyobs_core-1.53.0/pyobs/interfaces/ITemperatures.py +0 -24
  114. pyobs_core-1.53.0/pyobs/interfaces/IVideo.py +0 -22
  115. pyobs_core-1.53.0/pyobs/modules/camera/dummycamera.py +0 -302
  116. pyobs_core-1.53.0/pyobs/modules/telescope/dummytelescope.py +0 -346
  117. pyobs_core-1.53.0/pyobs/robotic/scripts/utils/callmodule.py +0 -66
  118. pyobs_core-1.53.0/pyobs/utils/simulation/__init__.py +0 -11
  119. pyobs_core-1.53.0/pyobs/utils/simulation/camera.py +0 -284
  120. pyobs_core-1.53.0/pyobs/utils/simulation/telescope.py +0 -200
  121. pyobs_core-1.53.0/pyobs/utils/simulation/world.py +0 -107
  122. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/.gitignore +0 -0
  123. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/LICENSE +0 -0
  124. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/__init__.py +0 -0
  125. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/background_task.py +0 -0
  126. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/cli/__init__.py +0 -0
  127. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/cli/_cli.py +0 -0
  128. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/cli/pyobs.py +0 -0
  129. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/cli/pyobsd.py +0 -0
  130. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/cli/pyobsw.py +0 -0
  131. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/__init__.py +0 -0
  132. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/dummy/__init__.py +0 -0
  133. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/dummy/dummycomm.py +0 -0
  134. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/local/__init__.py +0 -0
  135. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/local/localnetwork.py +0 -0
  136. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/__init__.py +0 -0
  137. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
  138. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
  139. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
  140. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
  141. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
  142. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
  143. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/__init__.py +0 -0
  144. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/badweather.py +0 -0
  145. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/exposurestatuschanged.py +0 -0
  146. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/filterchanged.py +0 -0
  147. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/focusfound.py +0 -0
  148. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/goodweather.py +0 -0
  149. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/modechanged.py +0 -0
  150. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/moduleclosed.py +0 -0
  151. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/moduleopened.py +0 -0
  152. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/motionstatuschanged.py +0 -0
  153. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/move.py +0 -0
  154. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/newimage.py +0 -0
  155. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/newspectrum.py +0 -0
  156. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/offsets.py +0 -0
  157. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/roofclosing.py +0 -0
  158. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/roofopened.py +0 -0
  159. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/taskfailed.py +0 -0
  160. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/taskfinished.py +0 -0
  161. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/taskstarted.py +0 -0
  162. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/events/testevent.py +0 -0
  163. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/__init__.py +0 -0
  164. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/image.py +0 -0
  165. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/__init__.py +0 -0
  166. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/altazoffsets.py +0 -0
  167. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/exptime.py +0 -0
  168. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/genericoffset.py +0 -0
  169. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/onskydistance.py +0 -0
  170. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/pixeloffsets.py +0 -0
  171. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/radecoffsets.py +0 -0
  172. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/skyoffsets.py +0 -0
  173. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processor.py +0 -0
  174. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/__init__.py +0 -0
  175. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/_daobackgroundremover.py +0 -0
  176. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/__init__.py +0 -0
  177. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/_pillow.py +0 -0
  178. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/circle.py +0 -0
  179. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/crosshair.py +0 -0
  180. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/text.py +0 -0
  181. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/__init__.py +0 -0
  182. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
  183. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
  184. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
  185. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
  186. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/astrometry.py +0 -0
  187. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/dotnet.py +0 -0
  188. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/__init__.py +0 -0
  189. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
  190. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
  191. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/calibration.py +0 -0
  192. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/__init__.py +0 -0
  193. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
  194. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/_source_catalog.py +0 -0
  195. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/daophot.py +0 -0
  196. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/pysep.py +0 -0
  197. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/simpledisk.py +0 -0
  198. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/sourcedetection.py +0 -0
  199. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/exptime/__init__.py +0 -0
  200. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/exptime/exptime.py +0 -0
  201. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/exptime/star.py +0 -0
  202. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/__init__.py +0 -0
  203. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/addfitsheaders.py +0 -0
  204. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/download.py +0 -0
  205. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/flip.py +0 -0
  206. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/grayscale.py +0 -0
  207. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/httpserver.py +0 -0
  208. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/normalize.py +0 -0
  209. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/save.py +0 -0
  210. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/saveimage.py +0 -0
  211. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/smooth.py +0 -0
  212. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/softbin.py +0 -0
  213. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/__init__.py +0 -0
  214. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/addmask.py +0 -0
  215. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
  216. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/circularmask.py +0 -0
  217. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/createfilename.py +0 -0
  218. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
  219. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/removebackground.py +0 -0
  220. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/modules/__init__.py +0 -0
  221. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/__init__.py +0 -0
  222. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/astrometry.py +0 -0
  223. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/brighteststar.py +0 -0
  224. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
  225. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
  226. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
  227. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/fitsheader.py +0 -0
  228. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/offsets.py +0 -0
  229. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
  230. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/projected.py +0 -0
  231. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/__init__.py +0 -0
  232. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
  233. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
  234. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
  235. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
  236. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/photometry.py +0 -0
  237. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/photutil.py +0 -0
  238. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/pysep.py +0 -0
  239. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/wcs/__init__.py +0 -0
  240. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
  241. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAbortable.py +0 -0
  242. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAcquisition.py +0 -0
  243. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAutoGuiding.py +0 -0
  244. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAutonomous.py +0 -0
  245. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ICalibrate.py +0 -0
  246. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ICamera.py +0 -0
  247. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IData.py +0 -0
  248. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IDome.py +0 -0
  249. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
  250. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
  251. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFlatField.py +0 -0
  252. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFocusModel.py +0 -0
  253. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingSeries.py +0 -0
  254. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IRoof.py +0 -0
  255. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IRunnable.py +0 -0
  256. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IScriptRunner.py +0 -0
  257. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ISpectrograph.py +0 -0
  258. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IStartStop.py +0 -0
  259. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ISyncTarget.py +0 -0
  260. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ITelescope.py +0 -0
  261. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IWeather.py +0 -0
  262. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/__init__.py +0 -0
  263. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/fitsnamespace.py +0 -0
  264. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/mixins/pipeline.py +0 -0
  265. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/__init__.py +0 -0
  266. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/__init__.py +0 -0
  267. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/adaptive.py +0 -0
  268. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/__init__.py +0 -0
  269. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/focus/__init__.py +0 -0
  270. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/__init__.py +0 -0
  271. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/imagewatcher.py +0 -0
  272. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/imagewriter.py +0 -0
  273. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/pipeline.py +0 -0
  274. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/seeing.py +0 -0
  275. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/__init__.py +0 -0
  276. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/dummyacquisition.py +0 -0
  277. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/dummyguiding.py +0 -0
  278. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
  279. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
  280. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
  281. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
  282. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
  283. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/__init__.py +0 -0
  284. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/mastermind.py +0 -0
  285. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/scheduler.py +0 -0
  286. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/scriptrunner.py +0 -0
  287. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/__init__.py +0 -0
  288. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/basedome.py +0 -0
  289. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/telescope/__init__.py +0 -0
  290. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/test/__init__.py +0 -0
  291. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/test/standalone.py +0 -0
  292. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/__init__.py +0 -0
  293. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/fluentlogger.py +0 -0
  294. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/httpfilecache.py +0 -0
  295. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/matrix.py +0 -0
  296. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/__init__.py +0 -0
  297. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/weather.py +0 -0
  298. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/weather_api.py +0 -0
  299. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/weather_state.py +0 -0
  300. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/py.typed +0 -0
  301. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/__init__.py +0 -0
  302. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/observation.py +0 -0
  303. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/__init__.py +0 -0
  304. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
  305. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
  306. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
  307. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
  308. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
  309. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
  310. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
  311. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
  312. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/dataprovider.py +0 -0
  313. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
  314. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
  315. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
  316. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/constant.py +0 -0
  317. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/follow.py +0 -0
  318. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/interval.py +0 -0
  319. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/merit.py +0 -0
  320. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
  321. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/random.py +0 -0
  322. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
  323. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/transit.py +0 -0
  324. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
  325. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
  326. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
  327. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
  328. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
  329. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
  330. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
  331. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
  332. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/target.py +0 -0
  333. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
  334. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/__init__.py +0 -0
  335. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
  336. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/__init__.py +0 -0
  337. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/cases.py +0 -0
  338. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/conditional.py +0 -0
  339. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/parallel.py +0 -0
  340. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/sequential.py +0 -0
  341. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
  342. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
  343. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/script.py +0 -0
  344. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/utils/__init__.py +0 -0
  345. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
  346. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/utils/log.py +0 -0
  347. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/__init__.py +0 -0
  348. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/backend/__init__.py +0 -0
  349. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/backend/observationarchive.py +0 -0
  350. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/backend/taskarchive.py +0 -0
  351. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
  352. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
  353. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
  354. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/__init__.py +0 -0
  355. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/_portal.py +0 -0
  356. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
  357. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
  358. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/configdb.py +0 -0
  359. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
  360. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
  361. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
  362. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
  363. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/task.py +0 -0
  364. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
  365. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
  366. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/memory/__init__.py +0 -0
  367. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
  368. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
  369. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/observationarchive.py +0 -0
  370. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/taskarchive.py +0 -0
  371. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/task.py +0 -0
  372. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/taskrunner.py +0 -0
  373. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/__init__.py +0 -0
  374. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/__init__.py +0 -0
  375. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/archive.py +0 -0
  376. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/local_archive.py +0 -0
  377. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/pyobs_archive.py +0 -0
  378. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/exptime/__init__.py +0 -0
  379. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/exptime/exptime.py +0 -0
  380. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
  381. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
  382. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
  383. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
  384. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
  385. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
  386. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
  387. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
  388. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
  389. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
  390. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/__init__.py +0 -0
  391. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/average.py +0 -0
  392. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/cache.py +0 -0
  393. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/config.py +0 -0
  394. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/coordinates.py +0 -0
  395. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/curvefit.py +0 -0
  396. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/exceptions.py +0 -0
  397. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/fits.py +0 -0
  398. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/__init__.py +0 -0
  399. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/base.py +0 -0
  400. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/photometry.py +0 -0
  401. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/projection.py +0 -0
  402. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/__init__.py +0 -0
  403. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/filters.py +0 -0
  404. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/grid.py +0 -0
  405. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/gridnode.py +0 -0
  406. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/pipeline.py +0 -0
  407. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/__init__.py +0 -0
  408. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/__init__.py +0 -0
  409. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/binningwidget.py +0 -0
  410. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
  411. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/exposewidget.py +0 -0
  412. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
  413. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
  414. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
  415. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
  416. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/http.py +0 -0
  417. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/influxdb.py +0 -0
  418. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/logger.py +0 -0
  419. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/logging/__init__.py +0 -0
  420. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/logging/resolvableerror.py +0 -0
  421. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/modulegui.py +0 -0
  422. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/__init__.py +0 -0
  423. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
  424. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/applyoffsets.py +0 -0
  425. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
  426. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/pipeline/__init__.py +0 -0
  427. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/pipeline/night.py +0 -0
  428. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/pipeline/pipeline.py +0 -0
  429. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/__init__.py +0 -0
  430. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/csv.py +0 -0
  431. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/http.py +0 -0
  432. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/log.py +0 -0
  433. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/multi.py +0 -0
  434. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/publisher.py +0 -0
  435. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/serialization.py +0 -0
  436. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/threads/__init__.py +0 -0
  437. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/threads/lockwithabort.py +0 -0
  438. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/time.py +0 -0
  439. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/utils/types.py +0 -0
  440. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/version.py +0 -0
  441. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/__init__.py +0 -0
  442. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/archivefile.py +0 -0
  443. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/bufferedfile.py +0 -0
  444. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/file.py +0 -0
  445. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/filelists/__init__.py +0 -0
  446. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/filelists/filelist.py +0 -0
  447. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/filelists/testing.py +0 -0
  448. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/httpfile.py +0 -0
  449. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/localfile.py +0 -0
  450. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/memfile.py +0 -0
  451. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/sftpfile.py +0 -0
  452. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/smbfile.py +0 -0
  453. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/sshfile.py +0 -0
  454. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/tempfile.py +0 -0
  455. {pyobs_core-1.53.0 → pyobs_core-2.0.0.dev3}/pyobs/vfs/vfs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-core
3
- Version: 1.53.0
3
+ Version: 2.0.0.dev3
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -98,7 +98,7 @@ class Application:
98
98
  super().__init__(identifier="pyobs", **kw)
99
99
 
100
100
  def _format_record(self, record: logging.LogRecord) -> list[tuple[str, Any]]:
101
- pairs = super()._format_record(record)
101
+ pairs: list[tuple[str, Any]] = super()._format_record(record)
102
102
  pairs.append(("PYOBS_MODULE", getattr(record, "pyobs_module", "")))
103
103
  return pairs
104
104
 
@@ -140,8 +140,17 @@ class Application:
140
140
  class_name = cfg["class"]
141
141
  klass = get_class_from_string(class_name)
142
142
 
143
- # create event loop
144
- self._loop = klass.new_event_loop()
143
+ # create event loop — if top-level class doesn't override new_event_loop,
144
+ # check child modules for one that does (e.g. pyobs_gui.GUI in a MultiModule)
145
+ loop_class = klass
146
+ if klass.new_event_loop is Module.new_event_loop:
147
+ for mod_cfg in cfg.get("modules", {}).values():
148
+ if isinstance(mod_cfg, dict) and "class" in mod_cfg:
149
+ child_klass = get_class_from_string(mod_cfg["class"])
150
+ if child_klass.new_event_loop is not Module.new_event_loop:
151
+ loop_class = child_klass
152
+ break
153
+ self._loop = loop_class.new_event_loop()
145
154
  asyncio.set_event_loop(self._loop)
146
155
 
147
156
  # create module and open it
@@ -1,25 +1,28 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import functools
4
5
  import inspect
5
6
  import logging
7
+ import sys
6
8
  from collections.abc import Callable, Coroutine
7
- from typing import TYPE_CHECKING, Any, TypeVar, overload
9
+ from typing import TYPE_CHECKING, Any, overload
8
10
 
9
11
  import pyobs.interfaces
10
12
  from pyobs.events import Event, LogEvent, ModuleClosedEvent
11
13
  from pyobs.interfaces import Interface
14
+ from pyobs.utils.enums import ModuleState
12
15
 
13
16
  from .commlogging import CommLoggingHandler
14
- from .proxy import Proxy
17
+ from .proxy import Proxy, ProxyType, _ProxyContext
15
18
 
16
19
  if TYPE_CHECKING:
17
20
  from pyobs.modules import Module
18
21
 
19
- log = logging.getLogger(__name__)
20
-
22
+ StateCallback = Callable[[Any], None]
23
+ PresenceCallback = Callable[["ModuleState", str], None]
21
24
 
22
- ProxyType = TypeVar("ProxyType")
25
+ log = logging.getLogger(__name__)
23
26
 
24
27
 
25
28
  class Comm:
@@ -27,13 +30,13 @@ class Comm:
27
30
 
28
31
  __module__ = "pyobs.comm"
29
32
 
30
- def __init__(self, cache_proxies: bool = True):
33
+ def __init__(self) -> None:
31
34
  """Creates a comm module."""
32
35
 
33
36
  self._proxies: dict[str, Proxy] = {}
37
+ self._state_subscriptions: dict[str, list[tuple[type[Interface], StateCallback]]] = {}
34
38
  self._module: Module | None = None
35
39
  self._log_queue: asyncio.Queue[LogEvent] = asyncio.Queue()
36
- self._cache_proxies = cache_proxies
37
40
  self._logging_task: asyncio.Task[Any] | None = None
38
41
  self._event_handlers: dict[type[Event], list[Callable[[Event, str], Coroutine[Any, Any, bool]]]] = {}
39
42
  self._closing = asyncio.Event()
@@ -64,9 +67,14 @@ class Comm:
64
67
  """Open module."""
65
68
 
66
69
  # add handler to global logger
67
- handler = CommLoggingHandler(self)
68
- handler.setLevel(logging.INFO)
69
- logging.getLogger().addHandler(handler)
70
+ root_logger = logging.getLogger()
71
+ if not any(isinstance(h, CommLoggingHandler) for h in root_logger.handlers):
72
+ from pyobs.utils.logging.context import ModuleNameFilter
73
+
74
+ handler = CommLoggingHandler(self)
75
+ handler.setLevel(logging.INFO)
76
+ handler.addFilter(ModuleNameFilter())
77
+ root_logger.addHandler(handler)
70
78
 
71
79
  # start logging thread
72
80
  self._logging_task = asyncio.create_task(self._logging())
@@ -117,7 +125,7 @@ class Comm:
117
125
  return None
118
126
 
119
127
  # if client doesn't exist or we disabled caching, fetch a new proxy
120
- if client not in self._proxies or not self._cache_proxies:
128
+ if client not in self._proxies:
121
129
  # get interfaces
122
130
  try:
123
131
  interfaces = await self.get_interfaces(client)
@@ -126,18 +134,20 @@ class Comm:
126
134
 
127
135
  # create new proxy
128
136
  proxy = Proxy(self, client, interfaces)
137
+
138
+ # subscribe to state
139
+ for interface in interfaces:
140
+ if getattr(interface, "State", None) is not None:
141
+ await self.subscribe_state(client, interface, functools.partial(proxy.update_state, interface))
142
+
129
143
  self._proxies[client] = proxy
130
144
 
131
145
  # return proxy
132
146
  return self._proxies[client]
133
147
 
134
- @overload
135
- async def proxy(self, name_or_object: str | object, obj_type: type[ProxyType]) -> ProxyType: ...
136
-
137
- @overload
138
- async def proxy(self, name_or_object: str | object, obj_type: type[ProxyType] | None = None) -> Any: ...
139
-
140
- async def proxy(self, name_or_object: str | object, obj_type: type[ProxyType] | None = None) -> Any | ProxyType:
148
+ async def _resolve_proxy(
149
+ self, name_or_object: str | object, obj_type: type[ProxyType] | None = None
150
+ ) -> Any | ProxyType:
141
151
  """Returns object directly if it is of given type. Otherwise get proxy of client with given name and check type.
142
152
 
143
153
  If name_or_object is an object:
@@ -184,16 +194,41 @@ class Comm:
184
194
  # completely wrong...
185
195
  raise ValueError(f'Given parameter is neither a name nor an object of requested type "{obj_type}".')
186
196
 
187
- async def safe_proxy(
197
+ async def _safe_resolve_proxy(
188
198
  self, name_or_object: str | object, obj_type: type[ProxyType] | None = None
189
199
  ) -> Any | ProxyType | None:
190
200
  """Calls proxy() in a safe way and returns None instead of raising an exception."""
191
201
 
192
202
  try:
193
- return await self.proxy(name_or_object, obj_type)
203
+ return await self._resolve_proxy(name_or_object, obj_type)
194
204
  except ValueError:
195
205
  return None
196
206
 
207
+ @overload
208
+ def proxy(self, name_or_object: str | object, obj_type: type[ProxyType]) -> _ProxyContext[ProxyType]: ...
209
+ @overload
210
+ def proxy(self, name_or_object: str | object, obj_type: None = None) -> _ProxyContext[Any]: ...
211
+
212
+ def proxy(self, name_or_object: str | object, obj_type: type[ProxyType] | None = None) -> _ProxyContext[Any]:
213
+ """Returns a context manager; use as `async with self.proxy(...) as x:`."""
214
+ return _ProxyContext(self._resolve_proxy(name_or_object, obj_type))
215
+
216
+ @overload
217
+ def safe_proxy(
218
+ self, name_or_object: str | object, obj_type: type[ProxyType]
219
+ ) -> _ProxyContext[ProxyType | None]: ...
220
+ @overload
221
+ def safe_proxy(self, name_or_object: str | object, obj_type: None = None) -> _ProxyContext[Any]: ...
222
+
223
+ def safe_proxy(self, name_or_object: str | object, obj_type: type[ProxyType] | None = None) -> _ProxyContext[Any]:
224
+ """Same as proxy(), but yields None inside the block instead of raising."""
225
+ return _ProxyContext(self._safe_resolve_proxy(name_or_object, obj_type))
226
+
227
+ async def has_proxy(self, name_or_object: str | object, obj_type: type[Any] | None = None) -> bool:
228
+ """True if a proxy of the given type can currently be resolved. Doesn't keep a reference
229
+ to it, so doesn't need async with the way proxy()/safe_proxy() do."""
230
+ return await self._safe_resolve_proxy(name_or_object, obj_type) is not None
231
+
197
232
  async def _client_disconnected(self, event: Event, sender: str) -> bool:
198
233
  """Called when a client disconnects.
199
234
 
@@ -203,9 +238,15 @@ class Comm:
203
238
 
204
239
  """
205
240
 
206
- # if a client disconnects, we remove its proxy
241
+ # if a client disconnects, clear its proxy state then evict it
207
242
  if sender in self._proxies:
243
+ self._proxies[sender].clear_state()
208
244
  del self._proxies[sender]
245
+
246
+ # tear down any state subscriptions held for that client
247
+ for interface, callback in self._state_subscriptions.pop(sender, []):
248
+ await self.unsubscribe_state(sender, interface, callback)
249
+
209
250
  return True
210
251
 
211
252
  @property
@@ -397,6 +438,149 @@ class Comm:
397
438
  ) -> None:
398
439
  pass
399
440
 
441
+ @staticmethod
442
+ def _interface_from_state(state_cls: type) -> type:
443
+ outer_name = state_cls.__qualname__.rsplit(".", 1)[0]
444
+ return getattr(sys.modules[state_cls.__module__], outer_name)
445
+
446
+ async def set_state(self, state: Any) -> None:
447
+ """Publish state for this module.
448
+
449
+ Args:
450
+ interface: Interface type for the state.
451
+ state: State object to publish.
452
+ """
453
+ interface = Comm._interface_from_state(type(state))
454
+ await self._set_state(interface, state)
455
+
456
+ async def _set_state(self, interface: type[Interface], state: Any) -> None:
457
+ pass
458
+
459
+ @staticmethod
460
+ def _interface_from_capabilities(caps_cls: type) -> type:
461
+ outer_name = caps_cls.__qualname__.rsplit(".", 1)[0]
462
+ return getattr(sys.modules[caps_cls.__module__], outer_name)
463
+
464
+ async def set_capabilities(self, capabilities: Any) -> None:
465
+ """Publish capabilities for this module.
466
+
467
+ Called by Module.open() for each interface that defines a Capabilities
468
+ dataclass. Not intended to be called directly by module authors after
469
+ that point — capabilities are fixed for the module lifetime.
470
+
471
+ Args:
472
+ capabilities: Capabilities dataclass instance.
473
+ """
474
+ interface = Comm._interface_from_capabilities(type(capabilities))
475
+ await self._set_capabilities(interface, capabilities)
476
+
477
+ async def _set_capabilities(self, interface: type[Interface], capabilities: Any) -> None:
478
+ pass
479
+
480
+ async def get_capabilities(self, module: str, interface: type[Interface]) -> Any | None:
481
+ """Fetch and deserialize capabilities for a remote module's interface.
482
+
483
+ Args:
484
+ module: Module name (e.g. "camera").
485
+ interface: Interface class whose Capabilities dataclass to fetch.
486
+
487
+ Returns:
488
+ Deserialized Capabilities dataclass instance, or None if not published.
489
+ """
490
+ return await self._get_capabilities(module, interface)
491
+
492
+ async def _get_capabilities(self, module: str, interface: type[Interface]) -> Any | None:
493
+ return None
494
+
495
+ async def set_presence(self, state: ModuleState, error_string: str = "") -> None:
496
+ """Publish presence for this module (module lifecycle state).
497
+
498
+ Called automatically by Module.set_state — not intended to be called
499
+ directly by module authors.
500
+
501
+ Args:
502
+ state: Current module lifecycle state.
503
+ error_string: Error message, used when state is ERROR.
504
+ """
505
+ await self._set_presence(state, error_string)
506
+
507
+ async def _set_presence(self, state: ModuleState, error_string: str = "") -> None:
508
+ pass
509
+
510
+ def get_client_state(self, module: str) -> tuple[ModuleState, str] | None:
511
+ """Return the last known presence state of a connected module.
512
+
513
+ Returns (ModuleState, error_string) or None if the module is not connected.
514
+ This replaces the old get_state()/get_error_string() RPC pattern.
515
+ """
516
+ return self._get_client_state(module)
517
+
518
+ def _get_client_state(self, module: str) -> tuple[ModuleState, str] | None:
519
+ return None
520
+
521
+ async def subscribe_state(
522
+ self,
523
+ module: str,
524
+ interface: type[Interface],
525
+ callback: StateCallback,
526
+ ) -> None:
527
+ """Subscribe to state updates for a given module and interface.
528
+
529
+ Delivers the current value immediately on subscribe.
530
+
531
+ Args:
532
+ module: Name of remote module.
533
+ interface: Interface type to subscribe to.
534
+ callback: Called with state object on each update.
535
+ """
536
+ self._state_subscriptions.setdefault(module, []).append((interface, callback))
537
+ await self._subscribe_state(module, interface, callback)
538
+
539
+ async def _subscribe_state(
540
+ self,
541
+ module: str,
542
+ interface: type[Interface],
543
+ callback: StateCallback,
544
+ ) -> None:
545
+ pass
546
+
547
+ async def unsubscribe_state(
548
+ self,
549
+ module: str,
550
+ interface: type[Interface],
551
+ callback: StateCallback,
552
+ ) -> None:
553
+ """Unsubscribe from state updates.
554
+
555
+ Args:
556
+ module: Name of remote module.
557
+ interface: Interface type to unsubscribe from.
558
+ callback: Callback that was registered.
559
+ """
560
+ await self._unsubscribe_state(module, interface, callback)
561
+
562
+ async def _unsubscribe_state(
563
+ self,
564
+ module: str,
565
+ interface: type[Interface],
566
+ callback: StateCallback,
567
+ ) -> None:
568
+ pass
569
+
570
+ async def subscribe_presence(self, module: str, callback: PresenceCallback) -> None:
571
+ """Subscribe to presence updates for a given module.
572
+
573
+ Delivers the current value immediately, then on every change.
574
+
575
+ Args:
576
+ module: Name of remote module.
577
+ callback: Called with (ModuleState, error_string) on each update.
578
+ """
579
+ await self._subscribe_presence(module, callback)
580
+
581
+ async def _subscribe_presence(self, module: str, callback: PresenceCallback) -> None:
582
+ pass
583
+
400
584
  def _send_event_to_module(self, event: Event, from_client: str) -> None:
401
585
  """Send an event to all connected modules.
402
586
 
@@ -34,9 +34,12 @@ class CommLoggingHandler(logging.Handler):
34
34
  # format message
35
35
  msg = self._formatter.format(rec) # noqa: UP031
36
36
 
37
- # create and send event
37
+ # create and send event — use pyobs_module if set (MultiModule context)
38
38
  time = datetime.fromtimestamp(rec.created, tz=UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")
39
- entry = LogEvent(time, rec.levelname, os.path.basename(rec.pathname), rec.funcName, rec.lineno, msg)
39
+ sender = getattr(rec, "pyobs_module", "") or ""
40
+ entry = LogEvent(
41
+ time, rec.levelname, os.path.basename(rec.pathname), rec.funcName, rec.lineno, msg, sender=sender
42
+ )
40
43
  self._comm.log_message(entry)
41
44
 
42
45
 
@@ -0,0 +1,157 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Coroutine
4
+ from typing import Any
5
+
6
+ from pyobs.comm import Comm
7
+ from pyobs.comm.comm import PresenceCallback
8
+ from pyobs.events import Event
9
+ from pyobs.interfaces import Interface
10
+ from pyobs.utils.enums import ModuleState
11
+ from pyobs.utils.types import cast_response_to_real
12
+
13
+ from .localnetwork import LocalNetwork
14
+
15
+
16
+ class LocalComm(Comm):
17
+ def __init__(self, name: str, *args: Any, **kwargs: Any):
18
+ Comm.__init__(self, *args, **kwargs)
19
+
20
+ self._name = name
21
+ self._network = LocalNetwork()
22
+ self._network.connect_client(self)
23
+
24
+ # in-memory state and capabilities storage
25
+ self._states: dict[str, Any] = {} # node -> state object
26
+ self._state_handlers: dict[str, list[tuple[type[Interface], Callable[[Any], None]]]] = {}
27
+ self._capabilities: dict[type[Interface], Any] = {} # interface -> Capabilities object
28
+ self._presence: tuple[ModuleState, str] = (ModuleState.READY, "")
29
+ self._presence_callbacks: dict[str, list[PresenceCallback]] = {}
30
+
31
+ @property
32
+ def name(self) -> str:
33
+ """Name of this client."""
34
+ return self._name
35
+
36
+ @property
37
+ def clients(self) -> list[str]:
38
+ """Returns list of currently connected clients."""
39
+ return self._network.get_client_names()
40
+
41
+ async def get_interfaces(self, client: str) -> list[type[Interface]]:
42
+ """Returns list of interfaces for given client."""
43
+ remote_client = self._network.get_client(client)
44
+ return [] if not remote_client.has_module else remote_client.module.interfaces
45
+
46
+ async def _supports_interface(self, client: str, interface: type[Interface]) -> bool:
47
+ """Checks whether the given client supports the given interface."""
48
+ interfaces = await self.get_interfaces(client)
49
+ return interface in interfaces
50
+
51
+ async def execute(self, client: str, method: str, annotation: dict[str, Any], *args: Any) -> Any:
52
+ """Execute a given method on a remote client."""
53
+ remote_client = self._network.get_client(client)
54
+ if remote_client.module is None:
55
+ raise ValueError
56
+ simple_results = await remote_client.module.execute(method, *args, sender=self.name)
57
+ real_results = cast_response_to_real(
58
+ simple_results, annotation["return"], self.cast_to_real_pre, self.cast_to_real_post
59
+ )
60
+ return real_results
61
+
62
+ async def send_event(self, event: Event) -> None:
63
+ """Send an event to other clients."""
64
+ from pyobs.events import LogEvent as _LogEvent
65
+
66
+ sender = self.name
67
+ if isinstance(event, _LogEvent) and event.sender:
68
+ sender = event.sender
69
+ remote_clients = self._network.get_clients()
70
+ for client in remote_clients:
71
+ client._send_event_to_module(event, sender)
72
+
73
+ async def _register_events(
74
+ self, events: list[type[Event]], handler: Callable[[Event, str], Coroutine[Any, Any, bool]] | None = None
75
+ ) -> None:
76
+ pass
77
+
78
+ # -------------------------------------------------------------------------
79
+ # State
80
+ # -------------------------------------------------------------------------
81
+
82
+ async def _set_state(self, interface: type[Interface], state: Any) -> None:
83
+ """Publish state locally and dispatch to subscribers."""
84
+ node = f"pyobs:state:{self._name}:{interface.__name__}:{interface.version}"
85
+ self._states[node] = state
86
+
87
+ # dispatch to all subscribers for this node
88
+ if node in self._state_handlers:
89
+ for _, callback in self._state_handlers[node]:
90
+ callback(state)
91
+
92
+ async def _subscribe_state(self, module: str, interface: type[Interface], callback: Callable[[Any], None]) -> None:
93
+ """Subscribe to state updates from a remote module."""
94
+ node = f"pyobs:state:{module}:{interface.__name__}:{interface.version}"
95
+
96
+ if node not in self._state_handlers:
97
+ self._state_handlers[node] = []
98
+ self._state_handlers[node].append((interface, callback))
99
+
100
+ # also register on the remote comm so it can dispatch to us
101
+ try:
102
+ remote = self._network.get_client(module)
103
+ if node not in remote._state_handlers:
104
+ remote._state_handlers[node] = []
105
+ remote._state_handlers[node].append((interface, callback))
106
+
107
+ # deliver current value immediately if available
108
+ if node in remote._states:
109
+ callback(remote._states[node])
110
+ except KeyError:
111
+ pass # remote not connected yet
112
+
113
+ # -------------------------------------------------------------------------
114
+ # Capabilities
115
+ # -------------------------------------------------------------------------
116
+
117
+ async def _set_capabilities(self, interface: type[Interface], capabilities: Any) -> None:
118
+ """Store capabilities locally."""
119
+ self._capabilities[interface] = capabilities
120
+
121
+ async def _get_capabilities(self, module: str, interface: type[Interface]) -> Any | None:
122
+ """Fetch capabilities from a remote module."""
123
+ if not hasattr(interface, "Capabilities"):
124
+ return None
125
+ try:
126
+ remote = self._network.get_client(module)
127
+ return remote._capabilities.get(interface)
128
+ except KeyError:
129
+ return None
130
+
131
+ # -------------------------------------------------------------------------
132
+ # Presence
133
+ # -------------------------------------------------------------------------
134
+
135
+ async def _set_presence(self, state: ModuleState, error_string: str = "") -> None:
136
+ """Store presence state and dispatch to all subscribers."""
137
+ self._presence = (state, error_string)
138
+ for client in self._network.get_clients():
139
+ for cb in client._presence_callbacks.get(self._name, []):
140
+ cb(state, error_string)
141
+
142
+ def _get_client_state(self, module: str) -> tuple[ModuleState, str] | None:
143
+ """Return presence state of a connected module."""
144
+ try:
145
+ remote = self._network.get_client(module)
146
+ return remote._presence
147
+ except KeyError:
148
+ return None
149
+
150
+ async def _subscribe_presence(self, module: str, callback: PresenceCallback) -> None:
151
+ """Register a presence callback and deliver the current state immediately."""
152
+ self._presence_callbacks.setdefault(module, []).append(callback)
153
+ try:
154
+ remote = self._network.get_client(module)
155
+ callback(*remote._presence)
156
+ except KeyError:
157
+ pass
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import inspect
4
5
  import types
5
- from typing import TYPE_CHECKING, Any, get_type_hints
6
+ from collections.abc import Coroutine
7
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar, get_type_hints
6
8
 
7
9
  from pyobs.interfaces import Interface
8
10
  from pyobs.utils.types import cast_bound_arguments_to_simple
@@ -10,6 +12,9 @@ from pyobs.utils.types import cast_bound_arguments_to_simple
10
12
  if TYPE_CHECKING:
11
13
  from pyobs.comm import Comm
12
14
 
15
+ """Class of a proxy."""
16
+ ProxyType = TypeVar("ProxyType")
17
+
13
18
 
14
19
  class Proxy:
15
20
  """A proxy for remote pyobs modules."""
@@ -48,6 +53,9 @@ class Proxy:
48
53
  # create methods
49
54
  self._methods = self._create_methods()
50
55
 
56
+ # store state
57
+ self._state: dict[type[Interface], Any] = {}
58
+
51
59
  @property
52
60
  def name(self) -> str:
53
61
  """Name of the client."""
@@ -150,5 +158,59 @@ class Proxy:
150
158
 
151
159
  return inner
152
160
 
161
+ def update_state(self, interface: type[Interface], state: Any) -> None:
162
+ """Called by Comm whenever a new state arrives. Not intended to be called directly by module code."""
163
+ self._state[interface] = state
164
+
165
+ def clear_state(self) -> None:
166
+ """Clear all cached state. Called by Comm when the remote module disconnects."""
167
+ self._state.clear()
168
+
169
+ def state(self, interface: type[Interface]) -> Any | None:
170
+ """Latest known state for the given interface, or None if nothing has arrived yet."""
171
+ return self._state.get(interface)
172
+
173
+ async def wait_for_state(
174
+ self,
175
+ interface: type[Interface],
176
+ timeout: float = 10.0,
177
+ ) -> Any:
178
+ """Return state immediately if available, otherwise wait for the first update."""
179
+ if self._state.get(interface) is not None:
180
+ return self._state[interface]
181
+
182
+ event = asyncio.Event()
183
+
184
+ def _notify(state: Any) -> None:
185
+ self._state[interface] = state
186
+ event.set()
187
+
188
+ # patch in a one-shot notifier alongside the existing callback
189
+ await self._comm.subscribe_state(self._client, interface, _notify)
190
+ try:
191
+ await asyncio.wait_for(event.wait(), timeout=timeout)
192
+ finally:
193
+ await self._comm.unsubscribe_state(self._client, interface, _notify)
194
+
195
+ return self._state.get(interface)
196
+
197
+
198
+ class _ProxyContext(Generic[ProxyType]):
199
+ """Returned by Comm.proxy() / Object.proxy() / Comm.safe_proxy(). Must be used as:
200
+ async with self.proxy("camera", ICooling) as camera:
201
+ ...
202
+ """
203
+
204
+ def __init__(self, coro: Coroutine[Any, Any, ProxyType]) -> None:
205
+ self._coro = coro
206
+
207
+ async def __aenter__(self) -> ProxyType:
208
+ return await self._coro
209
+
210
+ async def __aexit__(self, *exc_info: Any) -> None:
211
+ # intentionally a no-op: the underlying Proxy is owned and cached
212
+ # by Comm, not by this block, and stays alive for other callers.
213
+ pass
214
+
153
215
 
154
216
  __all__ = ["Proxy"]