Skip to content

pygx.error_handling

Exception classification and contextual error capture for PyGX.

error_handling

Exception classification and contextual error capture for PyGX.

pygx.error_handling bundles the small set of error-flow primitives PyGX uses internally and exposes to callers:

  • catch_errors / CatchErrorsContext contextually suppress and aggregate matching exceptions.
  • match_error tests whether an exception matches a (type, message_pattern) selector.
  • ErrorInfo captures a structured, JSON-serializable view of an exception chain — a pg.Object so it serializes and renders cleanly.

ErrorInfo

ErrorInfo(tag: str, description: str, stacktrace: str, **kwargs)

Bases: Object, Extension

Structured, JSON-serializable view of an exception chain.

Source code in pygx/error_handling/_error_info.py
def __init__(self, tag: str, description: str, stacktrace: str, **kwargs):
    super().__init__(
        tag=tag,
        description=description,
        stacktrace=stacktrace,
        **kwargs,
    )

from_exception classmethod

from_exception(error: BaseException) -> ErrorInfo

Creates an error info from an exception.

The stacktrace is taken from sys.exc_info(), so this should be called from within the except block handling error — otherwise the captured stacktrace is the meaningless placeholder 'NoneType: None' rather than the error's actual traceback.

Source code in pygx/error_handling/_error_info.py
@classmethod
def from_exception(cls, error: BaseException) -> 'ErrorInfo':
    """Creates an error info from an exception.

    The stacktrace is taken from ``sys.exc_info()``, so this should be
    called from within the ``except`` block handling ``error`` —
    otherwise the captured stacktrace is the meaningless placeholder
    ``'NoneType: None'`` rather than the error's actual traceback.
    """
    return cls(
        tag=cls._compute_tag(error),
        description=str(error),
        stacktrace=''.join(traceback.format_exception(*sys.exc_info())),
    )

CatchErrorsContext dataclass

CatchErrorsContext(error: BaseException | None = None)

Context yielded by pg.catch_errors.

Its error attribute holds the caught exception, or None if the guarded block completed without a matching error.

catch_errors

catch_errors(
    errors: (
        type[BaseException]
        | tuple[type[BaseException], str]
        | Sequence[type[BaseException] | tuple[type[BaseException], str]]
    ),
    error_handler: Callable[[BaseException], None] | None = None,
) -> Iterator[CatchErrorsContext]

Context manager for catching user-specified exceptions.

Example:

import pygx as pg

def parse_input():
  raise ValueError('Input is wrong.')

with pg.catch_errors(
    [
        RuntimeError,
        (ValueError, 'Input is wrong.')
    ],
) as error_context:
  parse_input()

# The matching error is captured instead of propagating.
assert isinstance(error_context.error, ValueError)

Parameters:

Name Type Description Default
errors type[BaseException] | tuple[type[BaseException], str] | Sequence[type[BaseException] | tuple[type[BaseException], str]]

An exception type, a tuple of (exception type, error message regular expression), or a sequence of either, as the specification of the exceptions to catch. If an error raised within the scope does not match the specification, it will be propagated to the outer scope.

required
error_handler Callable[[BaseException], None] | None

An optional callable object to handle the error on failure. It's usually provided if the user wants to create a context manager based on pg.catch_errors with specific error handling logic.

None

Yields:

Type Description
CatchErrorsContext

A CatchErrorsContext object, whose error attribute holds the

CatchErrorsContext

caught exception (or None if the block completed without error).

Source code in pygx/error_handling/_error_utils.py
@contextlib.contextmanager
def catch_errors(
    errors: (
        type[BaseException]
        | tuple[type[BaseException], str]
        | Sequence[type[BaseException] | tuple[type[BaseException], str]]
    ),
    error_handler: Callable[[BaseException], None] | None = None,
) -> Iterator['CatchErrorsContext']:
    """Context manager for catching user-specified exceptions.

    **Example:**

    ```python
    import pygx as pg

    def parse_input():
      raise ValueError('Input is wrong.')

    with pg.catch_errors(
        [
            RuntimeError,
            (ValueError, 'Input is wrong.')
        ],
    ) as error_context:
      parse_input()

    # The matching error is captured instead of propagating.
    assert isinstance(error_context.error, ValueError)
    ```

    Args:
      errors: An exception type, a tuple of (exception type, error message
        regular expression), or a sequence of either, as the specification
        of the exceptions to catch. If an error raised within the scope does
        not match the specification, it will be propagated to the outer
        scope.
      error_handler: An optional callable object to handle the error on
        failure. It's usually provided if the user wants to create a context
        manager based on ``pg.catch_errors`` with specific error handling
        logic.

    Yields:
      A ``CatchErrorsContext`` object, whose ``error`` attribute holds the
      caught exception (or None if the block completed without error).
    """
    error_mapping = _parse_error_spec(errors)
    context = CatchErrorsContext()
    try:
        yield context
    except BaseException as e:  # pylint: disable=broad-exception-caught
        if match_error(e, error_mapping):
            context.error = e
            if error_handler is not None:
                error_handler(e)
        else:
            raise

match_error

match_error(
    error: BaseException,
    errors: (
        type[BaseException]
        | tuple[type[BaseException], str]
        | Sequence[type[BaseException] | tuple[type[BaseException], str]]
        | dict[type[BaseException], list[str]]
    ),
) -> bool

Returns True if the error matches the specification.

A bare exception type matches any instance of that type (including subclasses). A (type, regex) tuple additionally matches the regular expression against the string '<TypeName>: <message>'; regexes not anchored with ^ or .* are treated as substring searches.

Parameters:

Name Type Description Default
error BaseException

The error to match.

required
errors type[BaseException] | tuple[type[BaseException], str] | Sequence[type[BaseException] | tuple[type[BaseException], str]] | dict[type[BaseException], list[str]]

An exception type, a tuple of (exception type, error message regular expression), or a sequence of either, as the specification of the exceptions to match.

required

Returns:

Type Description
bool

True if the error matches the specification, False otherwise.

Source code in pygx/error_handling/_error_utils.py
def match_error(
    error: BaseException,
    errors: (
        type[BaseException]
        | tuple[type[BaseException], str]
        | Sequence[type[BaseException] | tuple[type[BaseException], str]]
        | dict[type[BaseException], list[str]]
    ),
) -> bool:
    """Returns True if the error matches the specification.

    A bare exception type matches any instance of that type (including
    subclasses). A (type, regex) tuple additionally matches the regular
    expression against the string ``'<TypeName>: <message>'``; regexes not
    anchored with ``^`` or ``.*`` are treated as substring searches.

    Args:
      error: The error to match.
      errors: An exception type, a tuple of (exception type, error message
        regular expression), or a sequence of either, as the specification
        of the exceptions to match.

    Returns:
      True if the error matches the specification, False otherwise.
    """
    error_mapping = _parse_error_spec(errors)
    error_message = error.__class__.__name__ + ': ' + str(error)
    for error_type, error_regexes in error_mapping.items():
        if isinstance(error, error_type):
            if not error_regexes:
                return True
            else:
                for regex in error_regexes:
                    assert regex is not None
                    if not regex.startswith(('^', '.*')):
                        regex = '.*' + regex
                    if re.match(regex, error_message):
                        return True
    return False

options: show_root_heading: false show_root_toc_entry: false members_order: source heading_level: 2