Skip to content
This repository was archived by the owner on Jan 31, 2026. It is now read-only.
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
19 changes: 17 additions & 2 deletions tests/test_result.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import Any, Callable
from typing import Any, Callable, Iterable

import pytest

from tests.utils import add, add_async, is_even, is_even_async, set_async
from tibia import mapping
from tibia.result import Err, Ok, Result, match_ok, safe, safe_from, wraps
from tibia.result import Err, Ok, Result, match_ok, safe, safe_from, safe_iterate, wraps


@pytest.mark.parametrize(
Expand Down Expand Up @@ -455,3 +455,18 @@ def test_wraps_new():

assert isinstance(result, Ok)
assert result == 2


def test_safe_iterate():
@safe_iterate
def gen() -> Iterable[int]:
yield 1
raise Exception()
yield 2

items = list(gen())

assert len(items) == 2
assert isinstance(items[0], Ok)
assert items[0].unwrap() == 1
assert isinstance(items[1], Err)
22 changes: 19 additions & 3 deletions tibia/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import functools
import warnings
from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Concatenate, cast
from typing import Any, Awaitable, Callable, Concatenate, Iterable, cast

from tibia import future_result as fr
from tibia.future import Future
Expand Down Expand Up @@ -202,8 +202,7 @@ def inspect_err_async[**P](
def wraps[**P, R](func: Callable[P, R]) -> Callable[P, Result[R, Exception]]:
warnings.simplefilter("always", DeprecationWarning)
warnings.warn(
"Result.wraps will be deprecated in tibia@3.0.0, "
"use result.wraps instead",
"Result.wraps will be deprecated in tibia@3.0.0, use result.wraps instead",
DeprecationWarning,
)
warnings.simplefilter("default", DeprecationWarning)
Expand Down Expand Up @@ -589,3 +588,20 @@ def _safe(*args: P.args, **kwargs: P.kwargs) -> Result[T, Exception]:
return Err(exc)

return _safe


def safe_iterate[**P, T](
fn: Callable[P, Iterable[T]],
) -> Callable[P, Iterable[Result[T, Exception]]]:
@functools.wraps(fn)
def _safe_iterate(
*args: P.args,
**kwargs: P.kwargs,
) -> Iterable[Result[T, Exception]]:
try:
for item in fn(*args, **kwargs): # pragma: no cover
yield Ok(item)
except Exception as exc:
yield Err(exc)

return _safe_iterate