Return, don't raise.
Project description
tpify
Pronounced like "typify"
Return, don't raise.
What this does
tpify allows you to configure functions to return contextual statuses, and to return errors rather than raise them.
Why?
Python programs often have inconsistent conventions when it comes to returning data. They also have a problem of not making it clear when exceptions can be raised. This is an attempt to see if there is enough benefit to violate Pythonic conventions to get some additional reliability and improved (albeit verbose, Go-like) error handling.
Benefits
No more try/except
All tpified functions return an object, removing the need to wrap functions in a try block. This leads to safer, more predictable execution paths. Exceptions are still returned when raised, and the ability to customize which status code is returned means the type and source of error can be more easily communicated.
Better context
TPResponse objects contain status codes, enabling richer context about successes and failures. For example:
- Functions that return
Truecan return context about whether a new resource was created usingtp.Createdstatus, or updated using thetp.Updatedstatus. - Functions that return
Nonecan return information about whetherNonemeans a successful or failed execution. - Failure origination can be returned to indicate if the error is an error with the input data (
tp.InputErrorresponses), or with the processing of the data iteself (tp.ProcessingErrorresponses). If you can't find a code that works for you, there is always the option to create your own.
Dos and Don'ts
DO: Check your TPResponse status codes. While you can just take the content of a TPResponse if you only want the safety of avoiding unintended raise statements, it is encouraged to have some paradigm to process status_code values.
DO: append _tp at the end of tpified function names. This communicates that this function returns a TPResponse object, since the function itself may show it returns a different type.
from tpify import tpify
@tpify()
def fibonacci_tp(n: int) -> int:
# TODO: Implement fibonacci recursively
DO: use @tpify() after non-return type-modifying decorators. If you have multiple decorators on a tpified function, have tpify() be after those that don't modify return types, so that the return types are accounted for in any other decorator logic.
from functools import cache
from tpify import tpify
@cache
@tpify()
def fibonacci_tp(n: int):
# TODO: Implement fibonacci recursively
Advanced uses
exception_type_map
The @tpify() decorator takes an optional argument that maps raised exceptions to TPStatus codes. This allows you to map errors to status codes other than the default TPStatus.ProcessingError value. For example:
import json
from typing import Any
from tpify import append_statuses_tp, tp, tpify
@tpify(exception_type_map={json.JSONDecodeError: tp.InputError})
def parse_json_tp(json_str: str) -> Any:
return (tp.OK, json.loads(json_str))
if __name__ == "__main__":
status, resp = parse_json_tp('{"fibNum": 13}')
print(type(status))
print(status == tp.OK)
print(status == tp["OK"])
print(resp)
status, resp = parse_json_tp('{"fibNum": error}')
print(type(status))
print(status == tp.InputError)
print(status == tp["InputError"])
print(resp)
<enum 'TPStatus'>
True
True
{'fibNum': 13}
<enum 'TPStatus'>
True
True
Adding custom TPStatus values
You can add custom TPStatus values using append_statuses_tp(). This ends up creating a new TPStatusCustom object that contains the original TPStatus codes, as well as the new ones you request. Here's the gist of how that works:
import json
from typing import Any
from tpify import append_statuses_tp, tp, tpify
tp_status, tp_cust = append_statuses_tp(
statuses=(
"JSONParseOK",
"JSONParseFailed",
)
)
if tp_status != tp.OK:
print(f"ERROR: Could not create new statuses: {tp_cust}")
exit(1)
@tpify(exception_type_map={json.JSONDecodeError: tp_cust.JSONParseFailed})
def parse_json_tp(json_str: str) -> Any:
return (tp_cust.JSONParseOK, json.loads(json_str))
if __name__ == "__main__":
status, resp = parse_json_tp('{"fibNum": 13}')
print(type(status))
print(status == tp_cust.JSONParseOK)
print(status == tp_cust["JSONParseOK"])
print(resp)
status, resp = parse_json_tp('{"fibNum": error}')
print(type(status))
print(status == tp_cust.JSONParseFailed)
print(status == tp_cust["JSONParseFailed"])
print(resp)
<enum 'TPStatusCustom'>
True
True
{'fibNum': 13}
<enum 'TPStatusCustom'>
True
True
Expecting value: line 1 column 12 (char 11)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file tpify-0.0.1a3.tar.gz.
File metadata
- Download URL: tpify-0.0.1a3.tar.gz
- Upload date:
- Size: 6.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d32ca0ce736cedf308162b6cd09c9dc0b678471a30e7f4404c696c1ad22a7f72
|
|
| MD5 |
80621754a40fd3ecc0e18d8573d4f564
|
|
| BLAKE2b-256 |
b598c355e95c4ba7a4050ce7ad5d1a6e20dcd03ed08d49cdfec4fd5714c4c8e4
|