0

Consider this a more refined version of this question.

It seems python's module structure is tied to actual directory containing the files. While it is possible to re-export another module using __init__.py, this does not alter the actual module hierachy as log as the import facility is concerned.

For instance:

some_dir
├ main.py
└ mod_a
  ├ __init__.py
  └ mod_b
    └ mod_c.py

mod_a/__init__.py:

from .mod_b import mod_c

mod_a/mod_b/mod_c.py:

def foo():
    print("Foo!")

In this case, we can do this in main.py:

from mod_a import mod_c

mod_c.foo()

but not this:

import mod_a.mod_c

mod_c.foo()

which fails with:

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    import mod_a.mod_c
ModuleNotFoundError: No module named 'mod_a.mod_c'

So init.py can't exactly alter module hierachy; And this is the closest thing to 'altering module hierachy' in python I know of.

So, is there a way to the alter the module hierachy? As in:

  • a way to make import mod_a.mod_c a valid import statement?
  • a way to mount some python module in arbitrary path to some import-path, in general?
Miles
  • 100
  • 1
  • 10
  • 1
    Have you seen https://docs.python.org/3/reference/import.html ? – MatsLindh May 02 '20 at 13:52
  • import mod_a does not import its submodules, it only automatically imports mod_a.__init__ – Luv May 02 '20 at 13:57
  • @MatsLindh I have, but I skimmed over looking for a more straight forward answer. Nevertheless, it was a step in the right direction, thank you. – Miles May 03 '20 at 02:35

1 Answers1

0

This can be done by modifying __path__, with pkgutil.extend_path or by other means.

According to the documentation:

A package’s __path__ attribute is used during imports of its subpackages. Within the import machinery, it functions much the same as sys.path, i.e. providing a list of locations to search for modules during import.

__path__ may be modified in the module's __init__.py to change where import system searches for submodules.

In given case, using pkgutil.extend_path:

# mod_a/__init__.py
from pkgutil import extend_path

__path__ = extend_path(__path__, __name__ + '.mod_b')

or by modifying __path__ manually:

# mod_a/__init__.py
__path__.append("(...)/some_dir/mod_a/mod_b")

where the actual path to the mod_b is preferably computed from __file__.

These SO question and answers cover more about using __path__.

Miles
  • 100
  • 1
  • 10