wird 1.1.0__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wird-1.1.0 → wird-1.2.0}/PKG-INFO +81 -1
- {wird-1.1.0 → wird-1.2.0}/README.md +80 -0
- {wird-1.1.0 → wird-1.2.0}/pyproject.toml +8 -2
- {wird-1.1.0 → wird-1.2.0}/tests/test_maybe.py +1 -2
- wird-1.2.0/tests/test_result.py +331 -0
- {wird-1.1.0 → wird-1.2.0}/uv.lock +1 -1
- {wird-1.1.0 → wird-1.2.0}/wird/__init__.py +10 -0
- {wird-1.1.0 → wird-1.2.0}/wird/_maybe.py +2 -1
- wird-1.2.0/wird/_result.py +1347 -0
- wird-1.2.0/wird/future_result.py +460 -0
- wird-1.2.0/wird/result.py +452 -0
- {wird-1.1.0 → wird-1.2.0}/.github/workflows/check.yml +0 -0
- {wird-1.1.0 → wird-1.2.0}/.github/workflows/publish.yml +0 -0
- {wird-1.1.0 → wird-1.2.0}/.gitignore +0 -0
- {wird-1.1.0 → wird-1.2.0}/.python-version +0 -0
- {wird-1.1.0 → wird-1.2.0}/Makefile +0 -0
- {wird-1.1.0 → wird-1.2.0}/tests/__init__.py +0 -0
- {wird-1.1.0 → wird-1.2.0}/tests/test_future.py +0 -0
- {wird-1.1.0 → wird-1.2.0}/tests/test_value.py +0 -0
- {wird-1.1.0 → wird-1.2.0}/wird/_future.py +0 -0
- {wird-1.1.0 → wird-1.2.0}/wird/_value.py +0 -0
- {wird-1.1.0 → wird-1.2.0}/wird/future_maybe.py +0 -0
- {wird-1.1.0 → wird-1.2.0}/wird/maybe.py +0 -0
- {wird-1.1.0 → wird-1.2.0}/wird/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wird
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Basic monads for implementing pipe-styled processing
|
|
5
5
|
Project-URL: Repository, https://github.com/katunilya/wird
|
|
6
6
|
Project-URL: Issues, https://github.com/katunilya/wird/issues
|
|
@@ -260,3 +260,83 @@ and returns internally stored `Maybe` instance.
|
|
|
260
260
|
Also in some cases one might need point-free versions of `Maybe` interface methods, so
|
|
261
261
|
one can access them via `maybe` module. For `FutureMaybe` point-free functions one can
|
|
262
262
|
use `future_maybe` module.
|
|
263
|
+
|
|
264
|
+
### `Result`
|
|
265
|
+
|
|
266
|
+
Exception handling is one of the most important tasks in development. We often face
|
|
267
|
+
cases when invocation of some logic can lead to Exception raise. In python default
|
|
268
|
+
handling mechanism is `try` - `except` - `finally` block, which is actually just another
|
|
269
|
+
for of `if` statement.
|
|
270
|
+
|
|
271
|
+
Worst thing about this approach is that commonly in python the only way to know that
|
|
272
|
+
function can raise an exception is documentation (which is not always written or written
|
|
273
|
+
good). There is no explicit mechanism to tell LSP / type checker / linter, that this
|
|
274
|
+
specific function needs exception handling.
|
|
275
|
+
|
|
276
|
+
`Result` monad provides another approach, which is common for Rust and Go developers -
|
|
277
|
+
let's return exceptions instead of raising them. Thus we can explicitly tell that soma
|
|
278
|
+
action can fail and requires edge-case handling.
|
|
279
|
+
|
|
280
|
+
Like `Maybe`, `Result` is just a protocol and has 2 implementations:
|
|
281
|
+
|
|
282
|
+
- `Ok` - container indicating that calculation succeeded
|
|
283
|
+
- `Err` - container indicating that calculation failed
|
|
284
|
+
|
|
285
|
+
Simplest case of using `Result` is division:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
from wird import Result, Ok, Err
|
|
289
|
+
|
|
290
|
+
def try_div(a: int, b: int) -> Result[float, ZeroDivisionError]:
|
|
291
|
+
if b == 0:
|
|
292
|
+
return Err(ZeroDivisionError())
|
|
293
|
+
|
|
294
|
+
return Ok(a / b)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
There we explicitly tell that division operation can lead to failure and even pinpoint
|
|
298
|
+
specific type of error.
|
|
299
|
+
|
|
300
|
+
`Result` provides the following interface:
|
|
301
|
+
|
|
302
|
+
- `Result.unwrap` - extract internally stored value of `Ok` or raise `ErrUnwrapError`
|
|
303
|
+
- `Result.unwrap_or` - extract internally stored value of `Ok` or return other
|
|
304
|
+
- `Result.unwrap_or_else` - extract internally stored value of `Ok` or return closure
|
|
305
|
+
result
|
|
306
|
+
- `Result.unwrap_or_else_async` - same as `Result.unwrap_or_else`, but for async
|
|
307
|
+
closures
|
|
308
|
+
- `Result.unwrap_err` - same as `Result.unwrap`, but for `Err`
|
|
309
|
+
- `Result.unwrap_err_or` - same as `Result.unwrap_err_or`, but for `Err`
|
|
310
|
+
- `Result.unwrap_err_or_else` - same as `Result.unwrap_err_or_else`, but for `Err`
|
|
311
|
+
- `Result.unwrap_err_or_else_async` - same as `Result.unwrap_err_or_else_async`, but for
|
|
312
|
+
`Err`
|
|
313
|
+
- `Result.map` - binding method for `Ok`
|
|
314
|
+
- `Result.map_async` - same as `Result.map`, but for async functions
|
|
315
|
+
- `Result.inspect` - binding side-effect method for `Ok`
|
|
316
|
+
- `Result.inspect_async`- same as `Result.inspect_async`, but for async functions
|
|
317
|
+
- `Result.map_err` - same as `Result.map`, but for `Err`
|
|
318
|
+
- `Result.map_err_async` - same as `Result.map_async`, but for `Err`
|
|
319
|
+
- `Result.inspect_err` - same as `Result.inspect`, but for `Err`
|
|
320
|
+
- `Result.inspect_err_async` - same as `Result.inspect_async`, but for `Err`
|
|
321
|
+
- `Result.and_` - logical AND, replaces current `Result` with passed on `Ok`
|
|
322
|
+
- `Result.and_then` - same as `Result.map`, but for functions returning `Result`
|
|
323
|
+
- `Result.and_then_async` - same as `Result.and_then`, but for async functions
|
|
324
|
+
- `Result.or_` - logical OR, replaces current `Result` with passed on `Err`
|
|
325
|
+
- `Result.or_else` - same as `Result.map_err`, but for functions returning `Result`
|
|
326
|
+
- `Result.or_else_async` - same as `Result.or_else`, but for async functions
|
|
327
|
+
- `Result.is_ok` - `True` on `Ok`
|
|
328
|
+
- `Result.is_ok_and` - `True` on `Ok` and predicate `True`
|
|
329
|
+
- `Result.is_ok_and_async` - same as `Result.is_ok_and`, but for async predicate
|
|
330
|
+
- `Result.is_ok_or` - `True` on `Ok` or `Err` predicate `True`
|
|
331
|
+
- `Result.is_ok_or_async` - same as `Result.is_ok_or`, but for async predicate
|
|
332
|
+
- `Result.is_err` - `True` on `Err`
|
|
333
|
+
- `Result.is_err_and` - `True` on `Err` and predicate `True`
|
|
334
|
+
- `Result.is_err_and_async` - same as `Result.is_err_and`, but for async predicate
|
|
335
|
+
- `Result.is_err_or` - `True` on `Err` or `Ok` predicate `True
|
|
336
|
+
- `Result.is_err_or_async` - same as `result.is_err_or`, but for async predicate
|
|
337
|
+
|
|
338
|
+
In the same manner as with `Maybe` we wird provides:
|
|
339
|
+
|
|
340
|
+
- `FutureResult` as seamless adapter for `Future[Result]`
|
|
341
|
+
- point-free `Result` API in `wird.result` module
|
|
342
|
+
- point-free `FutureResult` API in `wird.future_result` module
|
|
@@ -249,3 +249,83 @@ and returns internally stored `Maybe` instance.
|
|
|
249
249
|
Also in some cases one might need point-free versions of `Maybe` interface methods, so
|
|
250
250
|
one can access them via `maybe` module. For `FutureMaybe` point-free functions one can
|
|
251
251
|
use `future_maybe` module.
|
|
252
|
+
|
|
253
|
+
### `Result`
|
|
254
|
+
|
|
255
|
+
Exception handling is one of the most important tasks in development. We often face
|
|
256
|
+
cases when invocation of some logic can lead to Exception raise. In python default
|
|
257
|
+
handling mechanism is `try` - `except` - `finally` block, which is actually just another
|
|
258
|
+
for of `if` statement.
|
|
259
|
+
|
|
260
|
+
Worst thing about this approach is that commonly in python the only way to know that
|
|
261
|
+
function can raise an exception is documentation (which is not always written or written
|
|
262
|
+
good). There is no explicit mechanism to tell LSP / type checker / linter, that this
|
|
263
|
+
specific function needs exception handling.
|
|
264
|
+
|
|
265
|
+
`Result` monad provides another approach, which is common for Rust and Go developers -
|
|
266
|
+
let's return exceptions instead of raising them. Thus we can explicitly tell that soma
|
|
267
|
+
action can fail and requires edge-case handling.
|
|
268
|
+
|
|
269
|
+
Like `Maybe`, `Result` is just a protocol and has 2 implementations:
|
|
270
|
+
|
|
271
|
+
- `Ok` - container indicating that calculation succeeded
|
|
272
|
+
- `Err` - container indicating that calculation failed
|
|
273
|
+
|
|
274
|
+
Simplest case of using `Result` is division:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
from wird import Result, Ok, Err
|
|
278
|
+
|
|
279
|
+
def try_div(a: int, b: int) -> Result[float, ZeroDivisionError]:
|
|
280
|
+
if b == 0:
|
|
281
|
+
return Err(ZeroDivisionError())
|
|
282
|
+
|
|
283
|
+
return Ok(a / b)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
There we explicitly tell that division operation can lead to failure and even pinpoint
|
|
287
|
+
specific type of error.
|
|
288
|
+
|
|
289
|
+
`Result` provides the following interface:
|
|
290
|
+
|
|
291
|
+
- `Result.unwrap` - extract internally stored value of `Ok` or raise `ErrUnwrapError`
|
|
292
|
+
- `Result.unwrap_or` - extract internally stored value of `Ok` or return other
|
|
293
|
+
- `Result.unwrap_or_else` - extract internally stored value of `Ok` or return closure
|
|
294
|
+
result
|
|
295
|
+
- `Result.unwrap_or_else_async` - same as `Result.unwrap_or_else`, but for async
|
|
296
|
+
closures
|
|
297
|
+
- `Result.unwrap_err` - same as `Result.unwrap`, but for `Err`
|
|
298
|
+
- `Result.unwrap_err_or` - same as `Result.unwrap_err_or`, but for `Err`
|
|
299
|
+
- `Result.unwrap_err_or_else` - same as `Result.unwrap_err_or_else`, but for `Err`
|
|
300
|
+
- `Result.unwrap_err_or_else_async` - same as `Result.unwrap_err_or_else_async`, but for
|
|
301
|
+
`Err`
|
|
302
|
+
- `Result.map` - binding method for `Ok`
|
|
303
|
+
- `Result.map_async` - same as `Result.map`, but for async functions
|
|
304
|
+
- `Result.inspect` - binding side-effect method for `Ok`
|
|
305
|
+
- `Result.inspect_async`- same as `Result.inspect_async`, but for async functions
|
|
306
|
+
- `Result.map_err` - same as `Result.map`, but for `Err`
|
|
307
|
+
- `Result.map_err_async` - same as `Result.map_async`, but for `Err`
|
|
308
|
+
- `Result.inspect_err` - same as `Result.inspect`, but for `Err`
|
|
309
|
+
- `Result.inspect_err_async` - same as `Result.inspect_async`, but for `Err`
|
|
310
|
+
- `Result.and_` - logical AND, replaces current `Result` with passed on `Ok`
|
|
311
|
+
- `Result.and_then` - same as `Result.map`, but for functions returning `Result`
|
|
312
|
+
- `Result.and_then_async` - same as `Result.and_then`, but for async functions
|
|
313
|
+
- `Result.or_` - logical OR, replaces current `Result` with passed on `Err`
|
|
314
|
+
- `Result.or_else` - same as `Result.map_err`, but for functions returning `Result`
|
|
315
|
+
- `Result.or_else_async` - same as `Result.or_else`, but for async functions
|
|
316
|
+
- `Result.is_ok` - `True` on `Ok`
|
|
317
|
+
- `Result.is_ok_and` - `True` on `Ok` and predicate `True`
|
|
318
|
+
- `Result.is_ok_and_async` - same as `Result.is_ok_and`, but for async predicate
|
|
319
|
+
- `Result.is_ok_or` - `True` on `Ok` or `Err` predicate `True`
|
|
320
|
+
- `Result.is_ok_or_async` - same as `Result.is_ok_or`, but for async predicate
|
|
321
|
+
- `Result.is_err` - `True` on `Err`
|
|
322
|
+
- `Result.is_err_and` - `True` on `Err` and predicate `True`
|
|
323
|
+
- `Result.is_err_and_async` - same as `Result.is_err_and`, but for async predicate
|
|
324
|
+
- `Result.is_err_or` - `True` on `Err` or `Ok` predicate `True
|
|
325
|
+
- `Result.is_err_or_async` - same as `result.is_err_or`, but for async predicate
|
|
326
|
+
|
|
327
|
+
In the same manner as with `Maybe` we wird provides:
|
|
328
|
+
|
|
329
|
+
- `FutureResult` as seamless adapter for `Future[Result]`
|
|
330
|
+
- point-free `Result` API in `wird.result` module
|
|
331
|
+
- point-free `FutureResult` API in `wird.future_result` module
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wird"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.0"
|
|
8
8
|
description = "Basic monads for implementing pipe-styled processing"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -36,7 +36,13 @@ asyncio_mode = "auto"
|
|
|
36
36
|
asyncio_default_fixture_loop_scope = "module"
|
|
37
37
|
|
|
38
38
|
[tool.coverage.run]
|
|
39
|
-
omit = [
|
|
39
|
+
omit = [
|
|
40
|
+
"tests/*",
|
|
41
|
+
"wird/maybe.py",
|
|
42
|
+
"wird/future_maybe.py",
|
|
43
|
+
"wird/result.py",
|
|
44
|
+
"wird/future_result.py",
|
|
45
|
+
]
|
|
40
46
|
branch = true
|
|
41
47
|
|
|
42
48
|
[tool.coverage.report]
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from wird import Err, ErrUnwrapError, Future, FutureResult, Ok, OkUnwrapError, Result
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def test_ok_unwraps() -> None:
|
|
9
|
+
r = Ok(1).of_err_type(str)
|
|
10
|
+
|
|
11
|
+
assert r.unwrap() == 1
|
|
12
|
+
assert r.unwrap_or(2) == 1
|
|
13
|
+
assert r.unwrap_or_else(lambda: 2) == 1
|
|
14
|
+
assert await r.unwrap_or_else_async(lambda: Future.from_(2)) == 1
|
|
15
|
+
|
|
16
|
+
with pytest.raises(OkUnwrapError, match="expected Err, got Ok"):
|
|
17
|
+
r.unwrap_err()
|
|
18
|
+
|
|
19
|
+
with pytest.raises(OkUnwrapError, match="on ok"):
|
|
20
|
+
r.unwrap_err(on_ok="on ok")
|
|
21
|
+
|
|
22
|
+
assert r.unwrap_err_or("err") == "err"
|
|
23
|
+
assert r.unwrap_err_or_else(lambda: "err") == "err"
|
|
24
|
+
assert await r.unwrap_err_or_else_async(lambda: Future.from_("err")) == "err"
|
|
25
|
+
|
|
26
|
+
assert await FutureResult.from_(r).unwrap() == 1
|
|
27
|
+
assert await FutureResult.from_(r).unwrap_or(2) == 1
|
|
28
|
+
assert await FutureResult.from_(r).unwrap_or_else(lambda: 2) == 1
|
|
29
|
+
assert (
|
|
30
|
+
await FutureResult.from_(r).unwrap_or_else_async(lambda: Future.from_(2)) == 1
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
with pytest.raises(OkUnwrapError, match="expected Err, got Ok"):
|
|
34
|
+
await FutureResult.from_(r).unwrap_err()
|
|
35
|
+
|
|
36
|
+
with pytest.raises(OkUnwrapError, match="on ok"):
|
|
37
|
+
await FutureResult.from_(r).unwrap_err(on_ok="on ok")
|
|
38
|
+
|
|
39
|
+
assert await FutureResult.from_(r).unwrap_err_or("err") == "err"
|
|
40
|
+
assert await FutureResult.from_(r).unwrap_err_or_else(lambda: "err") == "err"
|
|
41
|
+
assert (
|
|
42
|
+
await FutureResult.from_(r).unwrap_err_or_else_async(
|
|
43
|
+
lambda: Future.from_("err")
|
|
44
|
+
)
|
|
45
|
+
== "err"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def test_err_unwraps() -> None:
|
|
50
|
+
r = Err("err").of_ok_type(int)
|
|
51
|
+
|
|
52
|
+
with pytest.raises(ErrUnwrapError, match="expected Ok, got Err"):
|
|
53
|
+
r.unwrap()
|
|
54
|
+
|
|
55
|
+
with pytest.raises(ErrUnwrapError, match="on err"):
|
|
56
|
+
r.unwrap(on_err="on err")
|
|
57
|
+
|
|
58
|
+
assert r.unwrap_or(2) == 2
|
|
59
|
+
assert r.unwrap_or_else(lambda: 2) == 2
|
|
60
|
+
assert await r.unwrap_or_else_async(lambda: Future.from_(2)) == 2
|
|
61
|
+
|
|
62
|
+
assert r.unwrap_err() == "err"
|
|
63
|
+
assert r.unwrap_err_or("error") == "err"
|
|
64
|
+
assert r.unwrap_err_or_else(lambda: "error") == "err"
|
|
65
|
+
assert await r.unwrap_err_or_else_async(lambda: Future.from_("error")) == "err"
|
|
66
|
+
|
|
67
|
+
with pytest.raises(ErrUnwrapError, match="expected Ok, got Err"):
|
|
68
|
+
await FutureResult.from_(r).unwrap()
|
|
69
|
+
|
|
70
|
+
with pytest.raises(ErrUnwrapError, match="on err"):
|
|
71
|
+
await FutureResult.from_(r).unwrap(on_err="on err")
|
|
72
|
+
|
|
73
|
+
assert await FutureResult.from_(r).unwrap_or(2) == 2
|
|
74
|
+
assert await FutureResult.from_(r).unwrap_or_else(lambda: 2) == 2
|
|
75
|
+
assert (
|
|
76
|
+
await FutureResult.from_(r).unwrap_or_else_async(lambda: Future.from_(2)) == 2
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
assert await FutureResult.from_(r).unwrap_err() == "err"
|
|
80
|
+
assert await FutureResult.from_(r).unwrap_err_or("error") == "err"
|
|
81
|
+
assert await FutureResult.from_(r).unwrap_err_or_else(lambda: "error") == "err"
|
|
82
|
+
assert (
|
|
83
|
+
await FutureResult.from_(r).unwrap_err_or_else_async(
|
|
84
|
+
lambda: Future.from_("error")
|
|
85
|
+
)
|
|
86
|
+
== "err"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@pytest.mark.parametrize(
|
|
91
|
+
("result", "target"),
|
|
92
|
+
[
|
|
93
|
+
(Ok(1), Ok(5)),
|
|
94
|
+
(Err(""), Err("")),
|
|
95
|
+
],
|
|
96
|
+
)
|
|
97
|
+
async def test_result_map(
|
|
98
|
+
result: Result[int, str],
|
|
99
|
+
target: Result[int, str],
|
|
100
|
+
) -> None:
|
|
101
|
+
assert (
|
|
102
|
+
await result.map(operator.add, 1)
|
|
103
|
+
.map_async(lambda x: Future.from_(x + 1))
|
|
104
|
+
.map(operator.add, 1)
|
|
105
|
+
.map_async(lambda x: Future.from_(x + 1))
|
|
106
|
+
== target
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@pytest.mark.parametrize(
|
|
111
|
+
("result", "target"),
|
|
112
|
+
[
|
|
113
|
+
(Ok(""), Ok("")),
|
|
114
|
+
(Err(1), Err(5)),
|
|
115
|
+
],
|
|
116
|
+
)
|
|
117
|
+
async def test_result_map_err(
|
|
118
|
+
result: Result[str, int],
|
|
119
|
+
target: Result[str, int],
|
|
120
|
+
) -> None:
|
|
121
|
+
assert (
|
|
122
|
+
await result.map_err(operator.add, 1)
|
|
123
|
+
.map_err_async(lambda x: Future.from_(x + 1))
|
|
124
|
+
.map_err(operator.add, 1)
|
|
125
|
+
.map_err_async(lambda x: Future.from_(x + 1))
|
|
126
|
+
== target
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.mark.parametrize(
|
|
131
|
+
("result", "target"),
|
|
132
|
+
[
|
|
133
|
+
(Ok({}), Ok({"a": 1, "b": 2, "c": 3, "d": 4})),
|
|
134
|
+
(Err(""), Err("")),
|
|
135
|
+
],
|
|
136
|
+
)
|
|
137
|
+
async def test_result_inspect(
|
|
138
|
+
result: Result[dict, str],
|
|
139
|
+
target: Result[dict, str],
|
|
140
|
+
) -> None:
|
|
141
|
+
async def set_item(data: dict, key: str, value: int) -> None:
|
|
142
|
+
data[key] = value
|
|
143
|
+
|
|
144
|
+
assert (
|
|
145
|
+
await result.inspect(operator.setitem, "a", 1)
|
|
146
|
+
.inspect_async(set_item, "b", 2)
|
|
147
|
+
.inspect(operator.setitem, "c", 3)
|
|
148
|
+
.inspect_async(set_item, "d", 4)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@pytest.mark.parametrize(
|
|
153
|
+
("result", "target"),
|
|
154
|
+
[
|
|
155
|
+
(Err({}), Err({"a": 1, "b": 2, "c": 3, "d": 4})),
|
|
156
|
+
(Ok(""), Ok("")),
|
|
157
|
+
],
|
|
158
|
+
)
|
|
159
|
+
async def test_result_inspect_err(
|
|
160
|
+
result: Result[str, dict],
|
|
161
|
+
target: Result[str, dict],
|
|
162
|
+
) -> None:
|
|
163
|
+
async def set_item(data: dict, key: str, value: int) -> None:
|
|
164
|
+
data[key] = value
|
|
165
|
+
|
|
166
|
+
assert (
|
|
167
|
+
await result.inspect_err(operator.setitem, "a", 1)
|
|
168
|
+
.inspect_err_async(set_item, "b", 2)
|
|
169
|
+
.inspect_err(operator.setitem, "c", 3)
|
|
170
|
+
.inspect_err_async(set_item, "d", 4)
|
|
171
|
+
) == target
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
async def test_ok_predicates() -> None:
|
|
175
|
+
r = Ok(1).of_err_type(str)
|
|
176
|
+
|
|
177
|
+
assert r.is_ok()
|
|
178
|
+
assert r.is_ok_and(lambda x: x % 2 == 1)
|
|
179
|
+
assert r.is_ok_or(lambda x: len(x) > 10)
|
|
180
|
+
assert await r.is_ok_and_async(lambda x: Future.from_(x % 2 == 1))
|
|
181
|
+
assert await r.is_ok_or_async(lambda x: Future.from_(len(x) > 10))
|
|
182
|
+
|
|
183
|
+
assert not r.is_err()
|
|
184
|
+
assert not r.is_err_and(lambda x: len(x) > 10)
|
|
185
|
+
assert r.is_err_or(lambda x: x % 2 == 1)
|
|
186
|
+
assert not await r.is_err_and_async(lambda x: Future.from_(len(x) > 10))
|
|
187
|
+
assert await r.is_err_or_async(lambda x: Future.from_(x % 2 == 1))
|
|
188
|
+
|
|
189
|
+
assert await FutureResult.from_(r).is_ok()
|
|
190
|
+
assert await FutureResult.from_(r).is_ok_and(lambda x: x % 2 == 1)
|
|
191
|
+
assert await FutureResult.from_(r).is_ok_or(lambda x: len(x) > 10)
|
|
192
|
+
assert await FutureResult.from_(r).is_ok_and_async(
|
|
193
|
+
lambda x: Future.from_(x % 2 == 1)
|
|
194
|
+
)
|
|
195
|
+
assert await FutureResult.from_(r).is_ok_or_async(
|
|
196
|
+
lambda x: Future.from_(len(x) > 10)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
assert not await FutureResult.from_(r).is_err()
|
|
200
|
+
assert not await FutureResult.from_(r).is_err_and(lambda x: len(x) > 10)
|
|
201
|
+
assert await FutureResult.from_(r).is_err_or(lambda x: x % 2 == 1)
|
|
202
|
+
assert not await FutureResult.from_(r).is_err_and_async(
|
|
203
|
+
lambda x: Future.from_(len(x) > 10)
|
|
204
|
+
)
|
|
205
|
+
assert await FutureResult.from_(r).is_err_or_async(
|
|
206
|
+
lambda x: Future.from_(x % 2 == 1)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
async def test_err_predicates() -> None:
|
|
211
|
+
r = Err(1).of_ok_type(str)
|
|
212
|
+
|
|
213
|
+
assert not r.is_ok()
|
|
214
|
+
assert not r.is_ok_and(lambda x: len(x) > 10)
|
|
215
|
+
assert r.is_ok_or(lambda x: x % 2 == 1)
|
|
216
|
+
assert not await r.is_ok_and_async(lambda x: Future.from_(len(x) > 10))
|
|
217
|
+
assert await r.is_ok_or_async(lambda x: Future.from_(x % 2 == 1))
|
|
218
|
+
|
|
219
|
+
assert r.is_err()
|
|
220
|
+
assert r.is_err_and(lambda x: x % 2 == 1)
|
|
221
|
+
assert r.is_err_or(lambda x: len(x) > 10)
|
|
222
|
+
assert await r.is_err_and_async(lambda x: Future.from_(x % 2 == 1))
|
|
223
|
+
assert await r.is_err_or_async(lambda x: Future.from_(len(x) > 10))
|
|
224
|
+
|
|
225
|
+
assert not await FutureResult.from_(r).is_ok()
|
|
226
|
+
assert not await FutureResult.from_(r).is_ok_and(lambda x: len(x) > 10)
|
|
227
|
+
assert await FutureResult.from_(r).is_ok_or(lambda x: x % 2 == 1)
|
|
228
|
+
assert not await FutureResult.from_(r).is_ok_and_async(
|
|
229
|
+
lambda x: Future.from_(len(x) > 10)
|
|
230
|
+
)
|
|
231
|
+
assert await FutureResult.from_(r).is_ok_or_async(
|
|
232
|
+
lambda x: Future.from_(x % 2 == 1)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
assert await FutureResult.from_(r).is_err()
|
|
236
|
+
assert await FutureResult.from_(r).is_err_and(lambda x: x % 2 == 1)
|
|
237
|
+
assert await FutureResult.from_(r).is_err_or(lambda x: len(x) > 10)
|
|
238
|
+
assert await FutureResult.from_(r).is_err_and_async(
|
|
239
|
+
lambda x: Future.from_(x % 2 == 1)
|
|
240
|
+
)
|
|
241
|
+
assert await FutureResult.from_(r).is_err_or_async(
|
|
242
|
+
lambda x: Future.from_(len(x) > 10)
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
async def test_ok_and() -> None:
|
|
247
|
+
r = Ok(1).of_err_type(str)
|
|
248
|
+
|
|
249
|
+
assert r.and_(Ok(2)) == Ok(2)
|
|
250
|
+
assert r.and_(Err("")) == Err("")
|
|
251
|
+
assert r.and_then(lambda _: Ok(2)) == Ok(2)
|
|
252
|
+
assert r.and_then(lambda _: Err("")) == Err("")
|
|
253
|
+
assert await r.and_then_async(lambda _: Future.from_(Ok(2))) == Ok(2)
|
|
254
|
+
assert await r.and_then_async(lambda _: Future.from_(Err(""))) == Err("")
|
|
255
|
+
|
|
256
|
+
assert await FutureResult.from_(r).and_(Ok(2)) == Ok(2)
|
|
257
|
+
assert await FutureResult.from_(r).and_(Err("")) == Err("")
|
|
258
|
+
assert await FutureResult.from_(r).and_then(lambda _: Ok(2)) == Ok(2)
|
|
259
|
+
assert await FutureResult.from_(r).and_then(lambda _: Err("")) == Err("")
|
|
260
|
+
assert await FutureResult.from_(r).and_then_async(
|
|
261
|
+
lambda _: Future.from_(Ok(2))
|
|
262
|
+
) == Ok(2)
|
|
263
|
+
assert await FutureResult.from_(r).and_then_async(
|
|
264
|
+
lambda _: Future.from_(Err(""))
|
|
265
|
+
) == Err("")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
async def test_ok_or() -> None:
|
|
269
|
+
r = Ok(1).of_err_type(str)
|
|
270
|
+
|
|
271
|
+
assert r.or_(Ok(2)) == Ok(1)
|
|
272
|
+
assert r.or_(Err("")) == Ok(1)
|
|
273
|
+
assert r.or_else(lambda _: Ok(2)) == Ok(1)
|
|
274
|
+
assert r.or_else(lambda _: Err("")) == Ok(1)
|
|
275
|
+
assert await r.or_else_async(lambda _: Future.from_(Ok(2))) == Ok(1)
|
|
276
|
+
assert await r.or_else_async(lambda _: Future.from_(Err(""))) == Ok(1)
|
|
277
|
+
|
|
278
|
+
assert await FutureResult.from_(r).or_(Ok(2)) == Ok(1)
|
|
279
|
+
assert await FutureResult.from_(r).or_(Err("")) == Ok(1)
|
|
280
|
+
assert await FutureResult.from_(r).or_else(lambda _: Ok(2)) == Ok(1)
|
|
281
|
+
assert await FutureResult.from_(r).or_else(lambda _: Err("")) == Ok(1)
|
|
282
|
+
assert await FutureResult.from_(r).or_else_async(
|
|
283
|
+
lambda _: Future.from_(Ok(2))
|
|
284
|
+
) == Ok(1)
|
|
285
|
+
assert await FutureResult.from_(r).or_else_async(
|
|
286
|
+
lambda _: Future.from_(Err(""))
|
|
287
|
+
) == Ok(1)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
async def test_err_and() -> None:
|
|
291
|
+
r = Err(1).of_ok_type(str)
|
|
292
|
+
|
|
293
|
+
assert r.and_(Ok("ok")) == Err(1)
|
|
294
|
+
assert r.and_(Err(2)) == Err(1)
|
|
295
|
+
assert r.and_then(lambda _: Ok("ok")) == Err(1)
|
|
296
|
+
assert r.and_then(lambda _: Err(2)) == Err(1)
|
|
297
|
+
assert await r.and_then_async(lambda _: Future.from_(Ok("ok"))) == Err(1)
|
|
298
|
+
assert await r.and_then_async(lambda _: Future.from_(Err(2))) == Err(1)
|
|
299
|
+
|
|
300
|
+
assert await FutureResult.from_(r).and_(Ok("ok")) == Err(1)
|
|
301
|
+
assert await FutureResult.from_(r).and_(Err(2)) == Err(1)
|
|
302
|
+
assert await FutureResult.from_(r).and_then(lambda _: Ok("ok")) == Err(1)
|
|
303
|
+
assert await FutureResult.from_(r).and_then(lambda _: Err(2)) == Err(1)
|
|
304
|
+
assert await FutureResult.from_(r).and_then_async(
|
|
305
|
+
lambda _: Future.from_(Ok("ok"))
|
|
306
|
+
) == Err(1)
|
|
307
|
+
assert await FutureResult.from_(r).and_then_async(
|
|
308
|
+
lambda _: Future.from_(Err(2))
|
|
309
|
+
) == Err(1)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
async def test_err_or() -> None:
|
|
313
|
+
r = Err(1).of_ok_type(str)
|
|
314
|
+
|
|
315
|
+
assert r.or_(Ok("ok")) == Ok("ok")
|
|
316
|
+
assert r.or_(Err(2)) == Err(2)
|
|
317
|
+
assert r.or_else(lambda _: Ok("ok")) == Ok("ok")
|
|
318
|
+
assert r.or_else(lambda _: Err(2)) == Err(2)
|
|
319
|
+
assert await r.or_else_async(lambda _: Future.from_(Ok("ok"))) == Ok("ok")
|
|
320
|
+
assert await r.or_else_async(lambda _: Future.from_(Err(2))) == Err(2)
|
|
321
|
+
|
|
322
|
+
assert await FutureResult.from_(r).or_(Ok("ok")) == Ok("ok")
|
|
323
|
+
assert await FutureResult.from_(r).or_(Err(2)) == Err(2)
|
|
324
|
+
assert await FutureResult.from_(r).or_else(lambda _: Ok("ok")) == Ok("ok")
|
|
325
|
+
assert await FutureResult.from_(r).or_else(lambda _: Err(2)) == Err(2)
|
|
326
|
+
assert await FutureResult.from_(r).or_else_async(
|
|
327
|
+
lambda _: Future.from_(Ok("ok"))
|
|
328
|
+
) == Ok("ok")
|
|
329
|
+
assert await FutureResult.from_(r).or_else_async(
|
|
330
|
+
lambda _: Future.from_(Err(2))
|
|
331
|
+
) == Err(2)
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
from . import maybe, future_maybe # noqa
|
|
2
|
+
from . import result, future_result
|
|
2
3
|
from ._future import Future
|
|
3
4
|
from ._maybe import Empty, EmptyUnwrapError, FutureMaybe, Maybe, Some
|
|
4
5
|
from ._value import Value
|
|
6
|
+
from ._result import Result, Err, Ok, FutureResult, OkUnwrapError, ErrUnwrapError
|
|
5
7
|
|
|
6
8
|
__all__ = (
|
|
7
9
|
"Empty",
|
|
8
10
|
"EmptyUnwrapError",
|
|
11
|
+
"Err",
|
|
12
|
+
"ErrUnwrapError",
|
|
9
13
|
"Future",
|
|
10
14
|
"FutureMaybe",
|
|
15
|
+
"FutureResult",
|
|
11
16
|
"Maybe",
|
|
17
|
+
"Ok",
|
|
18
|
+
"OkUnwrapError",
|
|
19
|
+
"Result",
|
|
12
20
|
"Some",
|
|
13
21
|
"Value",
|
|
14
22
|
"future_maybe",
|
|
23
|
+
"future_result",
|
|
15
24
|
"maybe",
|
|
25
|
+
"result",
|
|
16
26
|
)
|
|
@@ -6,6 +6,7 @@ from typing import (
|
|
|
6
6
|
Awaitable,
|
|
7
7
|
Callable,
|
|
8
8
|
Concatenate,
|
|
9
|
+
Generator,
|
|
9
10
|
NoReturn,
|
|
10
11
|
Protocol,
|
|
11
12
|
Type,
|
|
@@ -653,7 +654,7 @@ class FutureMaybe[T]:
|
|
|
653
654
|
def from_[V](value: Maybe[V]) -> FutureMaybe[V]:
|
|
654
655
|
return FutureMaybe(f.Future.from_(value))
|
|
655
656
|
|
|
656
|
-
def __await__(self) ->
|
|
657
|
+
def __await__(self) -> Generator[Any, Any, Maybe[T]]:
|
|
657
658
|
return self.internal.__await__()
|
|
658
659
|
|
|
659
660
|
@overload
|