Source code for dynrec.errors
# Copyright 2026 Juan Daniel Suárez González
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""What can go wrong, as a family a caller can catch at whatever granularity it needs.
Everything raised by this library derives from :class:`DynrecError`, so a script that only wants
to log and carry on catches one thing. The distinctions below exist because they call for
different responses: a recorder that is not there yet is worth retrying, a call the recorder
refused is not.
"""
[docs]
class DynrecError(Exception):
"""Base class for every failure this library raises."""
[docs]
class RecorderNotFound(DynrecError):
"""No recorder on the graph, or none under the requested name."""
[docs]
class AmbiguousRecorder(DynrecError):
"""Several recorders are running and none was named.
Guessing would be worse than failing: the two recorders on a robot are usually recording
different things, and picking the wrong one is silent.
"""
def __init__(self, message, recorders=()):
super().__init__(message)
#: The recorder node names that were found, so a caller can choose without re-discovering.
self.recorders = list(recorders)
[docs]
class ServiceUnavailable(DynrecError):
"""The recorder is known but the service did not appear in time."""
[docs]
class CallTimeout(DynrecError):
"""The service was reached but did not reply in time.
Distinct from ServiceUnavailable on purpose: this one means the recorder is up and accepting
calls but did not finish this one, which points at a busy or wedged recorder rather than at a
name or a domain mismatch.
"""
[docs]
class CallFailed(DynrecError):
"""The recorder refused the call.
Carries the service's own `return_code` and `error_string`, because the recorder's message is
almost always more specific than anything this library could say on its behalf.
Also carries the raw `response`, when there was one. A refusal can still leave useful data on
the response -- a `set_topics` that failed every topic still lists them in `unavailable_topics`
-- and a CLI that prints those before reporting the error needs them to survive the raise.
"""
def __init__(self, message, return_code=None, error_string='', response=None):
super().__init__(message)
self.return_code = return_code
self.error_string = error_string
self.response = response
[docs]
class InvalidRequest(DynrecError, ValueError):
"""The request could not be built from what the caller passed.
Also a ValueError, since that is what a caller checking its own arguments will reach for, and
every instance of this really is a mistake in the calling code rather than a recorder fault.
"""