Warning
Most of the documentation was written prior to version 0.5 and needs to be updated. This work has now started for version 0.7 and we aim to have it completed before version 0.8 is available.
Warning
In this section, no attention is given to providing support for translating the information shown to the user.
Registering custom error types¶
By default, friendly
provides a set of traceback explanations for builtin exceptions.
However, it is also possible to add explanation texts for custom error types. For that,
two entry points are available. The first one is
@friendly_traceback.info_generic.register
decorator that registers a generic message that describes an error type.
Example:
from friendly_traceback import info_generic
@info_generic.register(CustomError)
def describe():
return "CustomError is raised when ..."
@friendly_traceback.info_generic.register
will accept any parameterless function that returns a string.
Danger
info_specific no longer exists with version 0.5.59 and above. the following will need to be rewritten for the newer version.
Example:
from friendly_traceback import info_specific
@info_specific.register(CustomError)
def describe(error, frame, traceback_data):
return {
"cause": (
f"The particular custom error was {error}, "
f"raised in file {traceback_data.filename!r} "
f"on line {traceback_data.program_stopped_bad_line!r}."
)
}
Notice that a function accepted by
@friendly_traceback.info_specific.register
gets three input arguments: the raised exception instance, the current frame
and the traceback data object generated by friendly
that provides great
help for eventual introspection. The function should return not a string
this time, but a dictionary with the custom description stored under "cause"
.
The dictionary may also contain a "suggest"
key: this is most often used to show
a simple suggestion for avoiding or mitigating the error as part of
the friendly traceback (friendly_tb()
).
Example¶
Imagine we have a container api
with a web service running behind
the address https://my-services/api
. The following snippet requests
data from the service:
import requests
def fetch_data():
response = requests.get("https://my-services/api")
response.raise_for_status()
return response.json()
fetch_data()
When the connection can not be established, a requests.ConnectionError
will be raised.
Fortunately, since it inherits from OSError
, friendly
is already
able to provide a default explanation about the error cause in this particular case:
An exception of type ``ConnectionError`` is a subclass of ``OSError``. An ``OSError``
exception is usually raised by the Operating System to indicate that an operation
is not allowed or that a resource is not available.
I suspect that you are trying to connect to a server and that a connection cannot be made.
If that is the case, check for typos in the URL and check your internet connectivity.
What if we want to add a more detailed information? First, we can register a custom generic
description for any occurences of the requests.ConnectionError
:
from friendly_traceback import info_generic
@info_generic.register(requests.ConnectionError)
return (
"A `ConnectionError` from the `requests` package\n"
"usually indicates that a service cannot be reached\n"
"because it is offline.\n"
)
Now friendly
will print
A `ConnectionError` from the `requests` package usually indicates that
a service cannot be reached because it is offline.
I suspect that you are trying to connect to a server and that a connection cannot be made.
If that is the case, check for typos in the URL and check your internet connectivity.
Second, we register a custom hook that generates a specific description for the particular error:
from friendly_traceback import info_specific
answer_to_why = """
First, check whether the container is running:
$ docker container inspect -f '{{.State.Running}}' api
If necessary, restart with
$ docker restart api
"""
@info_specific.register(requests.ConnectionError)
def describe(error, frame, traceback_data):
hint_added = (
f"The {error.request.method} request "
f"for `{error.request.url}` has failed.\n"
)
return {"cause": answer_to_why, "suggest": hint_added}
This results in the following customized friendly
output:
Traceback (most recent call last):
[Very long traceback omitted]
The GET request for `https://my-services/api` has failed.
A `ConnectionError` from the `requests` package
usually indicates that a service cannot be reached
because it is offline.
First, check whether the container is running:
$ docker container inspect -f '{{.State.Running}}' api
If necessary, restart with
$ docker restart api
Execution stopped on line 38 of file example.py.
33: response = requests.get("https://my-services/api")
34: response.raise_for_status()
35: return response.json()
-->38: fetch_data()
fetch_data: <function fetch_data>
Exception raised on line 516 of file LOCAL:\requests\adapters.py.
510: raise ProxyError(e, request=request)
512: if isinstance(e.reason, _SSLError):
513: # This branch is for urllib3 v1.22 and later.
514: raise SSLError(e, request=request)
-->516: raise ConnectionError(e, request=request)
518: except ClosedPoolError as e:
request: <PreparedRequest [GET]>
global ConnectionError: <class requests.exceptions.ConnectionError>