pyobs-core 2.0.0.dev47__tar.gz → 2.0.0.dev49__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 (446) hide show
  1. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/PKG-INFO +2 -2
  2. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/application.py +130 -61
  3. pyobs_core-2.0.0.dev49/pyobs/comm/xmpp/__init__.py +3 -0
  4. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xmppcomm.py +24 -4
  5. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/module.py +5 -2
  6. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyproject.toml +2 -2
  7. pyobs_core-2.0.0.dev47/pyobs/comm/xmpp/__init__.py +0 -3
  8. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/.gitignore +0 -0
  9. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/LICENSE +0 -0
  10. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/__init__.py +0 -0
  11. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/background_task.py +0 -0
  12. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/cli/__init__.py +0 -0
  13. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/cli/_cli.py +0 -0
  14. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/cli/pyobs.py +0 -0
  15. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/cli/pyobsd.py +0 -0
  16. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/cli/pyobsw.py +0 -0
  17. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/__init__.py +0 -0
  18. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/comm.py +0 -0
  19. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/commlogging.py +0 -0
  20. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/dummy/__init__.py +0 -0
  21. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/dummy/dummycomm.py +0 -0
  22. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/local/__init__.py +0 -0
  23. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/local/localcomm.py +0 -0
  24. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/local/localnetwork.py +0 -0
  25. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/proxy.py +0 -0
  26. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/rpc.py +0 -0
  27. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/serializer.py +0 -0
  28. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
  29. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
  30. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
  31. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
  32. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +0 -0
  33. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
  34. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
  35. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/comm/xmpp/xmppclient.py +0 -0
  36. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/__init__.py +0 -0
  37. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/badweather.py +0 -0
  38. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/event.py +0 -0
  39. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/exposurestatuschanged.py +0 -0
  40. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/filterchanged.py +0 -0
  41. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/focusfound.py +0 -0
  42. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/goodweather.py +0 -0
  43. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/log.py +0 -0
  44. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/modechanged.py +0 -0
  45. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/moduleclosed.py +0 -0
  46. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/moduleopened.py +0 -0
  47. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/motionstatuschanged.py +0 -0
  48. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/move.py +0 -0
  49. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/newimage.py +0 -0
  50. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/newspectrum.py +0 -0
  51. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/offsets.py +0 -0
  52. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/roofclosing.py +0 -0
  53. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/roofopened.py +0 -0
  54. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/taskfailed.py +0 -0
  55. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/taskfinished.py +0 -0
  56. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/taskstarted.py +0 -0
  57. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/events/testevent.py +0 -0
  58. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/__init__.py +0 -0
  59. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/image.py +0 -0
  60. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/__init__.py +0 -0
  61. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/altazoffsets.py +0 -0
  62. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/exptime.py +0 -0
  63. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/genericoffset.py +0 -0
  64. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/onskydistance.py +0 -0
  65. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/pixeloffsets.py +0 -0
  66. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/radecoffsets.py +0 -0
  67. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/meta/skyoffsets.py +0 -0
  68. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processor.py +0 -0
  69. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/__init__.py +0 -0
  70. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/_daobackgroundremover.py +0 -0
  71. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/annotation/__init__.py +0 -0
  72. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/annotation/_pillow.py +0 -0
  73. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/annotation/circle.py +0 -0
  74. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/annotation/crosshair.py +0 -0
  75. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/annotation/text.py +0 -0
  76. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/astrometry/__init__.py +0 -0
  77. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
  78. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
  79. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
  80. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
  81. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/astrometry/astrometry.py +0 -0
  82. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/astrometry/dotnet.py +0 -0
  83. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/calibration/__init__.py +0 -0
  84. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
  85. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
  86. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/calibration/calibration.py +0 -0
  87. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/detection/__init__.py +0 -0
  88. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
  89. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/detection/_source_catalog.py +0 -0
  90. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/detection/daophot.py +0 -0
  91. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/detection/pysep.py +0 -0
  92. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/detection/simpledisk.py +0 -0
  93. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/detection/sourcedetection.py +0 -0
  94. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/exptime/__init__.py +0 -0
  95. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/exptime/exptime.py +0 -0
  96. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/exptime/star.py +0 -0
  97. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/__init__.py +0 -0
  98. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/addfitsheaders.py +0 -0
  99. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/download.py +0 -0
  100. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/flip.py +0 -0
  101. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/grayscale.py +0 -0
  102. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/httpserver.py +0 -0
  103. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/normalize.py +0 -0
  104. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/save.py +0 -0
  105. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/saveimage.py +0 -0
  106. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/smooth.py +0 -0
  107. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/image/softbin.py +0 -0
  108. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/misc/__init__.py +0 -0
  109. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/misc/addmask.py +0 -0
  110. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
  111. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/misc/circularmask.py +0 -0
  112. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/misc/createfilename.py +0 -0
  113. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
  114. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/misc/removebackground.py +0 -0
  115. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/modules/__init__.py +0 -0
  116. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
  117. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/__init__.py +0 -0
  118. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/astrometry.py +0 -0
  119. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/brighteststar.py +0 -0
  120. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
  121. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
  122. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
  123. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/fitsheader.py +0 -0
  124. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/offsets.py +0 -0
  125. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
  126. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/projected.py +0 -0
  127. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/offsets/spilled_light.py +0 -0
  128. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/__init__.py +0 -0
  129. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
  130. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
  131. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
  132. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
  133. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/photometry.py +0 -0
  134. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/photutil.py +0 -0
  135. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/photometry/pysep.py +0 -0
  136. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/wcs/__init__.py +0 -0
  137. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
  138. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IAbortable.py +0 -0
  139. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IAcquisition.py +0 -0
  140. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IAutoFocus.py +0 -0
  141. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IAutoGuiding.py +0 -0
  142. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IAutonomous.py +0 -0
  143. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IBinning.py +0 -0
  144. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ICalibrate.py +0 -0
  145. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ICamera.py +0 -0
  146. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IConfig.py +0 -0
  147. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ICooling.py +0 -0
  148. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IData.py +0 -0
  149. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IDataSequence.py +0 -0
  150. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IDome.py +0 -0
  151. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IExposure.py +0 -0
  152. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IExposureTime.py +0 -0
  153. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IFilters.py +0 -0
  154. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
  155. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
  156. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IFlatField.py +0 -0
  157. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IFocusModel.py +0 -0
  158. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IFocuser.py +0 -0
  159. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IGain.py +0 -0
  160. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IImageFormat.py +0 -0
  161. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IImageType.py +0 -0
  162. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IMode.py +0 -0
  163. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IModule.py +0 -0
  164. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IMotion.py +0 -0
  165. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IMultiFiber.py +0 -0
  166. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IOffsetsAltAz.py +0 -0
  167. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IOffsetsRaDec.py +0 -0
  168. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingAltAz.py +0 -0
  169. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingBody.py +0 -0
  170. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingHeliocentricPolar.py +0 -0
  171. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingHeliographicStonyhurst.py +0 -0
  172. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingHelioprojective.py +0 -0
  173. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingOrbitalElements.py +0 -0
  174. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingRaDec.py +0 -0
  175. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IPointingSeries.py +0 -0
  176. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IReady.py +0 -0
  177. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IRoof.py +0 -0
  178. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IRotation.py +0 -0
  179. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IRunnable.py +0 -0
  180. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IRunning.py +0 -0
  181. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IScriptRunner.py +0 -0
  182. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ISpectrograph.py +0 -0
  183. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IStartStop.py +0 -0
  184. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IStructuredConfig.py +0 -0
  185. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ISyncTarget.py +0 -0
  186. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ITelescope.py +0 -0
  187. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ITemperatures.py +0 -0
  188. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ITrackingMode.py +0 -0
  189. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/ITrackingRate.py +0 -0
  190. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IVideo.py +0 -0
  191. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IWeather.py +0 -0
  192. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/IWindow.py +0 -0
  193. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/__init__.py +0 -0
  194. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/interfaces/interface.py +0 -0
  195. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/__init__.py +0 -0
  196. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/camerasettings.py +0 -0
  197. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/fitsheader.py +0 -0
  198. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/fitsnamespace.py +0 -0
  199. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/follow.py +0 -0
  200. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/motionstatus.py +0 -0
  201. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/pipeline.py +0 -0
  202. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/waitformotion.py +0 -0
  203. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/mixins/weatheraware.py +0 -0
  204. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/__init__.py +0 -0
  205. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/__init__.py +0 -0
  206. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/adaptive.py +0 -0
  207. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/basecamera.py +0 -0
  208. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/basespectrograph.py +0 -0
  209. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/basevideo.py +0 -0
  210. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/dummycamera.py +0 -0
  211. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/dummyspectrograph.py +0 -0
  212. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/dummyvideo.py +0 -0
  213. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/camera/pipelinecamera.py +0 -0
  214. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/flatfield/__init__.py +0 -0
  215. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/flatfield/flatfield.py +0 -0
  216. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/flatfield/pointing.py +0 -0
  217. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/flatfield/scheduler.py +0 -0
  218. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/focus/__init__.py +0 -0
  219. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/focus/dummyautofocus.py +0 -0
  220. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/focus/focusmodel.py +0 -0
  221. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/focus/focusseries.py +0 -0
  222. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/image/__init__.py +0 -0
  223. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/image/imagewatcher.py +0 -0
  224. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/image/imagewriter.py +0 -0
  225. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/image/pipeline.py +0 -0
  226. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/image/seeing.py +0 -0
  227. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/__init__.py +0 -0
  228. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/_base.py +0 -0
  229. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/_baseguiding.py +0 -0
  230. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/acquisition.py +0 -0
  231. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/autoguiding.py +0 -0
  232. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/dummyacquisition.py +0 -0
  233. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/dummyguiding.py +0 -0
  234. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
  235. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
  236. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
  237. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
  238. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
  239. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/pointing/scienceframeguiding.py +0 -0
  240. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/robotic/__init__.py +0 -0
  241. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/robotic/mastermind.py +0 -0
  242. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/robotic/pointing.py +0 -0
  243. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/robotic/scheduler.py +0 -0
  244. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/robotic/scriptrunner.py +0 -0
  245. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/roof/__init__.py +0 -0
  246. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/roof/basedome.py +0 -0
  247. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/roof/baseroof.py +0 -0
  248. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/roof/dummyroof.py +0 -0
  249. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/telescope/__init__.py +0 -0
  250. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/telescope/_dummytelescopebase.py +0 -0
  251. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/telescope/basetelescope.py +0 -0
  252. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/telescope/dummyaltaztelescope.py +0 -0
  253. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/telescope/dummyradectelescope.py +0 -0
  254. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/telescope/dummysolartelescope.py +0 -0
  255. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/test/__init__.py +0 -0
  256. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/test/standalone.py +0 -0
  257. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/__init__.py +0 -0
  258. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/dummymode.py +0 -0
  259. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/fluentlogger.py +0 -0
  260. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/httpfilecache.py +0 -0
  261. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/kiosk.py +0 -0
  262. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/matrix.py +0 -0
  263. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/stellarium.py +0 -0
  264. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/telegram.py +0 -0
  265. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/utils/trigger.py +0 -0
  266. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/weather/__init__.py +0 -0
  267. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/weather/mockweather.py +0 -0
  268. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/weather/weather.py +0 -0
  269. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/weather/weather_api.py +0 -0
  270. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/modules/weather/weather_state.py +0 -0
  271. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/object.py +0 -0
  272. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/py.typed +0 -0
  273. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/__init__.py +0 -0
  274. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/observation.py +0 -0
  275. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/__init__.py +0 -0
  276. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
  277. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
  278. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
  279. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
  280. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
  281. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
  282. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
  283. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
  284. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/dataprovider.py +0 -0
  285. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
  286. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
  287. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
  288. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/constant.py +0 -0
  289. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/follow.py +0 -0
  290. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/interval.py +0 -0
  291. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/merit.py +0 -0
  292. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
  293. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/random.py +0 -0
  294. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
  295. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/merits/transit.py +0 -0
  296. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
  297. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
  298. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
  299. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
  300. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/heliocentricpolartarget.py +0 -0
  301. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/helioprojectivetarget.py +0 -0
  302. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
  303. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
  304. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
  305. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
  306. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/targets/target.py +0 -0
  307. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
  308. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/__init__.py +0 -0
  309. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
  310. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
  311. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
  312. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
  313. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/control/__init__.py +0 -0
  314. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/control/cases.py +0 -0
  315. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/control/conditional.py +0 -0
  316. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/control/parallel.py +0 -0
  317. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/control/selector.py +0 -0
  318. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/control/sequential.py +0 -0
  319. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/exceptions.py +0 -0
  320. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
  321. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
  322. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
  323. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
  324. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/script.py +0 -0
  325. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/utils/__init__.py +0 -0
  326. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/utils/callmodule.py +0 -0
  327. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
  328. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/scripts/utils/log.py +0 -0
  329. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/__init__.py +0 -0
  330. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/backend/__init__.py +0 -0
  331. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/backend/observationarchive.py +0 -0
  332. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/backend/taskarchive.py +0 -0
  333. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
  334. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
  335. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
  336. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/__init__.py +0 -0
  337. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/_portal.py +0 -0
  338. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
  339. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
  340. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/configdb.py +0 -0
  341. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
  342. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
  343. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
  344. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
  345. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
  346. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
  347. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/task.py +0 -0
  348. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
  349. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
  350. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/memory/__init__.py +0 -0
  351. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
  352. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
  353. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/observationarchive.py +0 -0
  354. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/storage/taskarchive.py +0 -0
  355. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/task.py +0 -0
  356. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/taskrunner.py +0 -0
  357. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/__init__.py +0 -0
  358. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/archive/__init__.py +0 -0
  359. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/archive/archive.py +0 -0
  360. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/archive/local_archive.py +0 -0
  361. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/archive/pyobs_archive.py +0 -0
  362. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/exptime/__init__.py +0 -0
  363. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/exptime/exptime.py +0 -0
  364. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
  365. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
  366. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
  367. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/flatfielder.py +0 -0
  368. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
  369. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
  370. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
  371. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
  372. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
  373. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
  374. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
  375. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
  376. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/__init__.py +0 -0
  377. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/average.py +0 -0
  378. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/cache.py +0 -0
  379. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/config.py +0 -0
  380. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/config_schema.py +0 -0
  381. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/coordinates.py +0 -0
  382. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/curvefit.py +0 -0
  383. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/enums.py +0 -0
  384. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/exceptions.py +0 -0
  385. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/fits.py +0 -0
  386. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/focusseries/__init__.py +0 -0
  387. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/focusseries/base.py +0 -0
  388. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/focusseries/photometry.py +0 -0
  389. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/focusseries/projection.py +0 -0
  390. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/grids/__init__.py +0 -0
  391. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/grids/filters.py +0 -0
  392. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/grids/grid.py +0 -0
  393. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/grids/gridnode.py +0 -0
  394. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/grids/pipeline.py +0 -0
  395. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/__init__.py +0 -0
  396. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/__init__.py +0 -0
  397. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/binningwidget.py +0 -0
  398. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
  399. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/exposewidget.py +0 -0
  400. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
  401. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
  402. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
  403. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
  404. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/http.py +0 -0
  405. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/influxdb.py +0 -0
  406. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/logger.py +0 -0
  407. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/logging/__init__.py +0 -0
  408. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/logging/context.py +0 -0
  409. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/logging/resolvableerror.py +0 -0
  410. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/modulegui.py +0 -0
  411. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/offsets/__init__.py +0 -0
  412. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
  413. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/offsets/applyoffsets.py +0 -0
  414. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
  415. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/parallel.py +0 -0
  416. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/pipeline/__init__.py +0 -0
  417. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/pipeline/night.py +0 -0
  418. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/pipeline/pipeline.py +0 -0
  419. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/publisher/__init__.py +0 -0
  420. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/publisher/csv.py +0 -0
  421. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/publisher/http.py +0 -0
  422. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/publisher/log.py +0 -0
  423. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/publisher/multi.py +0 -0
  424. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/publisher/publisher.py +0 -0
  425. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/serialization.py +0 -0
  426. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/shellcommand.py +0 -0
  427. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/threads/__init__.py +0 -0
  428. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/threads/lockwithabort.py +0 -0
  429. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/time.py +0 -0
  430. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/utils/units.py +0 -0
  431. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/version.py +0 -0
  432. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/__init__.py +0 -0
  433. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/archivefile.py +0 -0
  434. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/bufferedfile.py +0 -0
  435. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/file.py +0 -0
  436. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/filelists/__init__.py +0 -0
  437. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/filelists/filelist.py +0 -0
  438. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/filelists/testing.py +0 -0
  439. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/httpfile.py +0 -0
  440. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/localfile.py +0 -0
  441. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/memfile.py +0 -0
  442. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/sftpfile.py +0 -0
  443. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/smbfile.py +0 -0
  444. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/sshfile.py +0 -0
  445. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/tempfile.py +0 -0
  446. {pyobs_core-2.0.0.dev47 → pyobs_core-2.0.0.dev49}/pyobs/vfs/vfs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-core
3
- Version: 2.0.0.dev47
3
+ Version: 2.0.0.dev49
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ Requires-Dist: logging-journald>=0.6.11
20
20
  Requires-Dist: numpy<3,>=2.2.5
21
21
  Requires-Dist: pandas>=2.2.3
22
22
  Requires-Dist: paramiko>=3.5.1
23
- Requires-Dist: pillow>=11.3.0
23
+ Requires-Dist: pillow>=12.3.0
24
24
  Requires-Dist: psutil>=7.2.2
25
25
  Requires-Dist: py-expression-eval<0.4,>=0.3.14
26
26
  Requires-Dist: pydantic>=2.12.5
@@ -5,6 +5,7 @@ import os
5
5
  import platform
6
6
  import signal
7
7
  import warnings
8
+ from collections.abc import Awaitable, Callable
8
9
  from io import StringIO
9
10
  from typing import Any, TypedDict
10
11
 
@@ -56,7 +57,9 @@ class Application:
56
57
 
57
58
  def __init__(
58
59
  self,
59
- config: str,
60
+ config: str | None = None,
61
+ module_factory: Callable[[], Awaitable[Module]] | None = None,
62
+ loop_module_class: type[Module] | None = None,
60
63
  log_file: str | None = None,
61
64
  log_level: str = "info",
62
65
  syslog: bool = False,
@@ -65,18 +68,36 @@ class Application:
65
68
  ):
66
69
  """Initializes a pyobs application.
67
70
 
71
+ Exactly one of `config`/`module_factory` must be given.
72
+
68
73
  Args:
69
74
  config: Name of config file.
75
+ module_factory: Async factory that builds the Module once the event loop is already
76
+ running, instead of parsing a config file synchronously in __init__ -- lets the
77
+ factory `await` things (e.g. a login dialog's submit signal) before the module
78
+ (and its comm connection) exists at all. Mutually exclusive with `config`.
79
+ loop_module_class: Module class to pick `new_event_loop()` from when `module_factory`
80
+ is given (the caller already knows which module class it's about to build, e.g.
81
+ `GUI` in pyobs-gui's case). Required when `module_factory` is given, ignored
82
+ otherwise.
70
83
  log_file: Name of log file, if any.
71
84
  log_level: Logging level.
72
85
  syslog: Send log to systemd journal, tagged with SYSLOG_IDENTIFIER=pyobs
73
86
  and PYOBS_MODULE=<module name>. Requires logging-journald.
74
87
  influx_log: Log to influx DB.
75
88
  """
89
+ if (config is None) == (module_factory is None):
90
+ raise ValueError("Exactly one of 'config' or 'module_factory' must be given.")
91
+ if module_factory is not None and loop_module_class is None:
92
+ raise ValueError("loop_module_class is required when module_factory is given.")
76
93
 
77
94
  # get config name without path and extension
78
95
  self._config = config
79
- config_base = os.path.splitext(os.path.basename(config))[0]
96
+ config_base = (
97
+ os.path.splitext(os.path.basename(config))[0]
98
+ if config is not None
99
+ else loop_module_class.__name__.lower() # type: ignore[union-attr]
100
+ )
80
101
 
81
102
  # filter that injects %(pyobs_module)s into every LogRecord from the context var
82
103
  module_name_filter = ModuleNameFilter()
@@ -156,72 +177,100 @@ class Application:
156
177
 
157
178
  log = logging.getLogger(__name__)
158
179
 
159
- # set (maybe temporary) module name to filename without leading underscores
160
- module_name.set(Path(self._config).stem.lstrip("_"))
161
-
162
- # load config
163
- log.info("Loading configuration from %s...", self._config)
164
- with StringIO(pre_process_yaml(self._config)) as f:
165
- cfg: dict[str, Any] = yaml.safe_load(f)
166
-
167
- # get module class
168
- class_name = cfg["class"]
169
- klass = get_class_from_string(class_name)
170
-
171
- # create event loop — if top-level class doesn't override new_event_loop,
172
- # check child modules for one that does (e.g. pyobs_gui.GUI in a MultiModule)
173
- loop_class = klass
174
- if klass.new_event_loop is Module.new_event_loop:
175
- for mod_cfg in cfg.get("modules", {}).values():
176
- if isinstance(mod_cfg, dict) and "class" in mod_cfg:
177
- child_klass = get_class_from_string(mod_cfg["class"])
178
- if child_klass.new_event_loop is not Module.new_event_loop:
179
- loop_class = child_klass
180
- break
181
- self._loop = loop_class.new_event_loop()
182
- asyncio.set_event_loop(self._loop)
183
-
184
- # create module and open it
185
- config_stem = Path(self._config).stem.lstrip("_")
186
- if "comm" not in cfg and not issubclass(klass, MultiModule):
187
- # no comm configured -- module would otherwise fall back to a DummyComm named
188
- # with the fixed placeholder "module", indistinguishable from every other
189
- # comm-less module in PYOBS_MODULE log tagging (see
190
- # specs/steering/finding-module-logs-under-pyobsd.md) and guaranteed to trip the
191
- # stem-mismatch check below. Give it the config file's own stem instead.
192
- cfg = {**cfg, "comm": {"class": f"{DummyComm.__module__}.{DummyComm.__name__}", "name": config_stem}}
193
- log.info("Creating module from class %s...", klass.__name__)
194
- self._module = get_object(cfg, Module)
195
-
196
- # a config file whose stem doesn't match its own module's name silently splits
197
- # PYOBS_MODULE log tagging in two: logging that falls through both execute()
198
- # (module.py) and BackgroundTask (background_task.py) stays on the filename set
199
- # above, while logging covered by either of those uses the module's real
200
- # Comm-derived name -- refuse to start rather than let that drift run undetected.
201
- # MultiModule is exempt: its own .name is always the fixed placeholder "multi"
202
- # (module.py), never meant to match a filename -- each child module is already
203
- # correctly distinguished via execute()/BackgroundTask.
204
- if not isinstance(self._module, MultiModule):
205
- if self._module.name != config_stem:
206
- log.warning(
207
- "Config file stem (%s) does not match module's own name "
208
- "(%s). Rename the config file or fix the module's "
209
- "comm configuration so they match.",
210
- config_stem,
211
- self._module.name,
212
- )
180
+ # set (maybe temporary) module name -- filename without leading underscores for the
181
+ # config path, or a placeholder derived from loop_module_class for the factory path
182
+ # (there's no real name yet until the factory resolves the actual module/comm identity)
183
+ module_name.set(Path(self._config).stem.lstrip("_") if self._config is not None else config_base)
184
+
185
+ self._module: Module | None = None
186
+ self._module_factory = module_factory
187
+
188
+ if config is not None:
189
+ # load config
190
+ log.info("Loading configuration from %s...", config)
191
+ with StringIO(pre_process_yaml(config)) as f:
192
+ cfg: dict[str, Any] = yaml.safe_load(f)
193
+
194
+ # get module class
195
+ class_name = cfg["class"]
196
+ klass = get_class_from_string(class_name)
197
+
198
+ # create event loop — if top-level class doesn't override new_event_loop,
199
+ # check child modules for one that does (e.g. pyobs_gui.GUI in a MultiModule)
200
+ loop_class = klass
201
+ if klass.new_event_loop is Module.new_event_loop:
202
+ for mod_cfg in cfg.get("modules", {}).values():
203
+ if isinstance(mod_cfg, dict) and "class" in mod_cfg:
204
+ child_klass = get_class_from_string(mod_cfg["class"])
205
+ if child_klass.new_event_loop is not Module.new_event_loop:
206
+ loop_class = child_klass
207
+ break
208
+ self._loop = loop_class.new_event_loop()
209
+ asyncio.set_event_loop(self._loop)
210
+
211
+ # create module and open it
212
+ config_stem = Path(config).stem.lstrip("_")
213
+ if "comm" not in cfg and not issubclass(klass, MultiModule):
214
+ # no comm configured -- module would otherwise fall back to a DummyComm named
215
+ # with the fixed placeholder "module", indistinguishable from every other
216
+ # comm-less module in PYOBS_MODULE log tagging (see
217
+ # specs/steering/finding-module-logs-under-pyobsd.md) and guaranteed to trip the
218
+ # stem-mismatch check below. Give it the config file's own stem instead.
219
+ cfg = {**cfg, "comm": {"class": f"{DummyComm.__module__}.{DummyComm.__name__}", "name": config_stem}}
220
+ log.info("Creating module from class %s...", klass.__name__)
221
+ self._module = get_object(cfg, Module)
222
+
223
+ # a config file whose stem doesn't match its own module's name silently splits
224
+ # PYOBS_MODULE log tagging in two: logging that falls through both execute()
225
+ # (module.py) and BackgroundTask (background_task.py) stays on the filename set
226
+ # above, while logging covered by either of those uses the module's real
227
+ # Comm-derived name -- refuse to start rather than let that drift run undetected.
228
+ # MultiModule is exempt: its own .name is always the fixed placeholder "multi"
229
+ # (module.py), never meant to match a filename -- each child module is already
230
+ # correctly distinguished via execute()/BackgroundTask.
231
+ if not isinstance(self._module, MultiModule):
232
+ if self._module.name != config_stem:
233
+ log.warning(
234
+ "Config file stem (%s) does not match module's own name "
235
+ "(%s). Rename the config file or fix the module's "
236
+ "comm configuration so they match.",
237
+ config_stem,
238
+ self._module.name,
239
+ )
240
+ else:
241
+ # module_factory path: pick the event loop from the class the caller already knows
242
+ # it's about to build -- the module itself gets built later, inside the running
243
+ # loop (see _main()), since only there can the factory await anything.
244
+ assert loop_module_class is not None # validated above
245
+ self._loop = loop_module_class.new_event_loop()
246
+ asyncio.set_event_loop(self._loop)
213
247
 
214
248
  def run(self) -> None:
215
249
  """Run app."""
216
250
 
251
+ # run main task forever -- created before the signal handlers are registered below, so
252
+ # a signal arriving the instant it's registered can never find self._main_task unset
253
+ self._main_task = self._loop.create_task(self._main())
254
+
217
255
  # signals
218
256
  for sig in (signal.SIGTERM, signal.SIGINT):
219
257
  self._loop.add_signal_handler(sig, self._signal_handler, sig)
220
258
 
221
- # run main task forever
222
- main = self._loop.create_task(self._main())
223
259
  self._loop.run_forever()
224
- self._loop.run_until_complete(main)
260
+ try:
261
+ self._loop.run_until_complete(self._main_task)
262
+ except asyncio.CancelledError:
263
+ # _signal_handler() cancelled _main_task directly -- only reachable while
264
+ # module_factory hadn't resolved yet (e.g. a login dialog still open), since
265
+ # self._module.quit() is what normally unblocks run_forever()/main otherwise.
266
+ pass
267
+ except RuntimeError:
268
+ # Qt-backed loops (qasync, used by pyobs-gui) can report the underlying Qt event
269
+ # loop as stopped before _main_task is done -- e.g. when the GUI's native window
270
+ # close triggers Qt's own quit independently of self._module.quit(). _main_task
271
+ # keeps running as a plain asyncio task regardless; the cancel/gather below still
272
+ # drains it, so this is a benign race in loop bookkeeping, not a failed shutdown.
273
+ log.warning("Event loop reported as stopped before shutdown finished; continuing cleanup.")
225
274
 
226
275
  # main finished, cancel all tasks
227
276
  tasks = asyncio.all_tasks(self._loop)
@@ -229,7 +278,11 @@ class Application:
229
278
  log.debug("Task %s still running, cancelling it...", t)
230
279
  t.cancel()
231
280
  group = asyncio.gather(*tasks, return_exceptions=True)
232
- self._loop.run_until_complete(group)
281
+ try:
282
+ self._loop.run_until_complete(group)
283
+ except RuntimeError:
284
+ # same benign qasync race as above, now on the cancellation gather itself.
285
+ log.warning("Event loop reported as stopped before task cancellation finished.")
233
286
 
234
287
  # finished
235
288
  log.info("Closing loop...")
@@ -238,7 +291,14 @@ class Application:
238
291
  def _signal_handler(self, sig: int) -> None:
239
292
  """React to signals and quit the module."""
240
293
 
241
- self._module.quit()
294
+ if self._module is not None:
295
+ self._module.quit()
296
+ else:
297
+ # module_factory hasn't resolved yet (e.g. a login dialog still open) -- nothing to
298
+ # quit, so cancel the pending factory wait directly instead. Module.quit() would
299
+ # otherwise have called loop.stop() itself (module.py); mirror that here too.
300
+ self._main_task.cancel()
301
+ self._loop.stop()
242
302
 
243
303
  # reset signal handlers
244
304
  log.info("Got signal: %s, shutting down.", sig)
@@ -251,6 +311,15 @@ class Application:
251
311
 
252
312
  # everything in a try/except/finally, so that we can shut down gracefully
253
313
  try:
314
+ # resolve the module from the factory, if we're in that mode and haven't yet --
315
+ # runs inside the running loop, so the factory can await anything it needs to
316
+ # (e.g. a login dialog's submit signal) before the module/comm connection exists
317
+ if self._module is None:
318
+ assert self._module_factory is not None # validated in __init__
319
+ log.info("Waiting for module factory to resolve...")
320
+ self._module = await self._module_factory()
321
+ log.info("Module factory resolved.")
322
+
254
323
  # open module
255
324
  log.info("Opening module...")
256
325
  await self._module.startup()
@@ -0,0 +1,3 @@
1
+ from .xmppcomm import XmppComm, is_valid_jid
2
+
3
+ __all__ = ["XmppComm", "is_valid_jid"]
@@ -48,6 +48,26 @@ def _retry_delay(attempt: int, cap: float = 30.0, base: float = 1.0) -> float:
48
48
  return random.uniform(0, min(cap, base * (2**attempt)))
49
49
 
50
50
 
51
+ # anchored at both ends -- re.match alone doesn't anchor the end, so e.g. "user@domain/res/extra"
52
+ # would otherwise still "match" as a valid prefix
53
+ _JID_RE = re.compile(r"([\w_\-\.]+)@([\w_\-\.]+)/([\w_\-\.]+)$")
54
+
55
+
56
+ def is_valid_jid(jid: str, resource: str = "pyobs") -> bool:
57
+ """Whether jid is a valid user@domain or user@domain/resource JID -- exactly what
58
+ XmppComm.__init__ requires, so callers taking raw user input (e.g. a login window) can
59
+ validate it up front instead of finding out via a raised, less specific exception.
60
+
61
+ Args:
62
+ jid: JID to check, with or without a resource part.
63
+ resource: Resource to assume if jid doesn't already include one -- must match whatever
64
+ XmppComm itself would be constructed with, since a resource-less jid is only valid
65
+ if *some* resource ends up attached to it.
66
+ """
67
+ candidate = jid if "/" in jid else f"{jid}/{resource}"
68
+ return bool(_JID_RE.match(candidate))
69
+
70
+
51
71
  class EventStanza(ElementBase):
52
72
  name = "event"
53
73
  namespace = "pyobs:event"
@@ -180,10 +200,10 @@ class XmppComm(Comm):
180
200
  jid += "/" + resource
181
201
 
182
202
  # get user/domain/resource and write it back to config
183
- m = re.match(r"([\w_\-\.]+)@([\w_\-\.]+)\/([\w_\-\.]+)", jid)
203
+ m = _JID_RE.match(jid)
184
204
  if not m:
185
- log.error("Invalid JID format.")
186
- raise ValueError()
205
+ log.error("Invalid JID format: %r (expected user@domain or user@domain/resource).", jid)
206
+ raise ValueError(f"Invalid JID format: {jid!r} (expected user@domain or user@domain/resource).")
187
207
  self._user = m.group(1)
188
208
  self._domain = m.group(2)
189
209
  self._resource = m.group(3)
@@ -1161,4 +1181,4 @@ class XmppComm(Comm):
1161
1181
  pass # already gone server-side
1162
1182
 
1163
1183
 
1164
- __all__ = ["XmppComm"]
1184
+ __all__ = ["XmppComm", "is_valid_jid"]
@@ -383,11 +383,14 @@ class Module(Object, IModule, IConfig):
383
383
  """
384
384
  await self.open()
385
385
 
386
- # warn if any stateful interface hasn't published state yet
386
+ # a stateful interface with no published state by now isn't a transient, self-healing
387
+ # condition -- it's a standing defect (missing placeholder publish in open()) that sits
388
+ # there on every single startup until fixed, so this is an error, not a warning, even
389
+ # though it doesn't block startup itself (see specs/plans/enforce-state-publishing.md)
387
390
  if self._comm is not None:
388
391
  missing = self._comm.missing_published_state(self._interfaces)
389
392
  for iface in missing:
390
- log.warning(
393
+ log.error(
391
394
  "Module %s implements %s which declares state, but no state has been published for it yet.",
392
395
  self.name,
393
396
  iface.__name__,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyobs-core"
3
- version = "2.0.0.dev47"
3
+ version = "2.0.0.dev49"
4
4
  description = "robotic telescope software"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
6
  requires-python = ">=3.11"
@@ -27,7 +27,7 @@ dependencies = [
27
27
  "influxdb-client>=1.49.0",
28
28
  "pydantic>=2.12.5",
29
29
  "astropydantic>=0.0.5",
30
- "pillow>=11.3.0",
30
+ "pillow>=12.3.0",
31
31
  "tenacity>=9.1.4",
32
32
  "psutil>=7.2.2",
33
33
  "logging-journald>=0.6.11",
@@ -1,3 +0,0 @@
1
- from .xmppcomm import XmppComm
2
-
3
- __all__ = ["XmppComm"]