4

I am struggling with nested __init__.py in a Python package I am writting. The Package has the following architecture:

module/
├── __init__.py
├── submodule1
│   ├── __init__.py
│   └── source.py
└── submodule2
    ├── __init__.py
    ├── source.py
    └── subsubmodule2
        ├── __init__.py
        └── source.py

My intent is to be able to access functions defined in submodule2/source.py through module.submodule2.function and in subsubmodules2/source.py through module.submodule2.subsubmodule2.function.

The first thing I tried was to define __init__.py in submodule2 this way:

from .subsubmodule2 import *

But doing so, I get the functions defined in subsubmodules2/source.py through module.submodule2.function (and module.function).

If I do:

from . import subsubmodule2

I get these functions through module.subsubmodule2.function.

I also tried to define __all__ keyword in __init__, with no more success. If I understand well Python documentation, I guess I could leave empty __init__.py files and it could work, but from my understanding that is not the best practice either.

What would be the best way to access these functions as intended in my module?

MBR
  • 794
  • 13
  • 34
  • Maybe you can check PYTHONPATH environment variable, this is the directory where python will find module. By default, this is where you run your code. – icejoywoo Feb 19 '21 at 11:00
  • Please clarify what you want. The way you describe it, ``__init__.py`` does not define anything – therefore, there is no "functions defined in ``submodule2``", only "functions defined in ``submodule2.source`` and ``submodule2.subsubmodule2.source``". Are you asking how to pull the objects defined inside ``.source`` into the parent module? – MisterMiyagi Feb 19 '21 at 11:37
  • Thank you for pointing the ambiguity. I clarified in the text, sorry if it was unclear but I obviously meant "functions defined in `submodules2/source.py` and so on. – MBR Feb 19 '21 at 11:43

2 Answers2

2

in module __init__.py file write the module which you want to import as

from . import submodule1
from . import submodule2
__all__ = ['submodule1', 'submodule2']

Now, in submodule1 __init__.py file write

from . import source
from . import subsubmodule
# if you want to import functions from source then import them or in source.py
# define __all__ and add function which you want to expose
__all__ = ['source', 'subsubmodule']

now in subsubmodule __init__ file define function or class which you want to expose

from . source import *
__all__ =  ['source']
# if you want to use * as import, i suggest you to use __all__ in source.py and mention all exposed function there
Fr33dan
  • 4,227
  • 3
  • 35
  • 62
sahasrara62
  • 10,069
  • 3
  • 29
  • 44
  • If I do that, I get a `ModuleNotFoundError` error on `submodule1` when importing my module. Am I missing something else? – MBR Feb 19 '21 at 11:24
  • just try to change the import statement like `from . import submodule1` or `import submodule1` or `import .submodule1` like these statements, i only this will cause issue – sahasrara62 Feb 19 '21 at 11:26
  • `from . import ` was the right statement to write at all level, thanks! – MBR Feb 19 '21 at 11:30
  • I had a weird situation when I named my main file say `A.py` and had a folder `A`. It got fixed after I renamed it say `B` folder. I guess I had to write `.A.A` but `.A` is already same with `A.A`? idk. wanted to put that here, may help somebody. It can be unnoticeable. – FLAW Apr 07 '22 at 03:37
  • @FLAW use folder name if it is B then use .B.A – sahasrara62 Apr 07 '22 at 05:42
  • No there was a problem on `A.A`. let me clear this up: `top/A.py`, `top/A/test.py` when I tried to `from .A.test import tester` in `A.py`, it couldn't get. after I renamed it `top/B/test.py it worked. Didn't dive deep on that error tho. – FLAW Apr 07 '22 at 08:31
2

The __init__.py file represents its respective package. For example, module/submodule2/__init__.py represents module. submodule2 .

In order to pull objects defined in submodules into their package namespace, import them:

# module/submodule2/__init__.py
from .source import *

Since __init__.py is a regular Python module, one can also forgo a separate .source module and define objects directly inside __init__.py:

# module/submodule2/__init__.py

def function():
    ...

Note that subpackages themselves are already available as their respective name. One does not have to – and in fact should not – import them in the parent module. They will be imported if code using the package imports them.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • If I don't import the `submodule` in `submodule`, I don't see it in my namespace. – MBR Feb 19 '21 at 11:46
  • Whatever code *uses* the ``submodule`` should import it. That's the point of not importing them in ``__init__.py`` – so that they are only imported as needed. Compare [multiprocessing.queue module is missing until a Queue is instanced](https://stackoverflow.com/questions/66142690/multiprocessing-queue-module-is-missing-until-a-queue-is-instanced) – MisterMiyagi Feb 19 '21 at 11:54
  • OK, after testing it I get it. But what buggers me with this is that I tend to use `__init__.py` to expose my API explicitely, and I must also say that I find it much less user friendly in an interactive environment (it is not possible to "discover" the content of the given module). – MBR Feb 19 '21 at 13:07