Reputation: 7714
My package looks something like this:
/mypackage
|-- __init__.py
|-- moduleA.py
|-- moduleB.py
|-- oldModule.py
And __init__.py
looks something like this:
from mypackage.moduleA import abc
from mypackage.moduleB import jkl
This allows users to simply code as follows:
import mypackage as mpkg
mpkg.abc()
mpkg.jkl()
By default (using the above import
) users have no access to functions in oldModule.py
.
This is because 95% of users don't need that stuff (it's old).
For the 5% of users who need functions from the oldModule, they can write:
from mypackage.oldModule import xyz
xyz()
Many of these "oldModule" users don't need access to the functions imported by __init__.py
from mypackage.oldModule import xyz
__init__.py
is also being run.Is there any way that I can either
__init__.py
does not run if user does from mypackage.oldModule import ...
, or__init__.py
,from mypackage.oldModule import ...
,if
block inside __init__.py
to decide which parts of the code to run ??Thanks.
Upvotes: 0
Views: 266
Reputation: 104752
There's no way to do exactly what you're asking for. When you import some_package.some_module
, the package always gets fully loaded before the submodule does.
But there might be some ways to mitigate the issues this causes in your case. If you want to avoid importing the moduleA
and moduleB
submodules when a user is looking for oldModule
, you can perhaps make their loading lazy, rather than eager.
Starting in Python 3.7, PEP 562 enables a module to have a function named __getattr__
which will work like a method of the same name defined in a class. When an attribute of the module is looked up but not found, the __getattr__
function will be called with the name.
So in your __init__.py
file, you could do:
# from mypackage.moduleA import xyz don't do these imports unconditionally any more
# from mypackage.moduleB import abc
def __getattr__(name):
global xyz, abc
if name == 'xyz':
from .moduleA import xyz
return xyz
elif name == 'abc':
from .moduleB import abc
return abc
raise AttributeError(f"module {__name__} has no attribute {name}")
If you have a lot more names you need to protect this way (not just two), you may want to write more general code that can process a list of names (probably one per sub-module) and import them properly using importlib
and writing directly into the package's __dict__
. That will be more convenient than trying to use global
statements and writing a separate if
branch for each name.
Upvotes: 1