pyobs-core 2.0.0.dev85__tar.gz → 2.0.0.dev87__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 (449) hide show
  1. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/PKG-INFO +1 -1
  2. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xmppcomm.py +31 -2
  3. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/fitsheader.py +9 -0
  4. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/backend/observationarchive.py +34 -14
  5. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/backend/taskarchive.py +33 -11
  6. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/task.py +1 -0
  7. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/archive/archive.py +2 -0
  8. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/archive/local_archive.py +9 -2
  9. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/archive/pyobs_archive.py +7 -2
  10. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/http.py +16 -2
  11. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyproject.toml +1 -1
  12. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/.gitignore +0 -0
  13. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/LICENSE +0 -0
  14. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/__init__.py +0 -0
  15. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/application.py +0 -0
  16. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/background_task.py +0 -0
  17. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/cli/__init__.py +0 -0
  18. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/cli/_cli.py +0 -0
  19. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/cli/pyobs.py +0 -0
  20. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/cli/pyobsd.py +0 -0
  21. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/cli/pyobsw.py +0 -0
  22. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/__init__.py +0 -0
  23. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/comm.py +0 -0
  24. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/commlogging.py +0 -0
  25. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/dummy/__init__.py +0 -0
  26. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/dummy/dummycomm.py +0 -0
  27. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/local/__init__.py +0 -0
  28. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/local/localcomm.py +0 -0
  29. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/local/localnetwork.py +0 -0
  30. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/proxy.py +0 -0
  31. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/__init__.py +0 -0
  32. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/rpc.py +0 -0
  33. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/serializer.py +0 -0
  34. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
  35. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
  36. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
  37. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
  38. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +0 -0
  39. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
  40. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
  41. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/comm/xmpp/xmppclient.py +0 -0
  42. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/__init__.py +0 -0
  43. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/badweather.py +0 -0
  44. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/event.py +0 -0
  45. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/exposurestatuschanged.py +0 -0
  46. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/filterchanged.py +0 -0
  47. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/focusfound.py +0 -0
  48. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/goodweather.py +0 -0
  49. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/log.py +0 -0
  50. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/modechanged.py +0 -0
  51. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/moduleclosed.py +0 -0
  52. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/moduleopened.py +0 -0
  53. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/motionstatuschanged.py +0 -0
  54. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/move.py +0 -0
  55. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/newimage.py +0 -0
  56. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/newspectrum.py +0 -0
  57. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/offsets.py +0 -0
  58. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/roofclosing.py +0 -0
  59. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/roofopened.py +0 -0
  60. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/taskfailed.py +0 -0
  61. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/taskfinished.py +0 -0
  62. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/taskstarted.py +0 -0
  63. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/events/testevent.py +0 -0
  64. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/__init__.py +0 -0
  65. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/image.py +0 -0
  66. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/__init__.py +0 -0
  67. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/altazoffsets.py +0 -0
  68. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/exptime.py +0 -0
  69. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/genericoffset.py +0 -0
  70. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/onskydistance.py +0 -0
  71. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/pixeloffsets.py +0 -0
  72. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/radecoffsets.py +0 -0
  73. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/meta/skyoffsets.py +0 -0
  74. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processor.py +0 -0
  75. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/__init__.py +0 -0
  76. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/_daobackgroundremover.py +0 -0
  77. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/annotation/__init__.py +0 -0
  78. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/annotation/_pillow.py +0 -0
  79. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/annotation/circle.py +0 -0
  80. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/annotation/crosshair.py +0 -0
  81. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/annotation/text.py +0 -0
  82. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/astrometry/__init__.py +0 -0
  83. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
  84. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
  85. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
  86. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
  87. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/astrometry/astrometry.py +0 -0
  88. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/astrometry/dotnet.py +0 -0
  89. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/calibration/__init__.py +0 -0
  90. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
  91. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
  92. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/calibration/calibration.py +0 -0
  93. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/detection/__init__.py +0 -0
  94. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
  95. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/detection/_source_catalog.py +0 -0
  96. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/detection/daophot.py +0 -0
  97. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/detection/pysep.py +0 -0
  98. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/detection/simpledisk.py +0 -0
  99. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/detection/sourcedetection.py +0 -0
  100. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/exptime/__init__.py +0 -0
  101. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/exptime/exptime.py +0 -0
  102. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/exptime/star.py +0 -0
  103. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/__init__.py +0 -0
  104. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/addfitsheaders.py +0 -0
  105. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/download.py +0 -0
  106. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/flip.py +0 -0
  107. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/grayscale.py +0 -0
  108. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/httpserver.py +0 -0
  109. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/normalize.py +0 -0
  110. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/save.py +0 -0
  111. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/saveimage.py +0 -0
  112. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/smooth.py +0 -0
  113. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/image/softbin.py +0 -0
  114. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/misc/__init__.py +0 -0
  115. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/misc/addmask.py +0 -0
  116. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
  117. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/misc/circularmask.py +0 -0
  118. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/misc/createfilename.py +0 -0
  119. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
  120. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/misc/removebackground.py +0 -0
  121. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/modules/__init__.py +0 -0
  122. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
  123. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/__init__.py +0 -0
  124. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/astrometry.py +0 -0
  125. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/brighteststar.py +0 -0
  126. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
  127. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
  128. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
  129. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/fitsheader.py +0 -0
  130. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/offsets.py +0 -0
  131. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
  132. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/projected.py +0 -0
  133. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/offsets/spilled_light.py +0 -0
  134. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/__init__.py +0 -0
  135. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
  136. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
  137. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
  138. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
  139. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/photometry.py +0 -0
  140. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/photutil.py +0 -0
  141. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/photometry/pysep.py +0 -0
  142. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/wcs/__init__.py +0 -0
  143. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
  144. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IAbortable.py +0 -0
  145. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IAcquisition.py +0 -0
  146. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IAutoFocus.py +0 -0
  147. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IAutoGuiding.py +0 -0
  148. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IAutonomous.py +0 -0
  149. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IBinning.py +0 -0
  150. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ICalibrate.py +0 -0
  151. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ICamera.py +0 -0
  152. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IConfig.py +0 -0
  153. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ICooling.py +0 -0
  154. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IData.py +0 -0
  155. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IDataSequence.py +0 -0
  156. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IDome.py +0 -0
  157. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IExposure.py +0 -0
  158. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IExposureTime.py +0 -0
  159. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IFilters.py +0 -0
  160. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
  161. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
  162. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IFlatField.py +0 -0
  163. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IFocusModel.py +0 -0
  164. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IFocuser.py +0 -0
  165. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IGain.py +0 -0
  166. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IImageFormat.py +0 -0
  167. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IImageType.py +0 -0
  168. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IMode.py +0 -0
  169. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IModule.py +0 -0
  170. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IMotion.py +0 -0
  171. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IMultiFiber.py +0 -0
  172. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IOffsetsAltAz.py +0 -0
  173. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IOffsetsRaDec.py +0 -0
  174. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingAltAz.py +0 -0
  175. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingBody.py +0 -0
  176. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingHeliocentricPolar.py +0 -0
  177. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingHeliographicStonyhurst.py +0 -0
  178. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingHelioprojective.py +0 -0
  179. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingOrbitalElements.py +0 -0
  180. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingRaDec.py +0 -0
  181. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IPointingSeries.py +0 -0
  182. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IReady.py +0 -0
  183. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IRoof.py +0 -0
  184. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IRotation.py +0 -0
  185. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IRunnable.py +0 -0
  186. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IRunning.py +0 -0
  187. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IScriptRunner.py +0 -0
  188. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ISpectrograph.py +0 -0
  189. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IStartStop.py +0 -0
  190. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IStructuredConfig.py +0 -0
  191. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ISyncTarget.py +0 -0
  192. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ITelescope.py +0 -0
  193. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ITemperatures.py +0 -0
  194. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ITrackingMode.py +0 -0
  195. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/ITrackingRate.py +0 -0
  196. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IVideo.py +0 -0
  197. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IWeather.py +0 -0
  198. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/IWindow.py +0 -0
  199. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/__init__.py +0 -0
  200. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/interfaces/interface.py +0 -0
  201. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/__init__.py +0 -0
  202. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/camerasettings.py +0 -0
  203. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/fitsnamespace.py +0 -0
  204. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/follow.py +0 -0
  205. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/motionstatus.py +0 -0
  206. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/pipeline.py +0 -0
  207. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/waitformotion.py +0 -0
  208. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/mixins/weatheraware.py +0 -0
  209. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/__init__.py +0 -0
  210. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/__init__.py +0 -0
  211. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/adaptive.py +0 -0
  212. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/basecamera.py +0 -0
  213. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/basespectrograph.py +0 -0
  214. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/basevideo.py +0 -0
  215. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/dummycamera.py +0 -0
  216. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/dummyspectrograph.py +0 -0
  217. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/dummyvideo.py +0 -0
  218. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/camera/pipelinecamera.py +0 -0
  219. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/flatfield/__init__.py +0 -0
  220. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/flatfield/flatfield.py +0 -0
  221. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/flatfield/pointing.py +0 -0
  222. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/flatfield/scheduler.py +0 -0
  223. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/focus/__init__.py +0 -0
  224. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/focus/dummyautofocus.py +0 -0
  225. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/focus/focusmodel.py +0 -0
  226. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/focus/focusseries.py +0 -0
  227. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/image/__init__.py +0 -0
  228. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/image/imagewatcher.py +0 -0
  229. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/image/imagewriter.py +0 -0
  230. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/image/pipeline.py +0 -0
  231. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/image/seeing.py +0 -0
  232. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/module.py +0 -0
  233. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/__init__.py +0 -0
  234. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/_base.py +0 -0
  235. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/_baseguiding.py +0 -0
  236. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/acquisition.py +0 -0
  237. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/autoguiding.py +0 -0
  238. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/dummyacquisition.py +0 -0
  239. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/dummyguiding.py +0 -0
  240. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
  241. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
  242. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
  243. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
  244. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
  245. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/pointing/scienceframeguiding.py +0 -0
  246. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/robotic/__init__.py +0 -0
  247. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/robotic/mastermind.py +0 -0
  248. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/robotic/pointing.py +0 -0
  249. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/robotic/scheduler.py +0 -0
  250. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/robotic/scriptrunner.py +0 -0
  251. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/roof/__init__.py +0 -0
  252. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/roof/basedome.py +0 -0
  253. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/roof/baseroof.py +0 -0
  254. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/roof/dummyroof.py +0 -0
  255. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/telescope/__init__.py +0 -0
  256. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/telescope/_dummytelescopebase.py +0 -0
  257. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/telescope/basetelescope.py +0 -0
  258. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/telescope/dummyaltaztelescope.py +0 -0
  259. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/telescope/dummyradectelescope.py +0 -0
  260. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/telescope/dummysolartelescope.py +0 -0
  261. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/test/__init__.py +0 -0
  262. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/test/standalone.py +0 -0
  263. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/__init__.py +0 -0
  264. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/dummymode.py +0 -0
  265. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/fluentlogger.py +0 -0
  266. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/httpfilecache.py +0 -0
  267. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/kiosk.py +0 -0
  268. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/matrix.py +0 -0
  269. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/stellarium.py +0 -0
  270. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/telegram.py +0 -0
  271. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/utils/trigger.py +0 -0
  272. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/weather/__init__.py +0 -0
  273. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/weather/mockweather.py +0 -0
  274. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/weather/weather.py +0 -0
  275. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/weather/weather_api.py +0 -0
  276. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/modules/weather/weather_state.py +0 -0
  277. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/object.py +0 -0
  278. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/py.typed +0 -0
  279. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/__init__.py +0 -0
  280. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/observation.py +0 -0
  281. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/__init__.py +0 -0
  282. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/_executor.py +0 -0
  283. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
  284. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
  285. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
  286. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
  287. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
  288. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
  289. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
  290. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
  291. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/dataprovider.py +0 -0
  292. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
  293. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
  294. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
  295. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/constant.py +0 -0
  296. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/follow.py +0 -0
  297. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/interval.py +0 -0
  298. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/merit.py +0 -0
  299. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
  300. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/random.py +0 -0
  301. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
  302. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/merits/transit.py +0 -0
  303. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
  304. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
  305. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
  306. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
  307. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/heliocentricpolartarget.py +0 -0
  308. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/helioprojectivetarget.py +0 -0
  309. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
  310. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
  311. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
  312. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
  313. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/targets/target.py +0 -0
  314. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
  315. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/__init__.py +0 -0
  316. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
  317. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
  318. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
  319. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
  320. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/control/__init__.py +0 -0
  321. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/control/cases.py +0 -0
  322. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/control/conditional.py +0 -0
  323. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/control/parallel.py +0 -0
  324. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/control/selector.py +0 -0
  325. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/control/sequential.py +0 -0
  326. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/exceptions.py +0 -0
  327. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
  328. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
  329. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
  330. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
  331. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/script.py +0 -0
  332. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/utils/__init__.py +0 -0
  333. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/utils/callmodule.py +0 -0
  334. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
  335. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/scripts/utils/log.py +0 -0
  336. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/__init__.py +0 -0
  337. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/backend/__init__.py +0 -0
  338. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
  339. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
  340. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
  341. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/__init__.py +0 -0
  342. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/_portal.py +0 -0
  343. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
  344. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
  345. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/configdb.py +0 -0
  346. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
  347. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
  348. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
  349. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
  350. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
  351. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
  352. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/task.py +0 -0
  353. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
  354. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
  355. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/memory/__init__.py +0 -0
  356. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
  357. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
  358. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/observationarchive.py +0 -0
  359. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/storage/taskarchive.py +0 -0
  360. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/taskrunner.py +0 -0
  361. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/__init__.py +0 -0
  362. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/archive/__init__.py +0 -0
  363. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/exptime/__init__.py +0 -0
  364. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/exptime/exptime.py +0 -0
  365. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
  366. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
  367. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
  368. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/flatfielder.py +0 -0
  369. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
  370. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
  371. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
  372. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
  373. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
  374. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
  375. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
  376. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
  377. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/__init__.py +0 -0
  378. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/average.py +0 -0
  379. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/cache.py +0 -0
  380. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/config.py +0 -0
  381. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/config_schema.py +0 -0
  382. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/coordinates.py +0 -0
  383. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/curvefit.py +0 -0
  384. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/enums.py +0 -0
  385. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/exceptions.py +0 -0
  386. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/fits.py +0 -0
  387. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/focusseries/__init__.py +0 -0
  388. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/focusseries/base.py +0 -0
  389. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/focusseries/photometry.py +0 -0
  390. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/focusseries/projection.py +0 -0
  391. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/grids/__init__.py +0 -0
  392. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/grids/filters.py +0 -0
  393. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/grids/grid.py +0 -0
  394. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/grids/gridnode.py +0 -0
  395. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/grids/pipeline.py +0 -0
  396. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/__init__.py +0 -0
  397. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/__init__.py +0 -0
  398. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/binningwidget.py +0 -0
  399. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
  400. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/exposewidget.py +0 -0
  401. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
  402. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
  403. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
  404. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
  405. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/influxdb.py +0 -0
  406. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/logger.py +0 -0
  407. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/logging/__init__.py +0 -0
  408. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/logging/context.py +0 -0
  409. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/logging/resolvableerror.py +0 -0
  410. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/modulegui.py +0 -0
  411. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/offsets/__init__.py +0 -0
  412. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
  413. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/offsets/applyoffsets.py +0 -0
  414. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
  415. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/parallel.py +0 -0
  416. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/pipeline/__init__.py +0 -0
  417. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/pipeline/pipeline.py +0 -0
  418. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/pipeline/progress.py +0 -0
  419. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/pipeline/reduction.py +0 -0
  420. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/pipeline/reduction_base.py +0 -0
  421. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/publisher/__init__.py +0 -0
  422. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/publisher/csv.py +0 -0
  423. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/publisher/http.py +0 -0
  424. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/publisher/log.py +0 -0
  425. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/publisher/multi.py +0 -0
  426. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/publisher/publisher.py +0 -0
  427. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/serialization.py +0 -0
  428. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/shellcommand.py +0 -0
  429. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/threads/__init__.py +0 -0
  430. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/threads/lockwithabort.py +0 -0
  431. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/time.py +0 -0
  432. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/units.py +0 -0
  433. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/utils/versions.py +0 -0
  434. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/version.py +0 -0
  435. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/__init__.py +0 -0
  436. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/archivefile.py +0 -0
  437. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/bufferedfile.py +0 -0
  438. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/file.py +0 -0
  439. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/filelists/__init__.py +0 -0
  440. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/filelists/filelist.py +0 -0
  441. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/filelists/testing.py +0 -0
  442. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/httpfile.py +0 -0
  443. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/localfile.py +0 -0
  444. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/memfile.py +0 -0
  445. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/sftpfile.py +0 -0
  446. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/smbfile.py +0 -0
  447. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/sshfile.py +0 -0
  448. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/tempfile.py +0 -0
  449. {pyobs_core-2.0.0.dev85 → pyobs_core-2.0.0.dev87}/pyobs/vfs/vfs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyobs-core
3
- Version: 2.0.0.dev85
3
+ Version: 2.0.0.dev87
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -60,6 +60,21 @@ def _retry_delay(attempt: int, cap: float = 30.0, base: float = 1.0) -> float:
60
60
  return random.uniform(0, min(cap, base * (2**attempt)))
61
61
 
62
62
 
63
+ def _log_task_exception(task: asyncio.Task[Any]) -> None:
64
+ """Retrieve and log a background task's exception, if it failed.
65
+
66
+ Results of asyncio.create_task() that are never awaited or callback-ed have their
67
+ exceptions reported by the event loop's default handler as "Task exception was never
68
+ retrieved" -- a noisy ERROR traceback that carries no context. Retrieving the exception
69
+ here turns that into a normal log line with the task's failure attached.
70
+ """
71
+ if task.cancelled():
72
+ return
73
+ exc = task.exception()
74
+ if exc is not None:
75
+ log.error("Unhandled exception in XMPP event handler task", exc_info=exc)
76
+
77
+
63
78
  # anchored at both ends -- re.match alone doesn't anchor the end, so e.g. "user@domain/res/extra"
64
79
  # would otherwise still "match" as a valid prefix
65
80
  _JID_RE = re.compile(r"([\w_\-\.]+)@([\w_\-\.]+)/([\w_\-\.]+)$")
@@ -979,7 +994,12 @@ class XmppComm(Comm):
979
994
  for callback in callbacks:
980
995
  callback(state_obj)
981
996
  else:
982
- asyncio.create_task(self._handle_event(msg, node))
997
+ # Non-state notifications are handled asynchronously. Attach a done-callback so the
998
+ # task's exception is retrieved: without it, a failure inside _handle_event is never
999
+ # retrieved and asyncio reports it as "Task exception was never retrieved" (see
1000
+ # _handle_event for the payload-less notification case that used to trigger this).
1001
+ task = asyncio.create_task(self._handle_event(msg, node))
1002
+ task.add_done_callback(_log_task_exception)
983
1003
 
984
1004
  async def _handle_event(self, msg: Any, node: str) -> None:
985
1005
  """Handles an event.
@@ -994,7 +1014,16 @@ class XmppComm(Comm):
994
1014
  # get body, unescape it, parse it
995
1015
  # State-node messages are handled synchronously in _handle_event_sync
996
1016
  # before this async task runs. By the time we get here it's a pyobs event.
997
- body = json.loads(xml.sax.saxutils.unescape(msg["pubsub_event"]["items"]["item"]["payload"].text))
1017
+ # Not every notification carries an item with a payload: retract stanzas,
1018
+ # node purges, and nodes with deliver_payloads off all arrive payload-less.
1019
+ # The state-node path copes via _fetch_and_dispatch_state; for events there is
1020
+ # nothing to refetch, so just drop the notification.
1021
+ items = msg["pubsub_event"]["items"]
1022
+ item = items["item"] if items is not None else None
1023
+ payload = item["payload"] if item is not None else None
1024
+ if payload is None or payload.text is None:
1025
+ return
1026
+ body = json.loads(xml.sax.saxutils.unescape(payload.text))
998
1027
 
999
1028
  # do we have a <delay> element?
1000
1029
  delay = msg.xml.findall("{urn:sleekxmpp:delay}delay")
@@ -135,6 +135,15 @@ class FitsHeaderMixin:
135
135
  except exc.RemoteError as e:
136
136
  log.warning("Could not fetch FITS headers from %s: %s.", client, str(e))
137
137
  continue
138
+ except Exception as e:
139
+ # XmppComm.execute() only converts IqError/IqTimeout into RemoteError; anything
140
+ # else raised while making or deserializing the RPC call (e.g. a peer returning a
141
+ # malformed IFitsHeaderBefore/After response) escapes as a raw exception. That must
142
+ # not take down the whole exposure -- log and skip this peer's headers (#767).
143
+ log.warning(
144
+ "Could not fetch FITS headers from %s: unexpected %s: %s.", client, type(e).__name__, str(e)
145
+ )
146
+ continue
138
147
 
139
148
  # add them to fits file
140
149
  if headers:
@@ -60,23 +60,43 @@ class BackendObservationArchive(ObservationArchive):
60
60
 
61
61
  while True:
62
62
  try:
63
- last_update = await self.last_update_time()
64
- if self._last_update is None or self._last_update < last_update:
65
- self._observations = await self._get_schedule()
66
- sorted_obs = sorted(
67
- filter(lambda o: o.state == ObservationState.PENDING, self._observations),
68
- key=lambda o: o.start,
69
- )
70
- if len(sorted_obs) == 0:
71
- log.info("Downloaded new schedule.")
72
- else:
73
- obs = sorted_obs[0]
74
- log.info("Downloaded new schedule. Next observation is task %s at %s.", obs.task, obs.start)
75
- self._last_update = last_update
63
+ await self._update()
76
64
  except Exception as e:
77
65
  log.error("Failed to update observations from backend: %s", e)
78
66
  await asyncio.sleep(5)
79
67
 
68
+ async def _update(self) -> None:
69
+ """Fetch the schedule from the backend and apply it if anything changed.
70
+
71
+ Re-fetches unconditionally on every poll instead of gating on the backend's
72
+ ``last_observation_update`` marker (per-process Django ``LocMemCache``, unreliable across
73
+ gunicorn workers -- see ``BackendTaskArchive``). Changes are detected by comparing full
74
+ observation contents via ``model_dump(use_task_id=True)``, keyed by observation ID so a
75
+ stable reordering of the same items is not mistaken for a change: the backend serializes
76
+ ``task`` as a plain FK ID, while cached copies get their ``task`` replaced by a full
77
+ ``Task`` when the mastermind calls ``fetch_task()``, so the ID normalization keeps both
78
+ sides comparable. The comparison includes ``state`` (``Observation.__eq__`` ignores it),
79
+ which matters for in-set transitions such as ``pending`` -> ``in_progress``; observations
80
+ leaving the fetched set -- e.g. ``window_expired``, which the server-side ``state``/
81
+ ``end_after`` filters drop -- are picked up as list shrinkage by the unconditional
82
+ refetch.
83
+ """
84
+ observations = await self._get_schedule()
85
+ if {o.id: o.model_dump(use_task_id=True) for o in observations} != {
86
+ o.id: o.model_dump(use_task_id=True) for o in self._observations
87
+ }:
88
+ self._observations = observations
89
+ self._last_update = Time.now()
90
+ sorted_obs = sorted(
91
+ filter(lambda o: o.state == ObservationState.PENDING, self._observations),
92
+ key=lambda o: o.start,
93
+ )
94
+ if len(sorted_obs) == 0:
95
+ log.info("Downloaded new schedule.")
96
+ else:
97
+ obs = sorted_obs[0]
98
+ log.info("Downloaded new schedule. Next observation is task %s at %s.", obs.task, obs.start)
99
+
80
100
  async def last_update_time(self) -> Time:
81
101
  """Fetches last schedule update time."""
82
102
  res = await http_request_with_retries(self._session, urljoin(self._url, "/api/last_observation_update/"))
@@ -229,7 +249,7 @@ class BackendObservationArchive(ObservationArchive):
229
249
  params["end_before"] = end_before.isot
230
250
  if end_after is not None:
231
251
  params["end_after"] = end_after.isot
232
- observations = await http_request_paginated(self._session, url, params=params)
252
+ observations = await http_request_paginated(self._session, url, params=params, strict=True)
233
253
  return ObservationList([self.pyobs_model_validate(Observation, obs) for obs in observations])
234
254
 
235
255
 
@@ -56,18 +56,36 @@ class BackendTaskArchive(TaskArchive):
56
56
  """Update tasks in background."""
57
57
  while True:
58
58
  try:
59
- last_update = await self.last_update_time()
60
- if self._last_update is None or self._last_update < last_update:
61
- self._projects = await self._get_projects()
62
- self._tasks = await self._get_tasks()
63
- log.info("Downloaded new tasks/projects.")
64
- self._last_update = last_update
65
- if self._on_tasks_changed is not None:
66
- await self._on_tasks_changed()
59
+ await self._update()
67
60
  except Exception as e:
68
61
  log.error("Failed to update tasks from backend: %s", e)
69
62
  await asyncio.sleep(5)
70
63
 
64
+ async def _update(self) -> None:
65
+ """Fetch tasks/projects from the backend and apply them if anything changed.
66
+
67
+ Re-fetches unconditionally on every poll instead of gating on the backend's
68
+ ``last_task_update`` marker: that marker is computed from a per-process Django
69
+ ``LocMemCache`` and is unreliable across gunicorn workers, so gating on it let the
70
+ mastermind run stale tasks forever. Real changes are detected by comparing the downloaded
71
+ content against the cached copy. The comparison uses ``model_dump()`` rather than pydantic
72
+ ``==``, which also compares runtime attributes (e.g. ``Task._cant_run_reason`` set by
73
+ ``can_run()``) and would flag unchanged tasks as changed on every poll; it is keyed by ID
74
+ so that a stable reordering of the same items (e.g. an unordered backend queryset) is not
75
+ mistaken for a change.
76
+ """
77
+ projects = await self._get_projects()
78
+ tasks = await self._get_tasks()
79
+ if {p.code: p.model_dump() for p in projects} != {p.code: p.model_dump() for p in self._projects} or {
80
+ t.id: t.model_dump() for t in tasks
81
+ } != {t.id: t.model_dump() for t in self._tasks}:
82
+ self._projects = projects
83
+ self._tasks = tasks
84
+ self._last_update = Time.now()
85
+ log.info("Downloaded new tasks/projects.")
86
+ if self._on_tasks_changed is not None:
87
+ await self._on_tasks_changed()
88
+
71
89
  async def last_update_time(self) -> Time:
72
90
  """Fetches last schedule update time."""
73
91
  res = await http_request_with_retries(self._session, urljoin(self._url, "/api/last_task_update/"))
@@ -75,16 +93,20 @@ class BackendTaskArchive(TaskArchive):
75
93
 
76
94
  async def _get_projects(self) -> list[Project]:
77
95
  """Fetch projects from backend."""
78
- projects = await http_request_paginated(self._session, urljoin(self._url, "/api/projects/"))
96
+ projects = await http_request_paginated(self._session, urljoin(self._url, "/api/projects/"), strict=True)
79
97
  return [self.pyobs_model_validate(Project, project) for project in projects]
80
98
 
81
99
  async def _get_tasks(self) -> list[Task]:
82
100
  """Fetch tasks from backend."""
83
- tasks = await http_request_paginated(self._session, urljoin(self._url, "/api/tasks/"))
101
+ tasks = await http_request_paginated(self._session, urljoin(self._url, "/api/tasks/"), strict=True)
84
102
  return [self.pyobs_model_validate(Task, task) for task in tasks]
85
103
 
86
104
  async def last_changed(self) -> Time | None:
87
- """Returns time when last time any tasks changed."""
105
+ """Returns time when last time any tasks changed (as observed by this archive).
106
+
107
+ This is the local time at which the last content change was detected by the polling loop,
108
+ not the backend's marker timestamp -- the marker is per-process and unreliable.
109
+ """
88
110
  return self._last_update
89
111
 
90
112
  async def get_projects(self) -> list[Project]:
@@ -150,6 +150,7 @@ class Project(BaseModel):
150
150
  name: str = ""
151
151
  priority: float | None = Field(ge=0.0, le=9999.0, default=1.0)
152
152
  users: list[str] = Field(default_factory=list)
153
+ public: bool = False # ingest backend `public` flag; default keeps old backends valid
153
154
 
154
155
 
155
156
  __all__ = ["Task", "TaskData", "Project"]
@@ -42,6 +42,7 @@ class Archive(PolymorphicBaseModel, metaclass=ABCMeta):
42
42
  binning: str | None = None,
43
43
  filter_name: str | None = None,
44
44
  rlevel: int | None = None,
45
+ obsnum: str | None = None,
45
46
  ) -> dict[str, list[Any]]: ...
46
47
 
47
48
  @abstractmethod
@@ -57,6 +58,7 @@ class Archive(PolymorphicBaseModel, metaclass=ABCMeta):
57
58
  binning: str | None = None,
58
59
  filter_name: str | None = None,
59
60
  rlevel: int | None = None,
61
+ obsnum: str | None = None,
60
62
  ) -> list[FrameInfo]: ...
61
63
 
62
64
  @abstractmethod
@@ -48,6 +48,7 @@ class LocalArchive(Archive):
48
48
  "site",
49
49
  "telescope",
50
50
  "rlevel",
51
+ "obsnum",
51
52
  ]
52
53
  }
53
54
  for filename in filenames:
@@ -61,6 +62,7 @@ class LocalArchive(Archive):
61
62
  columns["site"].append(hdr["SITEID"] if "SITEID" in hdr else None)
62
63
  columns["telescope"].append(hdr["TELID"] if "TELID" in hdr else None)
63
64
  columns["rlevel"].append(hdr["RLEVEL"] if "RLEVEL" in hdr else None)
65
+ columns["obsnum"].append(str(hdr["OBSNUM"]) if "OBSNUM" in hdr else None)
64
66
  columns["filename"] = filenames
65
67
  self._data = pd.DataFrame(columns)
66
68
 
@@ -76,6 +78,7 @@ class LocalArchive(Archive):
76
78
  binning: str | None = None,
77
79
  filter_name: str | None = None,
78
80
  rlevel: int | None = None,
81
+ obsnum: str | None = None,
79
82
  ) -> pd.DataFrame:
80
83
  data = self._data
81
84
  if start is not None:
@@ -98,6 +101,8 @@ class LocalArchive(Archive):
98
101
  data = data[data["filter"] == filter_name]
99
102
  if rlevel is not None:
100
103
  data = data[data["rlevel"] == rlevel]
104
+ if obsnum is not None:
105
+ data = data[data["obsnum"] == obsnum]
101
106
  return data
102
107
 
103
108
  async def list_options(
@@ -112,9 +117,10 @@ class LocalArchive(Archive):
112
117
  binning: str | None = None,
113
118
  filter_name: str | None = None,
114
119
  rlevel: int | None = None,
120
+ obsnum: str | None = None,
115
121
  ) -> dict[str, list[Any]]:
116
122
  data = self._filter_data(
117
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
123
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
118
124
  )
119
125
  return {
120
126
  "binnings": list(data["binning"].unique()),
@@ -137,9 +143,10 @@ class LocalArchive(Archive):
137
143
  binning: str | None = None,
138
144
  filter_name: str | None = None,
139
145
  rlevel: int | None = None,
146
+ obsnum: str | None = None,
140
147
  ) -> list[FrameInfo]:
141
148
  data = self._filter_data(
142
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
149
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
143
150
  )
144
151
  infos: list[FrameInfo] = []
145
152
  for _, row in data.iterrows():
@@ -68,10 +68,11 @@ class PyobsArchive(Archive):
68
68
  binning: str | None = None,
69
69
  filter_name: str | None = None,
70
70
  rlevel: int | None = None,
71
+ obsnum: str | None = None,
71
72
  ) -> dict[str, list[Any]]:
72
73
  url = urllib.parse.urljoin(self.url, "frames/aggregate/")
73
74
  params = self._build_query(
74
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
75
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
75
76
  )
76
77
  async with aiohttp.ClientSession() as session:
77
78
  async with session.get(url, params=params, headers=self._headers, timeout=self._timeout) as response:
@@ -91,10 +92,11 @@ class PyobsArchive(Archive):
91
92
  binning: str | None = None,
92
93
  filter_name: str | None = None,
93
94
  rlevel: int | None = None,
95
+ obsnum: str | None = None,
94
96
  ) -> list[FrameInfo]:
95
97
  url = urllib.parse.urljoin(self.url, "frames/")
96
98
  params = self._build_query(
97
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
99
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
98
100
  )
99
101
  frames: list[FrameInfo] = []
100
102
  params["offset"] = 0
@@ -123,6 +125,7 @@ class PyobsArchive(Archive):
123
125
  binning: str | None = None,
124
126
  filter_name: str | None = None,
125
127
  rlevel: int | None = None,
128
+ obsnum: str | None = None,
126
129
  ) -> dict[str, Any]:
127
130
  params: dict[str, Any] = {}
128
131
  if start is not None:
@@ -145,6 +148,8 @@ class PyobsArchive(Archive):
145
148
  params["FILTER"] = filter_name
146
149
  if rlevel is not None:
147
150
  params["RLEVEL"] = rlevel
151
+ if obsnum is not None:
152
+ params["OBSNUM"] = obsnum
148
153
  return params
149
154
 
150
155
  async def download_frames(self, infos: list[FrameInfo]) -> list[Image]:
@@ -46,13 +46,20 @@ async def http_request_with_retries(
46
46
 
47
47
 
48
48
  async def http_request_paginated(
49
- session: aiohttp.ClientSession, url: str, method: str = "get", expected_status: int = 200, **kwargs: Any
49
+ session: aiohttp.ClientSession,
50
+ url: str,
51
+ method: str = "get",
52
+ expected_status: int = 200,
53
+ strict: bool = False,
54
+ **kwargs: Any,
50
55
  ) -> list[dict[str, Any]]:
51
56
  """Fetches all pages of a DRF-style paginated list endpoint and returns the combined results.
52
57
 
53
58
  If a page beyond the first becomes invalid mid-fetch (DRF's "Invalid page." 404, which can
54
59
  happen when concurrent writes shift page boundaries on a live-growing dataset), pagination
55
- stops early with whatever was already fetched instead of failing the whole request.
60
+ stops early with whatever was already fetched instead of failing the whole request. With
61
+ ``strict=True`` that truncation is raised as an error instead, so callers that treat the
62
+ result as authoritative (e.g. replacing a cached list) never apply a partial result silently.
56
63
  """
57
64
  results: list[dict[str, Any]] = []
58
65
  next_url: str | None = url
@@ -69,6 +76,13 @@ async def http_request_paginated(
69
76
  and isinstance(e.body, dict)
70
77
  and e.body.get("detail") == "Invalid page."
71
78
  ):
79
+ if strict:
80
+ log.warning(
81
+ "Pagination page became invalid mid-fetch, raising instead of returning a "
82
+ "truncated result of %d item(s).",
83
+ len(results),
84
+ )
85
+ raise
72
86
  log.warning("Pagination page became invalid mid-fetch, stopping early with %d result(s).", len(results))
73
87
  break
74
88
  raise
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyobs-core"
3
- version = "2.0.0.dev85"
3
+ version = "2.0.0.dev87"
4
4
  description = "robotic telescope software"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
6
  requires-python = ">=3.11"