1

I tried to read how import works and use what is suggested, but I still have questions:

If I cd to project folder and run python ./test/test1.py I get ModuleNotFoundError: No module named 'lib'.

  • If I add print(os.getcwd()) it does print the current directory which is project. So, why it cannot find lib folder?
  • If I cd to project/test, add import sys sys.path.append('..') to the beginning of test1.py and then run python test1.py it works. But, why I cannot run it from one level above?
  • I also tried to add __init__.py to lib and test folders, but I get same error.
  • If I try changing to relative path from .lib.lib1 ... and run the test.py from project folder I get ImportError: attempted relative import with no known parent package error

What am I doing wrong?

test1.py

from lib.lib1 import MyClass

Project structure

project
  lib
    lib1.py
    lib2.py
  test
    test1.py # from lib.lib1 import MyClass
   
  main.py 
S.B
  • 13,077
  • 10
  • 22
  • 49
theateist
  • 13,879
  • 17
  • 69
  • 109
  • You need to tell python about your packages. This is usually done via the `__init__.py` files you already mentioned. I would expect one in each of your folders lib and test. Finally, you may want to add a setup.py that contains info about both packages (or at least about lib) in the setup function. Then you should be able to install your packages in develop mode to make it available to your interpreter with something like `pip install -e .` – Christian Steinmeyer Jun 17 '23 at 07:03

1 Answers1

0

When you run a script, Python automatically adds (only) "its" directory to the sys.path which is where it looks for other modules and packages. In your case, by running python ./test/test1.py, its directory which is <rest of the path>/test/ is going to be added to the sys.path and that's it.

Now where is the lib directory? It is in the <rest of the path>/project/ directory. But the <rest of the path>/project/ directory is not in the sys.path. Yes it's your current working directory but Python doesn't add your "current directory" to the sys.path. Only the script's directory.

The rule is, Check what is inside your sys.path and see if the module you're importing is accessible through one of the records inside that list. If it's not, you need to explicitly add it.

Why are you facing these kind of issues? Normally you won't(shouldn't?) run the sub modules/packages directly. They supposed to be invoked by the main.py module(except for tests though). In that case the <rest of the path>/project/ directory is in sys.path so that all other submodules which use the absolute import statements starting from the root will work properly.

Side note: In the absence of __init__.py module, your import statements still work because of the Implicit Namespace Packages.

Solution:

Add this at the top of your test1.py:

import sys
sys.path.append("<rest of the path>/project")

And then use this form for import in test1.py:

from lib.lib1 import MyClass

As you see, you have to add it to all sub-modules which you directly run, Another option is to use PYTHONPATH environment variable. So instead of writing sys.path.append...., run your script like:

PYTHONPATH="<rest of the path>/project" python ./test/test1.py 
S.B
  • 13,077
  • 10
  • 22
  • 49
  • I added `print(sys.path)` to `test1.py` and ran `python3 ./test/test1.py` from root. The `...../project/test` is included in `sys.path`. So, why when I add `from ..lib import lib1` to `test1.py` I get `attempted relative import with no known parent package?` Doesn't `..` mean to go one level up and find `lib` directory? So, since `project/test` is in `sys.path` it should have found it, right? – theateist Jun 17 '23 at 23:19
  • @theateist No. [Documentation](https://docs.python.org/3/tutorial/modules.html#intra-package-references) said that you shouldn't use relative imports when you're running your script as the main script. Although I showed a hack to make it work [here](https://stackoverflow.com/a/68302324/13944524) but I do not recommend it. Use absolute import whenever you're running your script as the main module. So `from lib.lib1 import MyClass`. As I mentioned in the answer it doesn't matter where you are in the file system. If you need to access `lib.lib1`, just check `sys.path`... – S.B Jun 18 '23 at 02:59
  • @theateist ... Is there any record which can find the `lib` ? Yes there is. You added the absolute path to the `project` directory to the `sys.path` so `lib` can be easily found. No need to go one or two levels up with dots and then import it. – S.B Jun 18 '23 at 03:00
  • just to make sure I understood, when I add `sys.path.append('..')` it means to go one level up from the directory the script was run from and NOT from the directory the script is located, right? Meaning, if I cd to `/A/project` and run `python3 ./test/test1.py` from there, it will look in `/A` directory and not one level up from the location of the script, meaning in `/A/project`, right? If the first is correct, then I have to cd to `test` directory and run the `test1.py` from there if I add `sys.path.append('..')`, right? – theateist Jun 18 '23 at 04:49
  • @theateist No. you must add `sys.path.append(".")`. Let me explain it this way: Python only sees where the directory of the script is located and add that path to the `sys.path`. Now you're in the root directory, the `os.getcwd()` shows you're in the root directory. Is it added to the `sys.path` ? No because it was not the `test1.py`'s directory. If you want to add the root path to the `sys.path` you should only add `"."` which currently points to your location. – S.B Jun 18 '23 at 06:49