0

python project files hierarchy:

parent/
    __init__.py
    one/
        __init__.py
        bar.py
    two/
       __init__.py
       foo.py

foo.py

from one import bar

I tried to run foo.py from terminal in other directory (e.g. users/user), I got the next error:

No module named one

When I trying to run foo.py, I guess it is trying to import the files from the directory that the code had been executed from, I had tried lot of ways and I couldn't find solution, finally I found a solution, the problem with this solution is that the solution is not elegant and I hope there is an elegant and better solution.

foo.py

from pathlib import Path
import sys

sys.path.append(str(Path(__file__).parent.parent))
sys.path.append("..")

from one import bar
  • This solution is not elegant because it preventing me to put all the imports in the start of the page.

2 Answers2

0

The fact that you have an __init.py__ in the parent directory suggests that parent is part of your package structure and that its parent directory, whatever that might be, should be in the PATH. Therefore your import should really be:

from parent.one import bar

It can be useful for an application directory structure to have a single root. Then the __init.py__ in that single root package can be used to load modules from subpackages, but this is certainly not a requirement. If that was not your intention, then you should probably delete the __init__.py that is in parent as it is serving no purpose (and is confusing) and ensure that directory parent is in your PATH.

HOWEVER: As long as the current directory you are in when you run your program is the parent directory of the root(s) of your package structure, Python should be able to find your packages with no special action on your part because the current directory is automatically added to the path. If that is inconvenient, you can set environment variable PYTHONPATH.

So, determine whether you should be changing your import statement or not based on which directories are part of your package structure. Then you should arrange for Python to find your packages either by setting the current directory, PYTHONPATH, or sys.path to the required directory -- but do this once. If you have to set sys.path, I would do this in your main program at startup before it needs to include anything:

If foo.py is your main program, then at the top of the program I would have:

if __name__ == '__main__':
    from pathlib import Path
    import sys

    # if your import statement is: from parent.one import bar, then:
    sys.path.insert(0, str(Path(__file__).parent.parent))
    """
    # if your import statement is: from one import bar, then:
    sys.path.insert(0, str(Path(__file__).parent))
    """
Booboo
  • 38,656
  • 3
  • 37
  • 60
0

Why don’t you let the parent act like a path provider to the child, by creating a path dictionary ? like this way :

class parent:
...
    def createPathDict(self):
        self.path_dict = {}
        self.path_dict ['parent'] = self.parentPath
        self.path_dict ['one'] = os.path.join(self.parentPath, 'one')
        self.path_dict ['two'] = os.path.join(self.parentPath, 'two')
        # self.path_dict ['three'] = …
        # ...

From child ‘two’ you import the dictionary like this (I assume you use classes) :

class foo:

    def __init__(self, parent):
          self.parent = parent

    def addPathsToPythDirs(self):
         sys.path.insert(1, self.parent.path_dict ['one'])  # better
         # sys.path.insert(0, self.parent.path_dict [key])  
...

In that way you could keep your imports in foo.py

Why use sys.path.append(path) instead of sys.path.insert(1, path)?

Laurent B.
  • 1,653
  • 1
  • 7
  • 16
  • Your link in your comment seems to be addressing the issue of using `sys.path.insert` to handle multiple environments instead the more proper `virtualenv` approach. Furthermore, Ulf Rompe says that `sys.path.insert(1, path)` is preferred to `sys.path.insert(0, path)` only because "since 3rd party code may rely on sys.path documentation", which is that entry 0 will contain either the directory where the script resides or '', which means the current directory. But we are not dealing with 3rd party code here. And there is no reason for either of those two directories to be searched first. – Booboo May 08 '20 at 12:21
  • Furthermore, there is only a single directory that you should be adding to the path, i.e. the correct one. It is either `parent` if the OP is coding `from one import bar` or the parent of `parent` if the OP is coding `from parent.one import bar`. There is not reason to add `one` to the path. – Booboo May 08 '20 at 12:27