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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wird
3
- Version: 1.1.0
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.1.0"
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 = ["tests/*", "wird/maybe.py", "wird/future_maybe.py"]
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]
@@ -2,8 +2,7 @@ import operator
2
2
 
3
3
  import pytest
4
4
 
5
- from wird import Empty, Future, Maybe, Some
6
- from wird._maybe import EmptyUnwrapError, FutureMaybe
5
+ from wird import Empty, EmptyUnwrapError, Future, FutureMaybe, Maybe, Some
7
6
 
8
7
 
9
8
  async def test_some_unwraps() -> None:
@@ -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)
@@ -310,7 +310,7 @@ wheels = [
310
310
 
311
311
  [[package]]
312
312
  name = "wird"
313
- version = "1.1.0"
313
+ version = "1.2.0"
314
314
  source = { editable = "." }
315
315
 
316
316
  [package.dev-dependencies]
@@ -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) -> f.Generator[Any, Any, Maybe[T]]:
657
+ def __await__(self) -> Generator[Any, Any, Maybe[T]]:
657
658
  return self.internal.__await__()
658
659
 
659
660
  @overload