Source code for hbutils.testing.capture.exit

"""
Overview:
    Capture for exitcode. This module provides utilities to capture system exit codes
    from calls to `sys.exit()` and `quit()` functions, allowing tests to verify exit
    behavior without actually terminating the process.
"""
from contextlib import contextmanager
from typing import ContextManager
from unittest.mock import patch

__all__ = [
    'capture_exit', 'ExitCaptureResult'
]

_DEFAULT_EXITCODE = 0


def _exitcode(status=None):
    """
    Normalize the exit status code.

    :param status: The exit status code, can be None or an integer.
    :type status: int or None
    :return: The normalized exit code, returns default if status is None.
    :rtype: int
    """
    return status if status is not None else _DEFAULT_EXITCODE


def _fake_exit(status=None):
    """
    Fake exit function that raises SystemExit instead of terminating the process.

    :param status: The exit status code.
    :type status: int or None
    :raises SystemExit: Always raises SystemExit with the given status.
    """
    raise SystemExit(status)


[docs] class ExitCaptureResult: """ Overview: Model of exit capture result. This class stores the captured exit code from a context where system exits are intercepted. """
[docs] def __init__(self, exitcode): """ Constructor of :class:`ExitCaptureResult`. :param exitcode: Initial exitcode value. :type exitcode: int """ self.__exitcode = exitcode
@property def exitcode(self) -> int: """ Get the captured exitcode value. :return: The exit code that was captured. :rtype: int .. note:: Do not use this property when :func:`capture_exit` is not over, otherwise this result \ is not guaranteed to be correct. """ return self.__exitcode
[docs] def put_result(self, exitcode: int): """ Put result inside this model. :param exitcode: New exitcode value to store. :type exitcode: int """ self.__exitcode = exitcode
[docs] @contextmanager def capture_exit(default: int = 0) -> ContextManager[ExitCaptureResult]: """ Overview: Capture for exitcode, :func:`quit` and :func:`sys.exit` can be captured. This context manager intercepts system exit calls and stores the exit code in an :class:`ExitCaptureResult` object instead of terminating the process. :param default: Default exitcode when no exit is called, default is ``0``. :type default: int :return: A context manager that yields an ExitCaptureResult object. :rtype: ContextManager[ExitCaptureResult] Examples:: >>> from hbutils.testing import capture_exit >>> with capture_exit() as ex: ... pass >>> ex.exitcode 0 >>> >>> with capture_exit() as ex: ... quit() >>> ex.exitcode 0 >>> >>> with capture_exit() as ex: ... quit(0xf) >>> ex.exitcode 15 >>> >>> import sys >>> with capture_exit() as ex: ... sys.exit(0xf) >>> ex.exitcode 15 """ capture = ExitCaptureResult(default) try: with patch('sys.exit', _fake_exit): yield capture except SystemExit as err: capture.put_result(_exitcode(err.code))