Skip to content

Commit 0952cda

Browse files
committed
v0.3.0
1 parent e2447f6 commit 0952cda

File tree

3 files changed

+269
-136
lines changed

3 files changed

+269
-136
lines changed

README.md

Lines changed: 180 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,39 @@ git clone https://github.com/ScottBoyce-Python/ResultContainer.git
3131
```
3232
then rename the file `ResultContainer/__init__.py` to `ResultContainer/ResultContainer.py` and move `ResultContainer.py` to wherever you want to use it.
3333

34+
35+
3436
## Variants
3537

3638
```python
3739
# Result is the main class and Ok and Err are constructors.
38-
from ResultContainer import Result, Ok, Err
40+
from ResultContainer import Result, Ok, Err, ResultErr
3941
```
4042

4143
- `Ok(value)`
42-
- `value` is wrapped within an `Ok`.
44+
- `value` is any object to be wrapped within an `Ok`.
4345
- Constructor: `Result.as_Ok(value)`
4446
- `Result.Ok` attribute returns the wrapped `value`
4547
- Can never wrap a `ResultErr` instance (it will just be converted to an `Err(value)`).
4648

4749
- `Err(e)`
48-
- `e` is wrapped within an `Err`, and `type(e) is ResultErr`.
50+
- `e` is any object to be wrapped within an `Err`.
51+
- `e` is stored as `ResultErr` Exception object.
52+
- If `not isinstance(e, ResultErr)`, then `e = ResultErr(e)`.
4953
- Constructor: `Result.as_Err(error_msg)`
5054
- `Result.Err` attribute returns the wrapped `e`.
5155

5256
### Properties of the `Result` Variants
5357

5458
#### `Err(e)`:
5559

56-
- Represents a failure (error-state) and contains `e` as a `ResultErr` object that stores error messages and traceback information.
60+
- Represents a failure (error-state).
61+
- `e` is a `ResultErr` object that stores error messages and traceback information.
5762

58-
- Can be initialized with `Err(error_msg)`
63+
- If `e` is another type, it is converted to a `ResultErr`.
64+
That is, given `Err(e)` and `not isinstance(e, ResultErr)` becomes `Err( ResultErr(e) )`.
65+
66+
- Can be initialized with `Err(error_msg)`, where `error_msg` can be any object (typically a str)
5967
- `Err(e)`    syntactic-sugar for    `Result.as_Err(e)`
6068

6169
- If an `Ok(value)` operation fails, then it is converted to an `Err(e)`, where `e` stores the error message.
@@ -94,14 +102,79 @@ from ResultContainer import Result, Ok, Err
94102
- `Err(e1) < Err(e2) ``False`
95103
- `Err(e1) <= Err(e2) ``True`
96104

97-
## Initialization
105+
106+
107+
## ResultErr Class
108+
109+
The `ResultErr` class is a custom exception class for error handling in the `Result` object. The `ResultErr` class captures error messages and optional traceback information. Its primary use is for identifying when a `Result` instance is an `Err` variant, which is handled automatically. It should not be necessary to use the ResultErr class directly, but select attributes and methods are presented here for background information.
110+
111+
### Initialization
112+
113+
```python
114+
# All arguments are optional
115+
from ResultContainer import ResultErr
116+
117+
# Main object signature:
118+
e = ResultErr(msg="", add_traceback=True, max_messages=20)
119+
# msg (Any, optional): Error message(s) to initialize with.
120+
# `str(msg)` is the message that is stored.
121+
# If msg is a Sequence, then each item in the Sequence is
122+
# appended as str(item) to the error messages.
123+
# Default is "", to disable error status.
124+
# add_traceback (bool, optional): If True, then traceback information is added to the message.
125+
# max_messages (int, optional): The maximum number of error messages to store.
126+
# After this, all additional messages are ignored. Default is 20.
127+
```
128+
129+
### Attributes and Methods
130+
131+
These are select attributes and methods built into `Result` object.
132+
133+
#### Attributes
134+
135+
```
136+
size (int): Returns the number of error messages.
137+
138+
is_Ok (bool): Returns False if in error status (ie, size == 0).
139+
is_Err (bool): Returns True if in error status (ie, size > 0).
140+
141+
Err_msg (list[str]): List of the error messages that have been added.
142+
Err_traceback (list[list[str]]): List of lists that contains the traceback information for each error message.
143+
```
144+
145+
#### Methods
146+
147+
```
148+
append(msg, add_traceback=True):
149+
Append an error message to the instance.
150+
151+
raises(add_traceback=False, error_msg=""):
152+
Raise a ResultErr exception if `size > 0`.
153+
`error_msg` is an optional note to append to the ResultErr.
154+
If not exception is raised, then returns itself.
155+
156+
str(sep=" | ", as_repr=True, add_traceback=False):
157+
Returns a string representation of the error messages and traceback information.
158+
If as_repr is True error messages are be printed inline (repr version),
159+
while False writes out traceback and error messages over multiple lines (str version).
160+
For general use, it is recomended to use the default values.
161+
162+
copy():
163+
Return a copy of the current ResultErr object.
164+
```
165+
166+
167+
168+
## Result Class
169+
170+
### Initialization
98171

99172
```python
100173
# Only the first argument is required for all constructors
101174
from ResultContainer import Result, Ok, Err
102175

103176
# Main object signature:
104-
res = Result(value, success, error_msg, add_traceback, deepcopy) # Construct either Ok or Er
177+
res = Result(value, success, error_msg, add_traceback, deepcopy) # Construct either Ok or Err
105178

106179
# Classmethod signatures:
107180
res = Result.as_Ok(value, deepcopy) # Construct Ok variant
@@ -129,11 +202,11 @@ res = Err(error_msg, add_traceback) # Construct Er
129202
#
130203
```
131204

132-
## Attributes and Methods
205+
### Attributes and Methods
133206

134207
These are select attributes and methods built into `Result` object. For a full listing please see the Result docstr.
135208

136-
### Attributes
209+
#### Attributes
137210

138211
```
139212
is_Ok (bool): True if the result is a success.
@@ -156,7 +229,7 @@ Err_traceback (list[list[str]]):
156229
For the Err(e) variant, returns list of traceback lines.
157230
```
158231

159-
### Methods
232+
#### Methods
160233

161234
```
162235
raises(add_traceback=False, error_msg=""):
@@ -239,14 +312,40 @@ copy(deepcopy=False):
239312
240313
```
241314

242-
243-
315+
244316

245317
## Usage
246318

247319
Below are examples showcasing how to create and interact with a `ResultContainer`.
248320

249-
### Creating a Result
321+
### ResultErr Initialization
322+
323+
```python
324+
from ResultContainer import ResultErr
325+
326+
# Initialize ResultErr instances
327+
a = ResultErr("Error Message")
328+
b = ResultErr(5)
329+
c = ResultErr(["Error Message 1", "Error Message 2"])
330+
331+
print(a.str()) # ResultErr("Error Message")
332+
print(b.str()) # ResultErr("5")
333+
print(c.str()) # ResultErr("Error Message 1 | Error Message 2")
334+
335+
raise c # Raises the following exception:
336+
337+
# Traceback (most recent call last):
338+
# File "example.py", line 12, in <module>
339+
# raise c
340+
# ResultContainer.ResultErr:
341+
# File "example.py", line 6, in <module>
342+
# c = ResultErr(["Error Message 1", "Error Message 2"])
343+
#
344+
# <Error Message 1>
345+
# <Error Message 2>
346+
```
347+
348+
### Result Initialization
250349

251350
```python
252351
from ResultContainer import Result, Ok, Err, ResultErr
@@ -272,12 +371,14 @@ a = Err(5)
272371
# A ResultErr instance is always wrapped by Err ---------------------------------------------------------
273372
e = ResultErr("Error Message") # e is an instance of ResultErr
274373

275-
a1 = Result(e, success=True) # a1 == a2 == a3 == Err(e)
276-
a2 = Result.as_Ok(5)
374+
a1 = Result(e, success=True) # a1 == a2 == a3 == Err(e); success is ignored because isinstance(e, ResultErr)
375+
a2 = Result.as_Ok(e)
277376
a3 = Ok(e)
278377
```
279378

280-
### Math Operations with a Result
379+
380+
381+
### Math Operations
281382

282383
```python
283384
from ResultContainer import Ok
@@ -299,7 +400,9 @@ z = x + y # z = Ok([1, 2, 3, 4, 5, 6, 7])
299400

300401
```
301402

302-
### Wrapping Objects
403+
404+
405+
### Wrapping `datetime Example
303406

304407
```python
305408
from ResultContainer import Result, Ok, Err
@@ -323,6 +426,32 @@ bad_dt = dt + timedelta(days=10000) # bad_dt = Err("a + b resulted in an
323426
bad_dt.raises() # raises a ResultErr exception
324427
```
325428

429+
430+
431+
### Passing Functions and Chaining Operations
432+
433+
```python
434+
from ResultContainer import Result, Ok, Err
435+
from math import sqrt
436+
# to use an external function, like sqrt
437+
# It must be passed to either apply or map or extracted with expect.
438+
# apply converts Ok to Err if the func fails, while map raises an exception.
439+
a = Ok(9) # Ok(9)
440+
b = a.apply(sqrt) # Ok(3.0)
441+
c = Ok(-9) # Ok(-9)
442+
d = c.apply(sqrt) # Err("Result.apply exception. | ValueError: math domain error")
443+
e = sqrt(c.expect()) # raises an error
444+
445+
plus1 = lambda x: x + 1
446+
a = Ok(5)
447+
b = Ok(0)
448+
c = (a / b).map_or(10, plus1).map_or(20, plus1).map_or(30, plus1) # c = Err() -> Ok(10) -> Ok(11) -> Ok(12)
449+
d = (a / 0).map_or(10, plus1).map_or(20, plus1).map_or(30, plus1) # d = Err() -> Ok(10) -> Ok(11) -> Ok(12)
450+
451+
```
452+
453+
454+
326455
### Raising Errors
327456

328457
```python
@@ -337,67 +466,51 @@ x /= 0 # x = Err("a /= b resulted in an Exception. | ZeroDivisionError
337466
y = x.raises() # Raises the following exception:
338467

339468
# Traceback (most recent call last):
469+
# File "example.py", line 9, in <module>
470+
# y = x.raises() # Raises the following exception:
471+
# ^^^^^^^^^^
472+
# File "ResultContainer\__init__.py", line 882, in raises
473+
# raise self._val # Result.Err variant raises an exception
474+
# ^^^^^^^^^^^^^^^
475+
# ResultContainer.ResultErr:
340476
# File "example.py", line 7, in <module>
341-
# x.raises()
342-
# ^^^^^^^^^^
343-
# File "ResultContainer/ResultContainer.py", line 957, in raises
344-
# raise self._Err
345-
# ResultErr:
346-
# [1] a /= b resulted in an Exception.
347-
# [12] ZeroDivisionError: division by zero
477+
# x /= 0 # x = Err("a /= b resulted in an Exception. | ZeroDivisionError: division by zero")
478+
# File "ResultContainer\__init__.py", line 1313, in __itruediv__
479+
# return self._operator_overload_error(e, op, True)
480+
#
481+
# <a /= b resulted in an Exception.>
482+
# <ZeroDivisionError: division by zero>
483+
# <Result.raises() on Err>
348484
```
349485

350486

351487

352-
353488
```python
354-
1 | from ResultContainer import Result, Ok, Err
355-
2 | from datetime import datetime, timedelta
356-
3 |
357-
4 | dt = Result(datetime(9999, 12, 31))
358-
5 |
359-
6 | bad_dt = dt + timedelta(days=10000)
360-
7 |
361-
8 | bad_dt.raises()
362-
# Raises the following exception.
363-
# Note the exception says it occured on `line 6` despite being called on `line 8`
489+
from ResultContainer import Result, Ok, Err
490+
from datetime import datetime, timedelta
364491

492+
dt = Result(datetime(9999, 12, 31))
493+
494+
bad_dt = dt + timedelta(days=10000)
495+
496+
bad_dt.raises() # Raises the following exception:
365497
# Traceback (most recent call last):
366498
# File "example.py", line 8, in <module>
367-
# bad_dt.raises()
368-
# File "ResultContainer/ResultContainer.py", line 957, in raises
369-
# raise self._Err
370-
# ResultErr:
371-
# File "ResultContainer/example.py", line 6, in <module>
499+
# bad_dt.raises()
500+
# ^^^^^^^^^^^^^^^
501+
# File "ResultContainer\__init__.py", line 882, in raises
502+
# raise self._val # Result.Err variant raises an exception
503+
# ^^^^^^^^^^^^^^^
504+
# ResultContainer.ResultErr:
505+
# File "example.py", line 6, in <module>
372506
# bad_dt = dt + timedelta(days=10000)
373-
#
374-
# [1] a + b resulted in an Exception.
375-
# [12] OverflowError: date value out of range
507+
#
508+
# <a + b resulted in an Exception.>
509+
# <OverflowError: date value out of range>
510+
# <Result.raises() on Err>
376511
```
377512

378-
### Passing Functions and Chaining Operations
379-
380-
```python
381-
from ResultContainer import Result, Ok, Err
382-
from math import sqrt
383-
# to use an external function, like sqrt
384-
# It must be passed to either apply or map or extracted with expect.
385-
# apply converts Ok to Err if the func fails, while map raises an exception.
386-
a = Ok(9) # Ok(9)
387-
b = a.apply(sqrt) # Ok(3.0)
388-
c = Ok(-9) # Ok(-9)
389-
d = c.apply(sqrt) # Err("Result.apply exception. | ValueError: math domain error")
390-
e = sqrt(c.expect()) # raises an error
391-
392-
plus1 = lambda x: x + 1
393-
a = Ok(5)
394-
b = Ok(0)
395-
c = (a / b).map_or(10, plus1).map_or(20, plus1).map_or(30, plus1) # c = Err() -> Ok(10) -> Ok(11) -> Ok(12)
396-
d = (a / 0).map_or(10, plus1).map_or(20, plus1).map_or(30, plus1) # d = Err() -> Ok(10) -> Ok(11) -> Ok(12)
397-
398-
```
399-
400-
513+
401514

402515
## Testing
403516

@@ -413,7 +526,7 @@ pytest # run all tests, note options are set in the pyproject.toml file
413526

414527
Note, that the [pyproject.toml](pyproject.toml) contains the flags used for pytest.
415528

416-
529+
417530

418531
## License
419532

0 commit comments

Comments
 (0)