Exceptions (pyobs.utils.exceptions)
The pyobs exception hierarchy, used both for local errors and to carry failures across the RPC boundary between modules.
Two independent axes live in this hierarchy:
Domain exceptions (everything above RemoteError below) mean “the operation failed for reason X.” These deliberately multiply into fine-grained leaves (MoveError, FocusError, GrabImageError, …) so a caller can react differently to each one, rather than catching one broad type and inspecting a message string.
`RemoteError` and its small subtree mean “the call itself didn’t reach/return” (a transport failure), not “the remote operation failed for a domain reason.” That distinction doesn’t benefit from the same fine-grained treatment, so it stays deliberately small.
A domain exception raised on the far side of an RPC call is looked up in the registry below by its fully-qualified name and re-raised locally as its real type, tagged with remote_module (see comm/xmpp/rpc.py’s fault reconstruction) – it does not travel wrapped in a RemoteError. If the type can’t be resolved (its defining module was never imported in this process, or it was never a PyobsError to begin with), it arrives as UnclassifiedError instead, with the original type name preserved as original_type.
Every subclass registers itself automatically via __init_subclass__ (PyobsError._registry); there’s nothing to do at definition time beyond subclassing the right branch of the hierarchy.
PyobsError
AbortedError
AcquisitionError
DeviceBusyError
- exception DeviceBusyError(message: str | None = None, **context: Any)[source]
The device can’t service this request right now because it’s already busy with another operation (e.g. an exposure/sequence already running, or another motion in progress) – back off and retry, as opposed to GrabImageError/MoveError/etc., which mean the operation was actually attempted and failed. Deliberately one type across camera/telescope/roof/focuser modules alike, not split by device or by which specific operation was busy – no caller reacts differently to those variants.
ExceptionHandler
- exception ExceptionHandler(exc_type, limit, timespan, module, callback)[source]
Create new instance of ExceptionHandler(exc_type, limit, timespan, module, callback)
- callback: Callable[[PyobsError], Coroutine[Any, Any, None]] | None
Alias for field number 4
- exc_type: type[PyobsError]
Alias for field number 0
- limit: int
Alias for field number 1
- module: str | None
Alias for field number 3
- timespan: float | None
Alias for field number 2
ForbiddenError
FocusError
GeneralError
GrabImageError
ImageError
InitError
InvalidArgumentError
- exception InvalidArgumentError(message: str | None = None, **context: Any)[source]
The caller passed an argument this method rejects (unknown name, out-of-range value, …). Unlike a plain ValueError, this survives an RPC round trip as itself, so except exc.InvalidArgumentError: around a proxy call actually catches it – a bare ValueError would silently degrade to UnclassifiedError the moment the call crosses XMPP, even though it works fine locally (LocalComm, direct calls, tests), which is exactly the kind of inconsistency that only shows up once code goes from a local test to a networked deployment. Deliberately one type across every setter-shaped method, not split per method or per argument – no caller reacts differently to “unknown filter” vs. “invalid focus value.
LoggedException
- exception LoggedException(time, exception)[source]
Create new instance of LoggedException(time, exception)
- exception: PyobsError
Alias for field number 1
- time: float
Alias for field number 0
ModuleError
ModuleStartingError
- exception ModuleStartingError(message: str | None = None, **context: Any)[source]
The module hasn’t finished its own startup yet (still inside open(), e.g. connecting to hardware) and isn’t safe to command – distinct from DeviceBusyError, which means the module is fully up but its device is occupied with another operation. Back off and retry once the module reports ModuleState.READY, rather than treating this as a domain failure.
MotionError
MoveError
NotSupportedError
- exception NotSupportedError(message: str | None = None, **context: Any)[source]
This module doesn’t support the requested operation at all – a capability the module only optionally implements (e.g. an alt/az-only telescope asked to move_radec) isn’t available, as opposed to a specific attempt at that operation failing. Cross-cutting: any module with an optional-capability mixin can raise this, not just telescopes.
ParkError
RemoteError
- exception RemoteError(message: str | None = None, **context: Any)[source]
The call itself didn’t reach/return – a transport failure, not a domain exception raised by the remote operation (see the module docstring for that distinction). Kept deliberately small: “the call failed to even happen” doesn’t need the same fine-grained treatment as a domain failure.
RemoteTimeoutError
UnclassifiedError
- exception UnclassifiedError(message: str | None = None, **context: Any)[source]
Wraps an exception that isn’t part of the deliberate PyobsError contract – either it never was a PyobsError to begin with (a builtin, a vendor SDK exception) and escaped a module’s own method body unconverted, or it crossed an RPC boundary and couldn’t be reconstructed as its real type because its defining module was never imported in this process. The original type name survives as original_type even when the class itself doesn’t.