pyobs-core 2.9.0__tar.gz → 2.9.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/PKG-INFO +1 -1
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/image/imagewatcher.py +149 -80
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/pushnotifier.py +8 -1
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyproject.toml +1 -1
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/.gitignore +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/LICENSE +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/application.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/background_task.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/cli/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/cli/_cli.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/cli/pyobs.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/cli/pyobsd.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/cli/pyobsw.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/comm.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/commlogging.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/dummy/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/dummy/dummycomm.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/local/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/local/localcomm.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/local/localnetwork.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/proxy.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/rpc.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/serializer.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xmppclient.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/comm/xmpp/xmppcomm.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/badweather.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/event.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/exposurestatuschanged.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/filterchanged.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/focusfound.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/goodweather.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/log.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/modechanged.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/moduleclosed.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/moduleopened.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/motionstatuschanged.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/move.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/newimage.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/newspectrum.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/offsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/roofclosing.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/roofopened.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/taskfailed.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/taskfinished.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/taskskipped.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/taskstarted.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/events/testevent.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/image.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/altazoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/exptime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/genericoffset.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/onskydistance.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/pixeloffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/radecoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/meta/skyoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processor.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/_daobackgroundremover.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/annotation/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/annotation/_pillow.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/annotation/circle.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/annotation/crosshair.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/annotation/text.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/astrometry.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/dotnet.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/calibration/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/calibration/calibration.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/_source_catalog.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/daophot.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/pysep.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/simpledisk.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/sourcedetection.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/exptime/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/exptime/exptime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/exptime/star.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/addfitsheaders.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/download.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/flip.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/grayscale.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/httpserver.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/normalize.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/save.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/saveimage.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/smooth.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/image/softbin.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/misc/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/misc/addmask.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/misc/circularmask.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/misc/createfilename.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/misc/removebackground.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/modules/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/astrometry.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/brighteststar.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/fitsheader.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/offsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/projected.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/spilled_light.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/photometry.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/photutil.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/photometry/pysep.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/wcs/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IAbortable.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IAcquisition.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IAutoFocus.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IAutoGuiding.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IAutonomous.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IBinning.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ICalibrate.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ICamera.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IConfig.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ICooling.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IData.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IDataSequence.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IDome.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IExposure.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IExposureTime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IFilters.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IFlatField.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IFocusModel.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IFocuser.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IGain.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IImageFormat.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IImageType.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IMode.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IModule.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IMotion.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IMultiFiber.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IOffsetsAltAz.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IOffsetsRaDec.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingAltAz.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingBody.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingHeliocentricPolar.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingHeliographicStonyhurst.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingHelioprojective.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingOrbitalElements.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingRaDec.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPointingSeries.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IPushNotifications.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IReady.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IRobotic.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IRoboticScheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IRoof.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IRotation.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IRunnable.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IRunning.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IScriptRunner.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ISpectrograph.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IStartStop.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IStructuredConfig.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ISyncTarget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ITelescope.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ITemperatures.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ITrackingMode.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/ITrackingRate.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IVideo.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IWeather.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/IWindow.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/interfaces/interface.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/camerasettings.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/fitsheader.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/fitsnamespace.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/follow.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/motionstatus.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/pipeline.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/waitformotion.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/mixins/weatheraware.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/adaptive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/basecamera.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/basespectrograph.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/basevideo.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/dummycamera.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/dummyspectrograph.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/dummyvideo.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/camera/pipelinecamera.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/flatfield/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/flatfield/flatfield.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/flatfield/pointing.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/flatfield/scheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/focus/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/focus/dummyautofocus.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/focus/focusmodel.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/focus/focusseries.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/image/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/image/imagewriter.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/image/pipeline.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/image/seeing.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/module.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/_base.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/_baseguiding.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/acquisition.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/autoguiding.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/dummyacquisition.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/dummyguiding.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/pointing/scienceframeguiding.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/robotic/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/robotic/dummymastermind.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/robotic/dummyscheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/robotic/mastermind.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/robotic/pointing.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/robotic/scheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/robotic/scriptrunner.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/roof/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/roof/basedome.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/roof/baseroof.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/roof/dummyroof.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/telescope/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/telescope/_dummytelescopebase.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/telescope/basetelescope.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/telescope/dummyaltaztelescope.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/telescope/dummyradectelescope.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/telescope/dummysolartelescope.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/test/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/test/standalone.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/dummymode.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/dummystructuredconfig.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/fluentlogger.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/httpfilecache.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/kiosk.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/matrix.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/stellarium.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/telegram.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/utils/trigger.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/weather/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/weather/mockweather.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/weather/weather.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/weather/weather_api.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/modules/weather/weather_state.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/object.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/py.typed +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/instruments.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/observation.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/_executor.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/dataprovider.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/constant.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/follow.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/interval.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/merit.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/random.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/merits/transit.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/heliocentricpolartarget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/helioprojectivetarget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/targets/target.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/control/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/control/cases.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/control/conditional.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/control/parallel.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/control/selector.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/control/sequential.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/exceptions.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/script.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/utils/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/utils/callmodule.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/scripts/utils/log.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/_portal.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/configdb.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/task.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/memory/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/observationarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/portal/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/portal/observationarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/portal/taskarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/storage/taskarchive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/task.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/taskrunner.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/archive/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/archive/archive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/archive/local_archive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/archive/pyobs_archive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/calibration.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/exptime/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/exptime/exptime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/flatfielder.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/average.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/cache.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/config.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/config_schema.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/coordinates.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/curvefit.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/enums.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/exceptions.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/exptime_grouping.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/fits.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/focusseries/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/focusseries/base.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/focusseries/photometry.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/focusseries/projection.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/grids/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/grids/filters.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/grids/grid.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/grids/gridnode.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/grids/pipeline.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/binningwidget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/exposewidget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/http.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/influxdb.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/logger.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/logging/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/logging/context.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/logging/resolvableerror.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/modulegui.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/offsets/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/offsets/applyoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/parallel.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/pipeline/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/pipeline/pipeline.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/pipeline/progress.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/pipeline/reduction.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/pipeline/reduction_base.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/publisher/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/publisher/csv.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/publisher/http.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/publisher/log.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/publisher/multi.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/publisher/publisher.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/serialization.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/shellcommand.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/threads/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/threads/lockwithabort.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/time.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/units.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/utils/versions.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/version.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/archivefile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/bufferedfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/file.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/filelists/__init__.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/filelists/filelist.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/filelists/testing.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/httpfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/localfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/memfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/sftpfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/smbfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/sshfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/tempfile.py +0 -0
- {pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/vfs/vfs.py +0 -0
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
import time
|
|
6
6
|
import warnings
|
|
7
|
-
from dataclasses import dataclass
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
8
|
from pathlib import Path, PurePosixPath
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
@@ -30,6 +30,21 @@ class CurrentFile:
|
|
|
30
30
|
hdu_list: fits.HDUList | None = None
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
@dataclass
|
|
34
|
+
class _RetryState:
|
|
35
|
+
attempts: int = 0
|
|
36
|
+
first_failed_at: float = field(default_factory=time.time)
|
|
37
|
+
alerted: bool = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Exceptions treated as unrecoverable regardless of which phase (read or write) raises them: no
|
|
41
|
+
# amount of retrying fixes a deleted file or wrong permissions. Everything else is assumed
|
|
42
|
+
# possibly transient (a down destination, a network hiccup) and gets bounded-backoff-forever
|
|
43
|
+
# retries instead. Deliberately coarse and biased towards over-retrying rather than silently
|
|
44
|
+
# dropping a file we misclassified -- see the design doc for the reasoning.
|
|
45
|
+
_TERMINAL_EXCEPTIONS = (FileNotFoundError, PermissionError, IsADirectoryError)
|
|
46
|
+
|
|
47
|
+
|
|
33
48
|
class ImageWatcher(Module):
|
|
34
49
|
"""Watch for new files and write them to all given destinations.
|
|
35
50
|
|
|
@@ -39,8 +54,11 @@ class ImageWatcher(Module):
|
|
|
39
54
|
New files are not processed immediately, but only after a wait time (``wait_time``) has passed since they were
|
|
40
55
|
detected. This is mainly to make sure the file is fully written before it is read: while the inotify watcher
|
|
41
56
|
only reports files after they have been closed for writing (``CLOSE_WRITE``), the polling watcher and the
|
|
42
|
-
initial scan in ``open()`` can see a file while its write is still in progress.
|
|
43
|
-
|
|
57
|
+
initial scan in ``open()`` can see a file while its write is still in progress.
|
|
58
|
+
|
|
59
|
+
A file that fails to process is retried with exponentially growing backoff, up to ``backoff_cap``, unless the
|
|
60
|
+
failure is unrecoverable (see ``_handle_failure``), in which case it's left in place and never retried. See
|
|
61
|
+
the ``error_after``/``backoff_cap`` constructor args for details.
|
|
44
62
|
"""
|
|
45
63
|
|
|
46
64
|
__module__ = "pyobs.modules.image"
|
|
@@ -54,6 +72,9 @@ class ImageWatcher(Module):
|
|
|
54
72
|
wait_time: int = 10,
|
|
55
73
|
pattern: str = "*",
|
|
56
74
|
flatten: bool = True,
|
|
75
|
+
*,
|
|
76
|
+
error_after: int,
|
|
77
|
+
backoff_cap: int = 3600,
|
|
57
78
|
**kwargs: Any,
|
|
58
79
|
):
|
|
59
80
|
"""Create a new image watcher.
|
|
@@ -64,8 +85,8 @@ class ImageWatcher(Module):
|
|
|
64
85
|
poll: If True, watchpath is polled instead of watched by inotify.
|
|
65
86
|
poll_interval: Interval for polling in seconds, if poll is True.
|
|
66
87
|
wait_time: Time in seconds between adding a file to the list and processing it. Gives a file that is
|
|
67
|
-
still being written time to finish (relevant for poll mode and the initial scan) and
|
|
68
|
-
|
|
88
|
+
still being written time to finish (relevant for poll mode and the initial scan) and is also the
|
|
89
|
+
base interval for retrying a file after a transient failure (see ``error_after``).
|
|
69
90
|
pattern: Only watch/process files matching this fnmatch pattern, checked against the full path (so
|
|
70
91
|
e.g. "*.fits" also matches files found in subdirectories). This is the single point of filtering,
|
|
71
92
|
applied uniformly in ``add_file`` regardless of watch mode -- inotify events, polling, and the
|
|
@@ -79,6 +100,21 @@ class ImageWatcher(Module):
|
|
|
79
100
|
write (the default for e.g. ``LocalFile``) -- a destination configured to not do that would
|
|
80
101
|
repeatedly fail and re-queue any file whose relative path needs a parent directory that doesn't
|
|
81
102
|
exist yet.
|
|
103
|
+
error_after: Seconds a file may keep failing with a transient error (see below) before a single
|
|
104
|
+
``ERROR`` is logged for it. Required -- no sane default exists, since how long a destination can
|
|
105
|
+
legitimately be unreachable (a remote archive, an NFS mount, ...) varies per deployment. Set it
|
|
106
|
+
to how long an outage has to last before it's worth paging someone. The file keeps being retried
|
|
107
|
+
(with growing backoff, capped at ``backoff_cap``) indefinitely either way; this only controls
|
|
108
|
+
when the one-time alert fires, not whether retrying continues.
|
|
109
|
+
backoff_cap: Maximum seconds between retries of a file that keeps failing transiently. The interval
|
|
110
|
+
starts at ``wait_time`` and doubles on each failure up to this cap.
|
|
111
|
+
|
|
112
|
+
Note:
|
|
113
|
+
A failure is either transient (retried forever, with the backoff/``error_after`` behavior above) or
|
|
114
|
+
terminal (``FileNotFoundError``, ``PermissionError``, ``IsADirectoryError``, wherever in the pipeline
|
|
115
|
+
they're raised) -- a terminal failure is logged as an ``ERROR`` immediately and the file is never
|
|
116
|
+
re-queued; it's simply left where it is in ``watchpath``; neither watch mode re-detects an existing,
|
|
117
|
+
unchanged file, so it stays visibly stuck rather than being retried forever or silently vanishing.
|
|
82
118
|
|
|
83
119
|
Note:
|
|
84
120
|
If a directory holding a file that's already queued (added but not yet processed by ``_worker``) is
|
|
@@ -105,6 +141,9 @@ class ImageWatcher(Module):
|
|
|
105
141
|
self._wait_time = wait_time
|
|
106
142
|
self._pattern = pattern
|
|
107
143
|
self._flatten = flatten
|
|
144
|
+
self._error_after = error_after
|
|
145
|
+
self._backoff_cap = backoff_cap
|
|
146
|
+
self._retry: dict[str, _RetryState] = {}
|
|
108
147
|
self.current_file: CurrentFile | None = None
|
|
109
148
|
|
|
110
149
|
# filename patterns
|
|
@@ -203,84 +242,114 @@ class ImageWatcher(Module):
|
|
|
203
242
|
await asyncio.sleep(wait)
|
|
204
243
|
log.info("Working on file %s...", filename)
|
|
205
244
|
|
|
206
|
-
# better safe than sorry
|
|
207
245
|
try:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
# other file (e.g. a raw camera binary like "w123.0") is still copied as-is to
|
|
214
|
-
# non-templated destinations, just without the parse attempt and the astropy
|
|
215
|
-
# header warnings it raises on non-FITS content. A matching name is no
|
|
216
|
-
# guarantee, though -- the data may be corrupt or truncated -- hence the
|
|
217
|
-
# try/except below.
|
|
218
|
-
fits_file = None
|
|
219
|
-
if os.path.basename(filename).lower().endswith(FITS_FILENAME_SUFFIXES):
|
|
220
|
-
try:
|
|
221
|
-
with warnings.catch_warnings():
|
|
222
|
-
warnings.simplefilter("ignore", fits.verify.VerifyWarning)
|
|
223
|
-
fits_file = await asyncio.to_thread(fits.HDUList.fromstring, data)
|
|
224
|
-
except Exception:
|
|
225
|
-
fits_file = None
|
|
226
|
-
|
|
227
|
-
# fill current file
|
|
228
|
-
self.current_file = CurrentFile(filename=filename, data=data, hdu_list=fits_file)
|
|
229
|
-
|
|
230
|
-
# loop archive and upload
|
|
231
|
-
success = True
|
|
232
|
-
for pattern in self._destinations:
|
|
233
|
-
# if it contains {placeholders}, we assume it's a FITS file and format filename
|
|
234
|
-
if "{" in pattern and "}" in pattern and fits_file is not None:
|
|
235
|
-
# format filename
|
|
236
|
-
out_filename = format_filename(fits_file["SCI"].header, pattern)
|
|
237
|
-
if out_filename is None:
|
|
238
|
-
raise ValueError("Could not create name for file.")
|
|
239
|
-
|
|
240
|
-
elif self._flatten:
|
|
241
|
-
# no formatting, so just add filename to destination
|
|
242
|
-
out_filename = os.path.join(pattern, os.path.basename(filename))
|
|
243
|
-
|
|
244
|
-
else:
|
|
245
|
-
# preserve the file's path relative to watchpath under the destination
|
|
246
|
-
rel = PurePosixPath(filename).relative_to(self._watchpath)
|
|
247
|
-
out_filename = str(PurePosixPath(pattern) / rel)
|
|
248
|
-
|
|
249
|
-
# store it
|
|
250
|
-
log.info("Storing file as %s...", out_filename)
|
|
251
|
-
self.current_file.out_filename = out_filename
|
|
252
|
-
try:
|
|
253
|
-
async with self.vfs.open_file(out_filename, "wb") as fd:
|
|
254
|
-
await fd.write(data)
|
|
255
|
-
except Exception as e:
|
|
256
|
-
log.warning("Error while copying file, skipping for now: %s", e)
|
|
257
|
-
success = False
|
|
258
|
-
break
|
|
259
|
-
|
|
260
|
-
# do extra processing
|
|
261
|
-
if not await self.process_extra(filename):
|
|
262
|
-
success = False
|
|
263
|
-
break
|
|
264
|
-
|
|
265
|
-
# no success?
|
|
266
|
-
if not success:
|
|
267
|
-
# re-queue file and skip file for now
|
|
268
|
-
self._queue.put_nowait((filename, time.time() + self._wait_time))
|
|
269
|
-
continue
|
|
270
|
-
|
|
271
|
-
# close and delete files
|
|
272
|
-
log.info("Removing file from watch directory...")
|
|
273
|
-
if not await self.vfs.remove(filename):
|
|
274
|
-
log.warning("Could not delete %s.", filename)
|
|
275
|
-
else:
|
|
276
|
-
# clean up any now-empty parent directories the file left behind
|
|
277
|
-
await self._cleanup_empty_parents(filename)
|
|
278
|
-
|
|
279
|
-
# cleanup extra
|
|
280
|
-
await self.cleanup_extra(filename)
|
|
246
|
+
await self._process_file(filename)
|
|
247
|
+
except Exception as e:
|
|
248
|
+
self._handle_failure(filename, e)
|
|
249
|
+
else:
|
|
250
|
+
self._retry.pop(filename, None)
|
|
281
251
|
|
|
252
|
+
async def _process_file(self, filename: str) -> None:
|
|
253
|
+
"""Read, archive and clean up a single file. Raises on any failure short of cleanup.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
filename: Local filename of the file to process.
|
|
257
|
+
"""
|
|
258
|
+
# get file data
|
|
259
|
+
async with self.vfs.open_file(filename, "rb") as fd:
|
|
260
|
+
data = await fd.read()
|
|
261
|
+
|
|
262
|
+
# only attempt to load the file as FITS when its name suggests it is one; any
|
|
263
|
+
# other file (e.g. a raw camera binary like "w123.0") is still copied as-is to
|
|
264
|
+
# non-templated destinations, just without the parse attempt and the astropy
|
|
265
|
+
# header warnings it raises on non-FITS content. A matching name is no
|
|
266
|
+
# guarantee, though -- the data may be corrupt or truncated -- hence the
|
|
267
|
+
# try/except below.
|
|
268
|
+
fits_file = None
|
|
269
|
+
if os.path.basename(filename).lower().endswith(FITS_FILENAME_SUFFIXES):
|
|
270
|
+
try:
|
|
271
|
+
with warnings.catch_warnings():
|
|
272
|
+
warnings.simplefilter("ignore", fits.verify.VerifyWarning)
|
|
273
|
+
fits_file = await asyncio.to_thread(fits.HDUList.fromstring, data)
|
|
282
274
|
except Exception:
|
|
283
|
-
|
|
275
|
+
fits_file = None
|
|
276
|
+
|
|
277
|
+
# fill current file
|
|
278
|
+
self.current_file = CurrentFile(filename=filename, data=data, hdu_list=fits_file)
|
|
279
|
+
|
|
280
|
+
# loop archive and upload
|
|
281
|
+
for pattern in self._destinations:
|
|
282
|
+
# if it contains {placeholders}, we assume it's a FITS file and format filename
|
|
283
|
+
if "{" in pattern and "}" in pattern and fits_file is not None:
|
|
284
|
+
# format filename
|
|
285
|
+
out_filename = format_filename(fits_file["SCI"].header, pattern)
|
|
286
|
+
if out_filename is None:
|
|
287
|
+
raise ValueError("Could not create name for file.")
|
|
288
|
+
|
|
289
|
+
elif self._flatten:
|
|
290
|
+
# no formatting, so just add filename to destination
|
|
291
|
+
out_filename = os.path.join(pattern, os.path.basename(filename))
|
|
292
|
+
|
|
293
|
+
else:
|
|
294
|
+
# preserve the file's path relative to watchpath under the destination
|
|
295
|
+
rel = PurePosixPath(filename).relative_to(self._watchpath)
|
|
296
|
+
out_filename = str(PurePosixPath(pattern) / rel)
|
|
297
|
+
|
|
298
|
+
# store it
|
|
299
|
+
log.info("Storing file as %s...", out_filename)
|
|
300
|
+
self.current_file.out_filename = out_filename
|
|
301
|
+
async with self.vfs.open_file(out_filename, "wb") as fd:
|
|
302
|
+
await fd.write(data)
|
|
303
|
+
|
|
304
|
+
# do extra processing
|
|
305
|
+
if not await self.process_extra(filename):
|
|
306
|
+
raise RuntimeError(f"process_extra rejected {filename}.")
|
|
307
|
+
|
|
308
|
+
# close and delete files
|
|
309
|
+
log.info("Removing file from watch directory...")
|
|
310
|
+
if not await self.vfs.remove(filename):
|
|
311
|
+
log.warning("Could not delete %s.", filename)
|
|
312
|
+
else:
|
|
313
|
+
# clean up any now-empty parent directories the file left behind
|
|
314
|
+
await self._cleanup_empty_parents(filename)
|
|
315
|
+
|
|
316
|
+
# cleanup extra -- the file has already been removed from watchpath and copied to every
|
|
317
|
+
# destination by this point, so a failure here can't be fixed by re-running the pipeline
|
|
318
|
+
# (it would just re-read a now-deleted file); log it and move on instead of re-queueing.
|
|
319
|
+
try:
|
|
320
|
+
await self.cleanup_extra(filename)
|
|
321
|
+
except Exception:
|
|
322
|
+
log.error("cleanup_extra failed for %s (file already transferred, not retrying).", filename)
|
|
323
|
+
|
|
324
|
+
def _handle_failure(self, filename: str, exc: Exception) -> None:
|
|
325
|
+
"""Handle a failure from ``_process_file``: re-queue transient failures with backoff, give
|
|
326
|
+
up (without re-queueing) on unrecoverable ones.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
filename: The file that failed to process.
|
|
330
|
+
exc: The exception raised by ``_process_file``.
|
|
331
|
+
"""
|
|
332
|
+
if isinstance(exc, _TERMINAL_EXCEPTIONS):
|
|
333
|
+
log.error("Giving up on %s, unrecoverable error: %s", filename, exc)
|
|
334
|
+
self._retry.pop(filename, None)
|
|
335
|
+
return
|
|
336
|
+
|
|
337
|
+
state = self._retry.setdefault(filename, _RetryState())
|
|
338
|
+
state.attempts += 1
|
|
339
|
+
elapsed = time.time() - state.first_failed_at
|
|
340
|
+
delay = min(self._wait_time * 2**state.attempts, self._backoff_cap)
|
|
341
|
+
if elapsed >= self._error_after and not state.alerted:
|
|
342
|
+
log.error(
|
|
343
|
+
"Still failing on %s after %.0fs of retries (attempt %d): %s",
|
|
344
|
+
filename,
|
|
345
|
+
elapsed,
|
|
346
|
+
state.attempts,
|
|
347
|
+
exc,
|
|
348
|
+
)
|
|
349
|
+
state.alerted = True
|
|
350
|
+
else:
|
|
351
|
+
log.warning("Retrying %s (attempt %d, next in %ds): %s", filename, state.attempts, delay, exc)
|
|
352
|
+
self._queue.put_nowait((filename, time.time() + delay))
|
|
284
353
|
|
|
285
354
|
async def _cleanup_empty_parents(self, filename: str) -> None:
|
|
286
355
|
"""Remove now-empty parent directories left behind by a just-deleted file.
|
|
@@ -87,7 +87,14 @@ class PushNotifier(Module, IPushNotifications):
|
|
|
87
87
|
if self._fcm_app is not None:
|
|
88
88
|
import firebase_admin
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
# delete_app() -> messaging service close() calls asyncio.run() internally, which
|
|
91
|
+
# raises if called from within a running event loop (our own) -- run it in a thread.
|
|
92
|
+
try:
|
|
93
|
+
await asyncio.wait_for(
|
|
94
|
+
asyncio.to_thread(firebase_admin.delete_app, self._fcm_app), timeout=_FIREBASE_CALL_TIMEOUT
|
|
95
|
+
)
|
|
96
|
+
except Exception:
|
|
97
|
+
log.exception("Failed to cleanly shut down Firebase app.")
|
|
91
98
|
self._fcm_app = None
|
|
92
99
|
|
|
93
100
|
async def _init_firebase(self) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/_dotnet_request_builder.py
RENAMED
|
File without changes
|
{pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/_dotnet_request_logger.py
RENAMED
|
File without changes
|
{pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/astrometry/_dotnet_response_saver.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/calibration/_calibration_cache.py
RENAMED
|
File without changes
|
{pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/calibration/_ccddata_calibrator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/detection/_pysep_stats_calculator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.9.0 → pyobs_core-2.9.2}/pyobs/images/processors/offsets/brighteststar_guiding.py
RENAMED
|
File without changes
|