I have a Python library where everything is exposed through the top-level import.
My exceptions are defined in src/foobar/_errors.py and then re-exported in src/foobar/__init__.py, so they can be used like this in user code:
from foobar import MySpecificFooBarError
For example, in _errors.py I have something like:
class MyBaseFooBarError(Exception):
pass
class MySpecificFooBarError(MyBaseFooBarError):
pass
This part works fine, and it’s documented.
The problem is with tracebacks. When one of these exceptions is raised, the traceback shows the fully qualified name from the private module, e.g.:
Traceback (most recent call last):
[...]
foobar._errors.MySpecificFooBarError: Oops!
This is technically correct, but it’s not what I want. Users find it confusing because they’ve been told to use foobar.MySpecificFooBarError, not foobar._errors.MySpecificFooBarError (I got an issue about this, which is why I’m making this post in the first place).
My current solution
Right now, I manually override the metadata on each exception:
class MySpecificFooBarError(MyBaseFooBarError):
__module__ = "foobar"
__qualname__ = "MySpecificFooBarError"
This works, but it’s not ideal. What I’d really like is to define this behavior once in MyBaseFooBarError and have subclasses automatically inherit it. At the moment, I can’t seem to get that working, so I need to repeat the __module__ and __qualname__ assignments for every error class.
Question
Is there a way to make Python show the public import path (foobar.MySpecificFooBarError) in tracebacks without having to redefine __module__/__qualname__ in every subclass, and without moving all exception definitions into __init__.py?