From 33f3c6fc4ca8810cee4c517594be725a26eec70c Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Sun, 26 Jul 2026 13:49:26 +0200 Subject: pyouroboros: Use consistent format docstrings This is basically a clean up pass to make sure all docstrings are aligned. The copyright banners are replaced by SPDX statements. Also did a general clean up on the comments. Passes pylint. No structural changes. Signed-off-by: Dimitri Staessens --- ouroboros/errors.py | 222 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 130 insertions(+), 92 deletions(-) (limited to 'ouroboros/errors.py') diff --git a/ouroboros/errors.py b/ouroboros/errors.py index aa1e174..6ab6053 100644 --- a/ouroboros/errors.py +++ b/ouroboros/errors.py @@ -1,30 +1,13 @@ # -# Ouroboros - Copyright (C) 2016 - 2026 -# -# Python API for Ouroboros - Exceptions and errno translation -# -# Dimitri Staessens -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# version 2.1 as published by the Free Software Foundation. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., http://www.fsf.org/about/contact/. +# SPDX-FileCopyrightText: 2016 - 2026 Dimitri Staessens +# SPDX-License-Identifier: LGPL-2.1-only # -"""Exception hierarchy and errno translation for pyouroboros. +""" +Exception hierarchy and errno translation for pyouroboros. -The hierarchy follows stdlib precedent (``socket``, ``ssl``, ``json``): -a single root :class:`OuroborosError`, semantically-named subclasses, -and multiple inheritance from Python builtins where the semantics -match (e.g. :class:`FlowTimeout` is a :class:`TimeoutError`). +The ``E*`` constants mirror the Ouroboros-specific errno values in +``include/ouroboros/errno.h``. """ from __future__ import annotations @@ -33,8 +16,6 @@ import errno import os from importlib.metadata import PackageNotFoundError, version -# Ouroboros-specific errno values, mirroring include/ouroboros/errno.h. -# Imported here (rather than via CFFI) so this module stays FFI-free ENOTALLOC = 1000 # Flow is not allocated EIPCPTYPE = 1001 # Unknown IPCP type EIRMD = 1002 # Failed to communicate with IRMD @@ -47,128 +28,177 @@ ECRYPT = 1008 # Encryption error EAUTH = 1009 # Authentication error EREPLAY = 1010 # OAP replay detected +_O7S_ERRNO_STR: dict[int, str] = { + ENOTALLOC: "flow is not allocated", + EIPCPTYPE: "unknown IPCP type", + EIRMD: "failed to communicate with IRMd", + EIPCP: "failed to communicate with IPCP", + EIPCPSTATE: "target in wrong state", + EFLOWDOWN: "flow is down", + EFLOWPEER: "flow peer timed out", + ENAME: "naming error", + ECRYPT: "encryption error", + EAUTH: "authentication error", + EREPLAY: "OAP replay detected", +} -# --- Root --- class OuroborosError(Exception): - """Base class for all pyouroboros exceptions.""" - + """ + Base class for all pyouroboros exceptions. + """ -# --- IRM-side --- class IrmError(OuroborosError): - """Base class for IRM (control plane) errors.""" + """ + Base class for IRM (control plane) errors. + """ class IpcpCreateError(IrmError): - """Raised when IPCP creation fails.""" + """ + Raised when IPCP creation fails. + """ class IpcpBootstrapError(IrmError): - """Raised when IPCP bootstrapping fails.""" + """ + Raised when IPCP bootstrapping fails. + """ class IpcpEnrollError(IrmError): - """Raised when IPCP enrollment fails.""" + """ + Raised when IPCP enrollment fails. + """ class IpcpConnectError(IrmError): - """Raised when IPCP connection or disconnection fails.""" + """ + Raised when IPCP connection or disconnection fails. + """ class IpcpTypeError(IrmError, ValueError): - """Raised when an unknown IPCP type is encountered.""" + """ + Raised when an unknown IPCP type is encountered. + """ class IpcpStateError(IrmError): - """Raised when an IPCP/IRMd is in the wrong state for the request.""" + """ + Raised when an IPCP/IRMd is in the wrong state for the request. + """ class IrmdError(IrmError): - """Raised when the IRM daemon is unreachable.""" + """ + Raised when the IRM daemon is unreachable. + """ class IpcpdError(IrmError): - """Raised when the IPCP daemon is unreachable.""" + """ + Raised when the IPCP daemon is unreachable. + """ class BindError(IrmError): - """Raised when binding a program/process/IPCP to a name fails.""" + """ + Raised when binding a program/process/IPCP to a name fails. + """ class NameNotFoundError(IrmError): - """Raised when a name lookup or unregister target does not exist.""" + """ + Raised when a name lookup or unregister target is missing. + """ class NameExistsError(IrmError): - """Raised when a name already exists.""" + """ + Raised when a name already exists. + """ class InvalidNameError(IrmError, ValueError): - """Raised when a name is malformed or otherwise invalid.""" - + """ + Raised when a name is malformed or otherwise invalid. + """ -# --- Flow / dev-side --- class FlowError(OuroborosError): - """Base class for flow-plane errors.""" + """ + Base class for flow-plane errors. + """ class FlowAlreadyAllocatedError(FlowError): - """Raised when allocating on a Flow that already holds an fd.""" + """ + Raised when allocating on a Flow that already holds an fd. + """ class FlowNotAllocatedError(FlowError): - """Raised when operating on a Flow that has no fd.""" + """ + Raised when operating on a Flow that has no fd. + """ class FlowDownError(FlowError, ConnectionError): - """Raised when the flow has gone down.""" + """ + Raised when the flow has gone down. + """ class FlowPeerError(FlowDownError): - """Raised when the flow's peer has timed out.""" + """ + Raised when the flow's peer has timed out. + """ class FlowPermissionError(FlowError, PermissionError): - """Raised when an operation is not permitted on the flow.""" + """ + Raised when an operation is not permitted on the flow. + """ class FlowTimeout(FlowError, TimeoutError): - """Raised when a flow operation times out.""" + """ + Raised when a flow operation times out. + """ class FlowCryptError(FlowError): - """Raised on flow encryption errors.""" + """ + Raised on flow encryption errors. + """ class FlowAuthError(FlowError): - """Raised on flow authentication errors.""" + """ + Raised on flow authentication errors. + """ class FlowReplayError(FlowError): - """Raised when OAP replay is detected.""" + """ + Raised when OAP replay is detected. + """ class FlowEventError(FlowError): - """Raised on errors from the flow event subsystem.""" + """ + Raised on errors from the flow event subsystem. + """ class FlowDeallocWarning(Warning): - """Warning issued when a deallocation reports a non-fatal problem.""" - + """ + Warning issued when a deallocation reports a non-fatal problem. + """ -# --- errno translation --- -# Errno -> Python exception class. Errnos are in their canonical -# positive form here; raise_errno() flips the sign. -# -# Mapped errnos override the caller's `default=` argument, so we only -# map errnos whose semantics are unambiguous across both flow and IRM -# contexts. EPERM and EINVAL are intentionally NOT mapped: they can -# mean very different things on different operations, and a global -# mapping would silently override the call-site's chosen default (e.g. -# masking IpcpBootstrapError as a bare ValueError). _ERRNO_MAP: dict[int, type[BaseException]] = { errno.ETIMEDOUT: TimeoutError, errno.EAGAIN: BlockingIOError, @@ -194,49 +224,57 @@ _ERRNO_MAP: dict[int, type[BaseException]] = { def _strerror(err: int) -> str: if err >= 1000: return _O7S_ERRNO_STR.get(err, f"ouroboros errno {err}") - return os.strerror(err) - -_O7S_ERRNO_STR: dict[int, str] = { - ENOTALLOC: "flow is not allocated", - EIPCPTYPE: "unknown IPCP type", - EIRMD: "failed to communicate with IRMd", - EIPCP: "failed to communicate with IPCP", - EIPCPSTATE: "target in wrong state", - EFLOWDOWN: "flow is down", - EFLOWPEER: "flow peer timed out", - ENAME: "naming error", - ECRYPT: "encryption error", - EAUTH: "authentication error", - EREPLAY: "OAP replay detected", -} + return os.strerror(err) def raise_errno(rc: int, default: type[OuroborosError] = FlowError) -> int: - """Translate a non-negative-or-negative-errno return code. - - Returns *rc* unchanged when it is non-negative. Otherwise raises - the mapped exception (or *default* if no mapping exists). """ + Translate a non-negative-or-negative-errno return code. + + ``_ERRNO_MAP`` holds errnos in their canonical positive form. + + :param rc: The return code to translate; a negative + value is a negated errno. + :param default: Exception class to raise when the errno + has no mapping; a mapped errno overrides + it. + :return: *rc* unchanged when it is non-negative. + :raises OuroborosError: (or the builtin mapped for ``-rc``) when + *rc* is negative. + """ + if rc >= 0: return rc + err = -rc exc_cls = _ERRNO_MAP.get(err, default) raise exc_cls(_strerror(err)) def check_ouroboros_version(o7s_major: int, o7s_minor: int) -> None: - """Verify that the installed pyouroboros matches the linked library. - - Compares *o7s_major*/*o7s_minor* (from - ``OUROBOROS_VERSION_{MAJOR,MINOR}`` in the CFFI module) against - the installed pyouroboros distribution metadata. Silently returns - when running from a source tree without distribution metadata. """ + Verify that the installed pyouroboros matches the linked library. + + Compares the linked library version against the installed + pyouroboros distribution metadata. Silently returns when running + from a source tree without distribution metadata. + + :param o7s_major: Major version of the linked library, from + ``OUROBOROS_VERSION_MAJOR`` in the CFFI + module. + :param o7s_minor: Minor version of the linked library, from + ``OUROBOROS_VERSION_MINOR`` in the CFFI + module. + :raises RuntimeError: If the library and pyouroboros ``major.minor`` + differ. + """ + try: pyo7s_parts = version("PyOuroboros").split(".") except PackageNotFoundError: return + if o7s_major != int(pyo7s_parts[0]) or \ o7s_minor != int(pyo7s_parts[1]): raise RuntimeError( -- cgit v1.2.3