Reputation: 21309
I have a package with several subdirectories containing __init__.py
files. These files perform checks and initialization jobs.
Now I also have in some of these folders a file that isn't supposed to be referenced directly by an import
as it relies on the sanity checks performed in its respective __init__.py
. Let's call these "hidden modules" - as a convention I use an underscore to make those obvious.
Is it a bad idea to do the following inside my __init__.py
(with a _implementation.py
located in the same folder):
import os, sys
if sanity_check_successful:
from ._implementation import *
__all__ = sys.modules[__name__ + "._implementation"].__all__
The idea should be clear, I am trying to provide meaningful error information at each respective module level in the package whenever a sanity check fails.
Is this a bad idea, i.e. copying the __all__
array over from the "hidden module"? If so, why and are there better alternatives?
Bonus points: is there a more concise way of writing those two lines?:
from ._implementation import *
__all__ = sys.modules[__name__ + "._implementation"].__all__
In particular it itches me that I have to use a string "._implementation"
in one place and as a module name in another.
Upvotes: 0
Views: 256
Reputation: 365925
Even simpler than Martijn Pieters' answer:
If you include '__all__'
in the __all__
list of ._implementation
, it will be automatically imported by your single from ._implementation import *
line.
As to your main question, whether this is a bad idea…
Well, it depends on what you're doing. This basically makes your package look exactly like its _implementation
module. If that's all you're doing, that's fine… but in that case, why not just move _implementation
into __init__
in the first place?
If you're trying to merge multiple modules into one, you probably want to add all of their __all__
lists into a single one. The stdlib has examples of this, like collections
, and the usual pattern is:
from collections.abc import *
import collections.abc
__all__ += collections.abc.__all__
That may seem a little verbose, but it's certainly clear.
From your edited question, I think what you're doing is reasonable in exactly the same way that collections
is, and the clearest and most idiomatic solution is to do the equivalent, but with =
instead of +=
(since you're just copying one list instead of adding multiple lists together).
But, since this:
import foo
bar = foo.bar
… is pretty much equivalent (as in close enough for your use case) to:
from foo import bar
… Martijn Pieters' answer is an obvious simplication:
from ._implementation import *
from ._implementation import __all__
So, I'd do that.
Upvotes: 1
Reputation: 1123610
There are simpler ways to set __all__
from the ._implementation
submodule; setting __all__
otherwise fine:
from ._implementation import *
from ._implementation import __all__
This simply imports __all__
and binds it to the local module namespace __all__
.
Upvotes: 3