Skip to content

Commit eb50ee8

Browse files
committed
refactor(python/exceptions): refactor exceptions (wip)
1 parent 8699470 commit eb50ee8

File tree

4 files changed

+30
-502
lines changed

4 files changed

+30
-502
lines changed

python/src/demo/containers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import builtins
44
from collections.abc import Iterable
5-
from typing import Any, Callable, Generic, Iterator, TypeVar
5+
from typing import Callable, Generic, Iterator, TypeVar
66

77
import cpp_features.containers as _containers
88

python/src/demo/exceptions.py

Lines changed: 27 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -4,138 +4,45 @@
44
"""
55

66
from enum import Enum
7-
from typing import Any, Callable, Generic, TypeVar
87

9-
T = TypeVar('T')
10-
U = TypeVar('U')
8+
import cpp_features.exceptions as _exceptions
119

1210

1311
class ErrorSeverity(Enum):
14-
TRACE = 0
15-
DEBUG = 1
16-
INFO = 2
17-
WARNING = 3
18-
ERROR = 4
19-
FATAL = 5
12+
"""Error severity levels."""
2013

14+
TRACE = _exceptions.ErrorSeverity.TRACE
15+
DEBUG = _exceptions.ErrorSeverity.DEBUG
16+
INFO = _exceptions.ErrorSeverity.INFO
17+
WARNING = _exceptions.ErrorSeverity.WARNING
18+
ERROR = _exceptions.ErrorSeverity.ERROR
19+
FATAL = _exceptions.ErrorSeverity.FATAL
2120

22-
class _PyResult(Generic[T]):
23-
def __init__(self, value: T | None = None, error: Exception | None = None):
24-
self._value = value
25-
self._error = error
2621

27-
def has_value(self) -> bool:
28-
return self._error is None
22+
def severity_to_string(severity: ErrorSeverity) -> str:
23+
"""Convert error severity to string.
2924
30-
def get_value(self) -> T:
31-
if self._error is not None:
32-
raise self._error
33-
return self._value # type: ignore[return-value]
25+
Parameters
26+
----------
27+
severity : ErrorSeverity
28+
The error severity to convert to a string
3429
35-
def get_exception(self) -> Exception:
36-
if self._error is None:
37-
raise RuntimeError('No exception in successful result')
38-
return self._error
30+
Returns
31+
-------
32+
str
33+
The string representation of the error severity
34+
"""
35+
return _exceptions.severity_to_string(severity)
3936

40-
def __str__(self) -> str:
41-
return f'Ok({self._value})' if self.has_value() else f'Err({self._error})'
42-
43-
44-
class Result(Generic[T]):
45-
def __init__(self, inner: _PyResult[T]):
46-
self._inner = inner
47-
48-
@property
49-
def is_ok(self) -> bool:
50-
return self._inner.has_value()
51-
52-
@property
53-
def is_err(self) -> bool:
54-
return not self.is_ok
55-
56-
def unwrap(self) -> T:
57-
return self._inner.get_value()
58-
59-
def unwrap_or(self, default: T) -> T:
60-
return self._inner.get_value() if self.is_ok else default
61-
62-
def unwrap_or_else(self, func: Callable[[], T]) -> T:
63-
return self._inner.get_value() if self.is_ok else func()
64-
65-
def map(self, func: Callable[[T], U]) -> 'Result[U]':
66-
if self.is_ok:
67-
try:
68-
return Result.ok(func(self.unwrap()))
69-
except Exception as e:
70-
return Result.error(str(e))
71-
return Result(_PyResult(error=self._inner.get_exception()))
72-
73-
def and_then(self, func: Callable[[T], 'Result[U]']) -> 'Result[U]':
74-
if self.is_ok:
75-
return func(self.unwrap())
76-
return Result(_PyResult(error=self._inner.get_exception()))
77-
78-
def or_else(self, func: Callable[[Exception], 'Result[T]']) -> 'Result[T]':
79-
if self.is_err:
80-
return func(self._inner.get_exception())
81-
return self
82-
83-
@staticmethod
84-
def ok(value: T) -> 'Result[T]':
85-
return Result(_PyResult(value=value))
86-
87-
@staticmethod
88-
def error(
89-
message: str, severity: ErrorSeverity = ErrorSeverity.ERROR
90-
) -> 'Result[Any]':
91-
# Attach severity information via a simple Exception subclass
92-
class _Error(Exception):
93-
def __init__(self, msg: str, sev: ErrorSeverity):
94-
super().__init__(msg)
95-
self.severity = sev
96-
97-
return Result(_PyResult(error=_Error(message, severity)))
98-
99-
def __bool__(self) -> bool:
100-
return self.is_ok
101-
102-
def __str__(self) -> str:
103-
return str(self._inner)
104-
105-
def __repr__(self) -> str:
106-
return f'Result({self._inner})'
107-
108-
109-
def safe_divide(a: float, b: float) -> Result[float]:
110-
if b == 0.0:
111-
return Result.error('Division by zero')
112-
return Result.ok(a / b)
113-
114-
115-
def safe_sqrt(x: float) -> Result[float]:
116-
if x < 0:
117-
return Result.error('Square root of negative number')
118-
return Result.ok(x**0.5)
119-
120-
121-
def chain_operations(
122-
*operations: Callable[[Any], Result[Any]]
123-
) -> Callable[[Any], Result[Any]]:
124-
def chained(initial_value: Any) -> Result[Any]:
125-
result = Result.ok(initial_value)
126-
for operation in operations:
127-
if result.is_err:
128-
break
129-
result = result.and_then(operation)
130-
return result
131-
132-
return chained
13337

38+
ValidationException: type[Exception] = _exceptions.ValidationException
39+
ResourceException: type[Exception] = _exceptions.ResourceException
40+
CalculationException: type[Exception] = _exceptions.CalculationException
13441

13542
__all__ = [
13643
'ErrorSeverity',
137-
'Result',
138-
'safe_divide',
139-
'safe_sqrt',
140-
'chain_operations',
44+
'severity_to_string',
45+
'ValidationException',
46+
'ResourceException',
47+
'CalculationException',
14148
]

python/tests/test_containers.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
"""
55

66
import pytest
7-
8-
from python import containers
7+
from demo import containers
98

109

1110
class TestContainer:

0 commit comments

Comments
 (0)