0

My project structure is the following:

.
└── project name
     ├── project name
     │   ├── __init__.py
     │   ├── module.py
     │  
     ├── PACKAGE_A
     │   ├── __init__.py
     │   ├── PACKAGE_A.py
     │   ├── module_a.py
     │

In PACKAGE_A.py

from module_a import Some_Class

a = Some_Class()

class Another_Class:
    # class code here

In module.py

"""
Notes
-----
https://stackoverflow.com/questions/16780014/import-file-from-parent-directory
"""

# Standard library imports
import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Local application imports
from PACKAGE_A.PACKAGE_A import Another_Class
from PACKAGE_A.module_a import some_function

While module_a.py and PACKAGE_A.py run without problem, running module.py fails with:

Traceback (most recent call last):
  File "path\to\project name\project name\module.py", line 12, in <module>
    from PACKAGE_A.PACKAGE_A import Another_Class
  File "path\to\project name\PACKAGE_A\PACKAGE_A.py", line 1, in <module>
    from module_a import Some_Class
ModuleNotFoundError: No module named 'module_a'

What am I doing wrong here?

T81
  • 171
  • 1
  • 3
  • 12
  • This is a relative import. from .module_a import Some_Class (note the dot before module_a) should do the work. https://www.python.org/dev/peps/pep-0328/#rationale-for-relative-imports – jbet Jun 05 '19 at 14:24
  • ```from PACKAGE_A.PACKAGE_A import Another_Class``` fails ```from PACKAGE_A.module_a import some_function``` doesn't fail – T81 Jun 05 '19 at 14:32
  • What really fails is the last line of the traceback: `from module_a import Some_Class`. It is called internally when you do `from PACKAGE_A.PACKAGE_A import Another_Class`. – jbet Jun 05 '19 at 14:37
  • True. Why does it fail? – T81 Jun 05 '19 at 14:43
  • Because you should make a relative import, not absolute one. In general, your "`module_a.py` and `PACKAGE_A.py` run without problem" is probably true only when running from `project name/PACKAGE_A` directory. – jbet Jun 05 '19 at 14:49
  • Do I need to change the project structure, too? – T81 Jun 05 '19 at 15:03

2 Answers2

1

import search for your packages in specific places, listed in sys.path. See the doc for the full details.

The current directory is always appended to this list, that's why you succeed to run everything inside PACKAGE_A. But from project name, there is no way to know where to find PACKAGE_A.

Solutions include:

  • use relative import
  • always run from the root directory (and start all your imports from the root directory)
  • add the root directory to the environment variable PYTHONPATH (same)
  • use a tool which sets PYTHONPATH when you enter your virtual environment (same)
  • ...

and depends on your project and your needs.

gaFF
  • 747
  • 3
  • 11
  • How does ```from PACKAGE_A.PACKAGE_A import Another_Class``` fails while```from PACKAGE_A.module_a import some_function``` doesn't fail, both from ```module.py```? – T81 Jun 05 '19 at 14:30
  • From where did you launch `module.py` ? Does `import PACKAGE_A.PACKAGE_A as PA` works (using `PA.Another_Class` in the code) ? No typo in `Another_Class` ? – gaFF Jun 05 '19 at 14:33
  • I am launching it from inside its directory (running it from SublimeText, using the build option). The import you mention above, fails with the same ```ModuleNotFoundError: No module named 'module_a'``` message. No typos. I am using a virtual environment. – T81 Jun 05 '19 at 14:39
  • So both `import PACKAGE_A.PACKAGE_A` and `import PACKAGE_A.module_a` fail ? I am not familiar with SublimeText, perhaps search in the option/settings what interpreter is used and from where it is launched. – gaFF Jun 05 '19 at 14:49
  • No. Only the first import fails. – T81 Jun 05 '19 at 15:52
  • Possibly I have to restructure my project – T81 Jun 05 '19 at 16:28
1

You need to change your import statement in PACKAGE_A.py from:

from module_a import Some_Class

to:

from PACKAGE_A.module_a import Some_Class

The reason is that you are adding the path\to\project name\ to sys.path, but you have no module_a.py in path\to\project name\, and path\to\project name\PACKAGE_A (where module_a.py resides) is not in sys.path.

As for why you succeed in running everything in PACKAGE_A, it's because Python adds the directory containing the script you are running to the list (as explained by gaFF).

I would recommend you read a bit more about python imports, if the doc seems too cluttered, you can check this link.

This is a personal preference, but I find it simpler to add the root directory of the project to the PYTHONPATH environment variable and then running all the scripts from that directory's level and changing the import statements accordingly. In your example, the root directory would be path\to\project name\.

  • Alright. I am a bit confused. The above change solved the problem. ```module.py``` does not fail, but now trying to run ```PACKAGE_A.py``` yields```ModuleNotFoundError: No module named 'PACKAGE_A.module_a'; 'PACKAGE_A' is not a package``` – T81 Jun 06 '19 at 05:44
  • You have to run `PACKAGE_A.py` from the root directory: `cd path\to\project name\`, then `PACKAGE_A/PACKAGE_A.py`. In `PACKAGE_A.py`, import will search for a file `PACKAGE_A/module_a.py` and find it from the current directory. – gaFF Jun 06 '19 at 07:29
  • 1
    Sorry for the confusion, but I mis-explained a part, when I said "current directory" is added to the list, I meant, "The directory containing the file you are running" (My bad), which means, by running `PACKAGE_A.py` Python will start searching the contents of `PACKAGE_A` directory for imports, and not the `project name` directory, which means when you say `PACKAGE_A.module_a`, Python will search for `module_a.py` inside of `PACKAGE_A.py`, which is a file not a directory, thus the `ModuleNotFoundError: No module named 'PACKAGE_A.module_a'; 'PACKAGE_A' is not a package`. – Ghassen Manai Jun 06 '19 at 07:40
  • Thanks both of you guys. Would you consider moving all the packages in ```./project name/project name``` folder a better practice? I think this is what @gaFF suggested more or less. Or maybe suggest a "proper" structure for projects? – T81 Jun 06 '19 at 08:01
  • Other than taking advantage of `PYTHONPATH`, I would suggest giving packages and the modules they contain different names, having a `PACKAGE_A` that contains a `PACKAGE_A.py` file very confusing, and the more you do it the more confusing it gets. As for the project structure, it would depend on what each module or package is used for as well as personal preference, so I can't really give any advice for this example. – Ghassen Manai Jun 06 '19 at 08:06
  • 1
    This is a separate topic. You can find a lot of references on the Web, such as [the hitchhiker guide to Python](https://docs.python-guide.org/writing/structure/), [DEV community](https://dev.to/codemouse92/dead-simple-python-project-structure-and-imports-38c6), [Real Python](https://realpython.com/python-application-layouts/), and even on [stack overflow](https://stackoverflow.com/questions/193161/what-is-the-best-project-structure-for-a-python-application). – gaFF Jun 06 '19 at 08:46