Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "danom"
version = "0.12.0"
version = "0.12.1"
description = "Functional streams and monads"
readme = "README.md"
license = "MIT"
Expand Down
23 changes: 9 additions & 14 deletions src/danom/_safe.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
E = TypeVar("E")


class safe[**P, U]: # noqa: N801
"""Decorator for functions that wraps the function in a try except returns ``Ok`` on success else ``Err``.
def safe[**P, U](func: Callable[P, U]) -> Callable[P, Result[U, Exception]]:
"""Decorator for functions that wraps the function in a try except returns `Ok` on success else `Err`.

.. code-block:: python

Expand All @@ -25,17 +25,14 @@ def add_one(a: int) -> int:
add_one(1) == Ok(inner=2)
"""

def __init__(self, func: Callable[P, U]) -> None:
self.func = func
functools.update_wrapper(self, func)

def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Result[U, Exception]:
@functools.wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> Result[U, Exception]:
try:
return Ok(self.func(*args, **kwargs))
return Ok(func(*args, **kwargs))
except Exception as e: # noqa: BLE001
return Err(
error=e, input_args=(args, kwargs), traceback=traceback.format_exc()
) # ty: ignore[invalid-return-type]
return Err(error=e, input_args=(args, kwargs), traceback=traceback.format_exc()) # ty: ignore[invalid-return-type]

return wrapper


def safe_method[T, **P, U](
Expand Down Expand Up @@ -63,8 +60,6 @@ def wrapper(self: T, *args: P.args, **kwargs: P.kwargs) -> Result[U, Exception]:
try:
return Ok(func(self, *args, **kwargs))
except Exception as e: # noqa: BLE001
return Err(
error=e, input_args=(self, args, kwargs), traceback=traceback.format_exc()
) # ty: ignore[invalid-return-type]
return Err(error=e, input_args=(self, args, kwargs), traceback=traceback.format_exc()) # ty: ignore[invalid-return-type]

return wrapper
4 changes: 2 additions & 2 deletions src/danom/_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ class _Nothing(Enum):
AsyncPlannedOps = tuple[str, AsyncStreamFn]


def _apply_fns_worker[T](args: tuple[tuple[T], tuple[PlannedOps, ...]]) -> tuple[T]:
def _apply_fns_worker[T](args: tuple[tuple[T], tuple[PlannedOps, ...]]) -> tuple[T, ...]:
seq, ops = args
return _par_apply_fns(seq, ops)

Expand Down Expand Up @@ -535,7 +535,7 @@ def _apply_fns[T](elements: tuple[T], ops: tuple[PlannedOps, ...]) -> tuple[T, .
return tuple(pipeline) # ty: ignore[invalid-return-type]


def _par_apply_fns[T](elements: tuple[T], ops: tuple[PlannedOps, ...]) -> tuple[T]:
def _par_apply_fns[T](elements: tuple[T], ops: tuple[PlannedOps, ...]) -> tuple[T, ...]:
results = []
for elem in elements:
valid = True
Expand Down
6 changes: 5 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def lt_10(x: float) -> bool:


async def async_is_file(path: Path) -> bool:
return path.is_file()
return path.is_file() # noqa: ASYNC240


async def async_read_text(path: str) -> str:
Expand Down Expand Up @@ -130,6 +130,10 @@ def add(self, a: int, b: int) -> Self:
def cls_raises(self, *_args: tuple, **_kwargs: dict) -> None:
raise ValueError

@safe
def safe_add(self, a: int, b: int) -> int:
return a + b


class ValueLogger:
def __init__(self, values: list | ListProxy | None = None) -> None:
Expand Down
11 changes: 8 additions & 3 deletions tests/test_safe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from danom._result import Err
from danom._result import Err, Ok
from tests.conftest import (
REPO_ROOT,
Adder,
Expand Down Expand Up @@ -65,8 +65,8 @@ def test_traceback():

expected_lines = [
"Traceback (most recent call last):",
' File "./src/danom/_safe.py", line 34, in __call__',
" return Ok(self.func(*args, **kwargs))",
' File "./src/danom/_safe.py", line 31, in wrapper',
" return Ok(func(*args, **kwargs))",
' File "./tests/conftest.py", line 117, in div_zero',
" return x / 0",
"ZeroDivisionError: division by zero",
Expand All @@ -80,3 +80,8 @@ def test_traceback():
missing_lines = [line for line in expected_lines if line not in tb_lines]

assert missing_lines == [], f"lines don't match {tb_lines = }"


def test_safe_on_method():
cls = Adder()
assert cls.safe_add(2, 2) == Ok(4)
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading