File: unexceptional.py

package info (click to toggle)
python-sshsig 0.2.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 236 kB
  • sloc: python: 901; sh: 53; makefile: 9
file content (62 lines) | stat: -rw-r--r-- 1,983 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# (c) 2024 E. Castedo Ellerman <castedo@castedo.com>
# Released under the MIT License (https://spdx.org/licenses/MIT)

"""Helper functions for returning and raising unexceptional exceptions.

Unexceptional exceptions are Exception objects that are returned as "normal" error
objects, with return type hints, for "normal" non-try-except execution flow in some,
but not necessarily all, layers of a Python application.

With return type hints, type checkers, such as Mypy, can identify when unexceptional
exceptions are not being handled properly.

See the README.md in https://gitlab.com/castedo/unexceptional for more information.
"""

from __future__ import annotations

from types import TracebackType
from typing import TYPE_CHECKING, TypeVar, cast

if TYPE_CHECKING:
    ExceptionT = TypeVar('ExceptionT', bound='Exception')
    NonExceptionT = TypeVar('NonExceptionT')


def cast_or_raise(ret: NonExceptionT | Exception) -> NonExceptionT:
    """Cast a value to a non-Exception type or raise an Exception.

    Example:
        def do_fancy_math(x: float) -> float | ValueError:
            ...

        try:
            ...
            y = cast_or_raise(do_fancy_math(x))
            # mypy knows y is a float here
            ...
        except ValueEror:
            ...
    """
    if isinstance(ret, Exception):
        raise ret
    return ret


def unexceptional(ex: ExceptionT, cause: Exception | None = None) -> ExceptionT:
    """Return an Exception object with a stack trace and optional cause.

    Example:
        if bad_case:
            return unexceptional(ValueError("Bad case"))
    """
    try:
        raise ex
    except Exception as ret:
        ret.__cause__ = cause
        if not ret.__traceback__:
            return cast('ExceptionT', ret)
        frame = ret.__traceback__.tb_frame
        frame = frame.f_back or frame
        tb = TracebackType(None, frame, frame.f_lasti, frame.f_lineno)
        return cast('ExceptionT', ret.with_traceback(tb))