with-err is a python library that converts try-except pattern into Go-like result, err pattern. I feel result, err pattern easier to maintain in large projects.
pip install with-err(with uv)
uv pip install with-errimport json
from with_err import with_err
json_loads_e = with_err(json.loads)
data, err = json_loads_e('{"a": 1}')
assert err is None
assert data == {"a": 1}
data, err = json_loads_e('{"a": }')
assert isinstance(err, json.decoder.JSONDecodeError)
assert data is Noneimport json
from with_err import with_err
@with_err
def json_loads_e(a: str):
return json.loads(a)
data, err = json_loads_e('{"a": 1}')
assert err is None
assert data == {"a": 1}
data, err = json_loads_e('{"a": }')
assert isinstance(err, json.decoder.JSONDecodeError)
assert data is NoneReturn err only with specified exceptions and raise other exceptions.
import json
from with_err import with_exc
@with_exc(json.decoder.JSONDecodeError)
def json_loads_e(a: str):
return json.loads(a)
data, err = json_loads_e('{"a": 1}')
assert err is None
assert data == {"a": 1}
data, err = json_loads_e('{"a": }')
assert isinstance(err, json.decoder.JSONDecodeError)
assert data is None# raise json.decoder.JSONDecodeError
import json
import re
from with_err import with_err
@with_exc(re.PatternError)
def json_loads_e(a: str):
return json.loads(a)
data, err = json_loads_e('{"a": }')# function
import json
from with_err import with_exc
json_loads_e = with_exc(json.decoder.JSONDecodeError)(json.loads)
data, err = json_loads_e('{"a": 1}')
assert err is None
assert data == {"a": 1}
data, err = json_loads_e('{"a": }')
assert isinstance(err, json.decoder.JSONDecodeError)
assert data is None# empty: return all Exceptions
import json
from with_err import with_exc
json_loads_e = with_exc()(json.loads)
data, err = json_loads_e('{"a": 1}')
assert err is None
assert data == {"a": 1}
data, err = json_loads_e('{"a": }')
assert isinstance(err, json.decoder.JSONDecodeError)
assert data is Nonefrom with_err import with_async_err
async def async_fetch_data(endpoint: str) -> dict[str, str]:
if endpoint == "bad":
raise ValueError("Failed to reach endpoint")
return {"status": "ok"}
async_fetch_data_e = with_async_err(async_fetch_data)
res, err = await async_fetch_data_e("bad")
assert isinstance(err, ValueError)
assert res is Nonefrom with_err import with_gen_err
@with_gen_err
def my_stream():
yield 1
raise ValueError('invalid')
for idx, (each, err) in enumerate(my_stream()):
if idx == 0:
assert each == 1
assert err is None
else:
assert each is None
assert isinstance(err, ValueError)from with_err import with_async_gen_err
@with_async_gen_err
async def my_async_stream():
yield 1
raise ValueError('invalid')
async for each, err in my_async_stream():
if each == 1:
assert each == 1
assert err is None
else:
assert each is None
assert isinstance(err, ValueError)@with_err
def my_list_func(is_good=False):
if not is_good:
raise ValueError('not good')
return [1, 2]
data, err = my_list_func()
assert data is None
assert isinstance(err, ValueError)
data, err = my_list_func(True)
assert data is not None
assert err is None
item1, item2 = data
assert item1 == 1
assert item2 == 2import json
from with_err import with_err, get_err_strs
@with_err
def json_loads_e(a: str):
return json.loads(a)
_data, err = json_loads_e('{"a": 1}')
err_stack = get_err_strs(err)
assert err is None
assert err_stack == []import json
import re
from with_err import with_err, get_err_strs
@with_err
def json_loads_e(a: str):
return json.loads(a)
_data, err = json_loads_e('{"a": }')
err_stack = get_err_strs(err)
err_str = '\n'.join(err_stack)
assert isinstance(err, json.decoder.JSONDecodeError)
assert len(err_stack) > 0
assert re.search(r', line \d+, in json_loads_e', err_str)
assert re.search(r'json/__init__.py", line \d+, in loads', err_str)
assert 'json.decoder.JSONDecodeError: Expecting value:' in err_strimport json
import re
from with_err import with_err, get_err_strs, raise_err
@with_err
def json_loads_e(a: str):
return json.loads(a)
@with_err
def gen_err():
data, err = json_loads_e('{"a": }')
if err is not None:
raise err
return data
data, err = gen_err()
err_stack = get_err_strs(err)
err_str = '\n'.join(err_stack)
assert isinstance(err, json.decoder.JSONDecodeError)
assert len(err_stack) > 0
assert re.search(r', line \d+, in gen_err', err_str)
assert re.search(r', line \d+, in json_loads_e', err_str)
assert re.search(r'json/__init__.py", line \d+, in loads', err_str)
assert 'json.decoder.JSONDecodeError: Expecting value:' in err_strimport json
import re
from with_err import with_err, get_err_strs, raise_err
@with_err
def json_loads_e(a: str):
return json.loads(a)
def gen_err():
data, err = json_loads_e('{"a": }')
return data, raise_err(err)
data, err = gen_err()
err_stack = get_err_strs(err)
err_str = '\n'.join(err_stack)
assert isinstance(err, json.decoder.JSONDecodeError)
assert len(err_stack) > 0
assert re.search(r', line \d+, in gen_err', err_str)
assert re.search(r', line \d+, in json_loads_e', err_str)
assert re.search(r'json/__init__.py", line \d+, in loads', err_str)
assert 'json.decoder.JSONDecodeError: Expecting value:' in err_str@with_err
def echo_e(a: str):
return a
def test_get_results():
strs = [r'[0-9', r'\d+', r'\W+', r'\d+\W+', r'[z-a]', r'\d+\W*']
rets = [echo_e(each) for each in strs]
errs = get_errs(rets)
assert len(errs) == 6
assert errs[0] is None
assert errs[1] is None
assert errs[2] is None
assert errs[3] is None
assert errs[4] is None
assert errs[5] is None
first_err = get_first_err(rets)
assert first_err is None
the_is_any_err = is_any_err(rets)
assert not the_is_any_err
the_is_all_err = is_all_err(rets)
assert not the_is_all_err
results = get_results(rets)
assert len(results) == 6
assert results[0] == r'[0-9'
assert results[1] == r'\d+'
assert results[2] == r'\W+'
assert results[3] == r'\d+\W+'
assert results[4] == r'[z-a]'
assert results[5] == r'\d+\W*'
results = get_results_or_none(rets)
assert len(results) == 6
assert results[0] == r'[0-9'
assert results[1] == r'\d+'
assert results[2] == r'\W+'
assert results[3] == r'\d+\W+'
assert results[4] == r'[z-a]'
assert results[5] == r'\d+\W*'The implementation is based on the following Gemini / ChatGPT suggestions: