indikit.exceptions¶
Everything INDIkit raises on purpose derives from IndiError, so one except
catches the whole library. Every type also derives from the builtin that was
raised at that site before the hierarchy existed - ProtocolError is a
ValueError, PropertyNotFound is a KeyError, NotConnectedError is a
ConnectionError - so existing except clauses keep catching exactly what they
caught before.
These are the types named in every Raises section elsewhere in this reference.
indikit.exceptions ¶
The exception hierarchy every INDIkit failure belongs to.
Two rules shape this module, and both matter more than the class names.
One root. Everything the package raises deliberately derives from
:class:IndiError, so an application that wants to survive the library rather
than diagnose it can write one except IndiError and mean it. Before this
existed the same RuntimeError came out of a retracted property handle and an
unattached device - two unrelated failures a caller had to tell apart by reading
the message text.
Every type keeps its builtin. Each class below also inherits from the
builtin that used to be raised at its site, so
PropertyNotFound is a :class:KeyError, WrongPropertyKind is a
:class:TypeError, and so on. That is what makes adopting the hierarchy purely
additive: existing except KeyError in this repository, in the examples and
in third-party drivers keeps catching exactly what it caught before, and the
familiar idioms (vector["RA"] inside a try/except KeyError,
dict-style lookups) go on reading naturally. The builtin also comes first in
the MRO for message formatting, so str(PropertyNotFound("RA")) still renders
the way a :class:KeyError does.
Adding a new type here means answering both questions: what it is a kind of
(:class:IndiError, always) and what it must stay compatible with.
IndiError ¶
Bases: Exception
Base class for every error INDIkit raises on purpose.
Catching this catches the whole library. It is never raised directly.
ProtocolError ¶
Bases: IndiError, ValueError
The INDI wire format was violated by a value or a message.
Raised for text that is not a number where the DTD requires one, a
#REQUIRED attribute that is absent, an unknown element kind, or a value
(a non-finite float) that neither wire format can carry. Also a
:class:ValueError, which is what the codecs raised before and what the
stream parser catches to drop a bad element instead of dying on it.
PropertyNotFound ¶
Bases: IndiError, KeyError
A property or an element was looked up by a name nothing answers to.
Covers both lookups because they are the same mistake at two depths: the
device has no such property, or the vector has no such element. Also a
:class:KeyError, so mapping-shaped access (device["COOLER"],
vector["RA"]) keeps failing the way a mapping fails.
WrongPropertyKind ¶
Bases: IndiError, TypeError
A property was reached through an accessor for a different vector kind.
device.number("CONNECTION") on a switch vector, or a
:meth:~indikit.driver.property.BoundProperty.select on a vector kind
with no natural "unselected" value. Also a :class:TypeError: the name
resolved, the operation does not apply to what it resolved to.
PropertyRetracted ¶
Bases: IndiError, RuntimeError
A retracted property's handle was used to publish an update.
The client has been told the property is gone, so anything published
through the dead handle contradicts that. Define the property again and use
the handle that returns. Also a :class:RuntimeError.
DeviceNotServing ¶
Bases: IndiError, RuntimeError
A device tried to send while not attached to a runtime.
define_*, message() and every other emission need somewhere to send
to, which a device only has while it is being served. Also a
:class:RuntimeError.
NotConnectedError ¶
Bases: IndiError, ConnectionError
The client has no live connection to indiserver.
Raised by every :class:~indikit.client.IndiClient send rather than
queueing the message for a connection that may be an hour away, and used to
fail the waiters still parked on
:meth:~indikit.client.IndiClient.wait_for when the client is closed.
Also a :class:ConnectionError (hence an :class:OSError), because that is
what it is: the peer is not there.
ConfigError ¶
Bases: IndiError, OSError
A device's saved configuration could not be read, written or located.
Covers every way persistence fails: no resolvable configuration directory,
a device name that cannot be a filename, a file that is absent, oversized or
not valid JSON, and any refusal from the filesystem underneath. Also an
:class:OSError, because that is what the filesystem would have raised on
its own, so an except OSError already wrapped around a driver goes on
catching it.
Its message is written for a client to read: it never quotes a path or an
operating-system error string, both of which go to the indikit
logger instead.
SendQueueFull ¶
Bases: IndiError, RuntimeError
The outbox is full: the connection is not draining as fast as we send.
The bound exists so a wedged writer cannot grow the queue without limit.
Reaching it means the socket has stopped accepting what this client is
producing, and for instrument control a command that arrives late is worse
than one that fails, so this is raised rather than awaited. Also a
:class:RuntimeError.