问题
Suppose I have a project with a folder structure like so.
/project
__init__.py
main.py
/__helpers
__init__.py
helpers.py
...
The module helpers.py
defines some exception and contains some method that raises that exception.
# /project/__helpers/helpers.py
class HelperException(Exception):
pass
def some_function_that_raises():
raise HelperException
On the other hand my main.py
module defines its own exceptions and imports methods that may raise an exception from helpers.py
.
# /projects/main.py
from project.__helpers.helpers import some_function_that_raises
class MainException(Exception):
pass
Now, I do not want users to have to do from project.__helpers.helpers import HelperException
if they want to catch that exception. It would make more sense to be able to import the exception from the public module that is raising it.
But I cannot just move HelperException
to main.py
, which would create a circular import.
What would be the best way to allow users to import all exceptions from main.py
while those being raised in /__helpers
?
回答1:
Here is the solution I came up with.
The idea is basically to put all the exceptions in one file from which they can be imported and then import them all in main.py
. To make everything clean and explicit, we finally define the __all__
attribute of the module.
Here is the new file structure
/project
__init__.py
main.py
/__helpers
__init__.py
error.py
helpers.py
...
Here is the error.py
file.
# /project/__helpers/error.py
class MainException(Exception):
pass
# Note that this also allows us to inherit between exceptions
class HelperException(MainException):
pass
Then we can import exceptions from that file without risk of circular dependency.
And finally we define __all__
in main.py
to make it explicit that the exceptions are to be imported.
# /projects/main.py
from project.__helpers.helpers import some_function_that_raises
from project.__helpers.errors import MainException, HelperException
__all__ = ['MainException', 'HelperException', ...]
Just a reminder the __all__
attribute defines what will be imported if one was to do from project import *
. So this both extends the desired behavior to import star and makes it explicit that we want the exceptions to be imported from that file.
Also note that some IDEs will even treat 'HelperException'
being in __all__
as a reference to HelperException
and will not bother you from having an unused import. This is what leads me to think this is the correct approach.
来源:https://stackoverflow.com/questions/48104895/best-practice-for-structuring-module-exceptions-in-python3