sain.error
Interfaces for working with Errors.
This exposes one abstract interface, Error that other errors can implement and use as an argument to match upon.
Usually this error is returned from a Result[T, Error] object.
Those errors can be converted into RuntimeError exceptions by calling sain.Result.unwrap and sain.Option.unwrap.
For an example
# Read the env variable, raises `RuntimeError` if it is not present.
path: Option[str] = Some(os.environ.get('SOME_PATH')).unwrap()
1# BSD 3-Clause License 2# 3# Copyright (c) 2022-Present, nxtlo 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, this 10# list of conditions and the following disclaimer. 11# 12# * Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# * Neither the name of the copyright holder nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30"""Interfaces for working with Errors. 31 32This exposes one abstract interface, `Error` that other errors can implement and use as an argument to match upon. 33 34Usually this error is returned from a `Result[T, Error]` object. 35 36Those errors can be converted into `RuntimeError` exceptions by calling `sain.Result.unwrap` and `sain.Option.unwrap`. 37 38For an example 39 40```py 41# Read the env variable, raises `RuntimeError` if it is not present. 42path: Option[str] = Some(os.environ.get('SOME_PATH')).unwrap() 43``` 44""" 45 46from __future__ import annotations 47 48__all__ = ("Error", "catch_unwind") 49 50import typing 51 52from sain import result as _result 53from sain.macros import rustc_diagnostic_item 54 55from . import option as _option 56from .convert import ToString 57 58if typing.TYPE_CHECKING: 59 from collections.abc import Callable 60 61 from sain import Option 62 from sain import result as _result 63 64R = typing.TypeVar("R", covariant=True) 65 66 67@rustc_diagnostic_item("Error") 68@typing.runtime_checkable 69class Error(ToString, typing.Protocol): 70 """`Error` is an interface usually used for values that returns `sain.Result[T, E]` 71 72 where `E` is an implementation of this interface. 73 74 Example 75 ------- 76 ```py 77 import requests 78 import http 79 from dataclasses import dataclass 80 81 from sain import Error 82 from sain import Result, Ok, Err 83 84 # an http error. 85 @dataclass 86 class HTTPError(Error): 87 response: requests.Response 88 kind: http.HTTPStatus 89 message: str = "" 90 91 def description(self) -> str: 92 return f"HTTP Error [{self.response.status_code}, {self.kind}] for {self.response.url}" 93 94 # A simple request that handles [404] responses. 95 def request(url: str, uid: int) -> Result[requests.Response, HTTPError]: 96 response = requests.get(url, json={"user_id": uid}) 97 if response.status_code == 404: 98 return Err( 99 HTTPError( 100 response, 101 kind=http.HTTPStatus.NOT_FOUND, 102 message=f"Resource not found for user_id {uid}", 103 ) 104 ) 105 return Ok(response) 106 107 # Execute the request 108 match request("some-url.com", 0): 109 case Ok(response): 110 # Deal with the response 111 ... 112 case Err(why): 113 # Deal with the error. 114 print(why) 115 116 ``` 117 """ 118 119 __slots__ = ("message",) 120 121 def __init__(self, message: str = "") -> None: 122 self.message = message 123 """A basic error message.""" 124 125 def source(self) -> Option[type[Error]]: 126 """The source of this error, if any.""" 127 return _option.NOTHING 128 129 def description(self) -> str: 130 """Context for this error.""" 131 return "" 132 133 def to_string(self) -> str: 134 return self.__repr__() 135 136 def __repr__(self) -> str: 137 source = None if (src := self.source()).is_none() else src 138 return ( 139 f'{type(self).__qualname__}(message: "{self.message}, source: {source!r})' 140 ) 141 142 def __str__(self) -> str: 143 return self.message 144 145 # An error is always falsy. 146 def __bool__(self) -> typing.Literal[False]: 147 return False 148 149 150@rustc_diagnostic_item("catch_unwind") 151def catch_unwind(fn: Callable[[], R]) -> _result.Result[R, BaseException]: 152 """Invokes a closure, capturing exceptions if any one raised. 153 154 This function will return `Ok` with the closure's result if it doesn't raise any exceptions, 155 otherwise it will return `Err(cause)` with the exception. 156 157 You can treat this as an inline try-except block. 158 159 Notes 160 ----- 161 This function also catch exceptions such as `KeyboardInterrupt` and `SystemExit`, 162 so try to use it with extreme caution. 163 164 Example 165 ------- 166 ```py 167 from sain.error import catch_unwind 168 169 def request() -> str: 170 return requests.get("some url").text 171 172 def fetch() -> str: 173 raise RuntimeError from None 174 175 result = catch_unwind(request) 176 assert result.is_ok() 177 178 result = catch_unwind(fetch) 179 assert result.is_err() 180 ``` 181 182 Parameters 183 ---------- 184 fn: `Callable[[], R]` 185 The function to run. 186 187 Returns 188 ------- 189 `Result[R, BaseException]` 190 Returns `Ok(value)` if the function ran successfully, otherwise `Err(cause)` with the exception. 191 """ 192 try: 193 return _result.Ok(fn()) 194 except BaseException as e: 195 return _result.Err(e)
68@rustc_diagnostic_item("Error") 69@typing.runtime_checkable 70class Error(ToString, typing.Protocol): 71 """`Error` is an interface usually used for values that returns `sain.Result[T, E]` 72 73 where `E` is an implementation of this interface. 74 75 Example 76 ------- 77 ```py 78 import requests 79 import http 80 from dataclasses import dataclass 81 82 from sain import Error 83 from sain import Result, Ok, Err 84 85 # an http error. 86 @dataclass 87 class HTTPError(Error): 88 response: requests.Response 89 kind: http.HTTPStatus 90 message: str = "" 91 92 def description(self) -> str: 93 return f"HTTP Error [{self.response.status_code}, {self.kind}] for {self.response.url}" 94 95 # A simple request that handles [404] responses. 96 def request(url: str, uid: int) -> Result[requests.Response, HTTPError]: 97 response = requests.get(url, json={"user_id": uid}) 98 if response.status_code == 404: 99 return Err( 100 HTTPError( 101 response, 102 kind=http.HTTPStatus.NOT_FOUND, 103 message=f"Resource not found for user_id {uid}", 104 ) 105 ) 106 return Ok(response) 107 108 # Execute the request 109 match request("some-url.com", 0): 110 case Ok(response): 111 # Deal with the response 112 ... 113 case Err(why): 114 # Deal with the error. 115 print(why) 116 117 ``` 118 """ 119 120 __slots__ = ("message",) 121 122 def __init__(self, message: str = "") -> None: 123 self.message = message 124 """A basic error message.""" 125 126 def source(self) -> Option[type[Error]]: 127 """The source of this error, if any.""" 128 return _option.NOTHING 129 130 def description(self) -> str: 131 """Context for this error.""" 132 return "" 133 134 def to_string(self) -> str: 135 return self.__repr__() 136 137 def __repr__(self) -> str: 138 source = None if (src := self.source()).is_none() else src 139 return ( 140 f'{type(self).__qualname__}(message: "{self.message}, source: {source!r})' 141 ) 142 143 def __str__(self) -> str: 144 return self.message 145 146 # An error is always falsy. 147 def __bool__(self) -> typing.Literal[False]: 148 return False
Error is an interface usually used for values that returns sain.Result[T, E]
where E is an implementation of this interface.
Example
import requests
import http
from dataclasses import dataclass
from sain import Error
from sain import Result, Ok, Err
# an http error.
@dataclass
class HTTPError(Error):
response: requests.Response
kind: http.HTTPStatus
message: str = ""
def description(self) -> str:
return f"HTTP Error [{self.response.status_code}, {self.kind}] for {self.response.url}"
# A simple request that handles [404] responses.
def request(url: str, uid: int) -> Result[requests.Response, HTTPError]:
response = requests.get(url, json={"user_id": uid})
if response.status_code == 404:
return Err(
HTTPError(
response,
kind=http.HTTPStatus.NOT_FOUND,
message=f"Resource not found for user_id {uid}",
)
)
return Ok(response)
# Execute the request
match request("some-url.com", 0):
case Ok(response):
# Deal with the response
...
case Err(why):
# Deal with the error.
print(why)
Implementations
This class implements Error in Rust.
126 def source(self) -> Option[type[Error]]: 127 """The source of this error, if any.""" 128 return _option.NOTHING
The source of this error, if any.
151@rustc_diagnostic_item("catch_unwind") 152def catch_unwind(fn: Callable[[], R]) -> _result.Result[R, BaseException]: 153 """Invokes a closure, capturing exceptions if any one raised. 154 155 This function will return `Ok` with the closure's result if it doesn't raise any exceptions, 156 otherwise it will return `Err(cause)` with the exception. 157 158 You can treat this as an inline try-except block. 159 160 Notes 161 ----- 162 This function also catch exceptions such as `KeyboardInterrupt` and `SystemExit`, 163 so try to use it with extreme caution. 164 165 Example 166 ------- 167 ```py 168 from sain.error import catch_unwind 169 170 def request() -> str: 171 return requests.get("some url").text 172 173 def fetch() -> str: 174 raise RuntimeError from None 175 176 result = catch_unwind(request) 177 assert result.is_ok() 178 179 result = catch_unwind(fetch) 180 assert result.is_err() 181 ``` 182 183 Parameters 184 ---------- 185 fn: `Callable[[], R]` 186 The function to run. 187 188 Returns 189 ------- 190 `Result[R, BaseException]` 191 Returns `Ok(value)` if the function ran successfully, otherwise `Err(cause)` with the exception. 192 """ 193 try: 194 return _result.Ok(fn()) 195 except BaseException as e: 196 return _result.Err(e)
Invokes a closure, capturing exceptions if any one raised.
This function will return Ok with the closure's result if it doesn't raise any exceptions,
otherwise it will return Err(cause) with the exception.
You can treat this as an inline try-except block.
Notes
This function also catch exceptions such as KeyboardInterrupt and SystemExit,
so try to use it with extreme caution.
Example
from sain.error import catch_unwind
def request() -> str:
return requests.get("some url").text
def fetch() -> str:
raise RuntimeError from None
result = catch_unwind(request)
assert result.is_ok()
result = catch_unwind(fetch)
assert result.is_err()
Parameters
- fn (
Callable[[], R]): The function to run.
Returns
Result[R, BaseException]: ReturnsOk(value)if the function ran successfully, otherwiseErr(cause)with the exception.- # Implementations
- **This function implements catch_unwind: