0

I have a directory and module design that looks like this:

MyProject --- - script.py
             |
              - helpers --- - __init__.py
                           |
                            - class_container.py
                           |
                            - helper.py
# class_container.py

class MyClass:
    pass
# helper.py

from class_container import MyClass
def func():
   # some code using MyClass
# script.py

from helpers.helper import func

When I run script.py:

ModuleNotFoundError: No module named 'class_container'

I tried changing the code in helper.py such as:

# helper.py

from helpers.class_container import MyClass
def func():
   # some code using MyClass

Then running script.py started working. But when I explicitly run helper.py:

ModuleNotFoundError: No module named 'helpers'

I want to be able run both script.py and helper.py separately without needing to change the code in any module.


Edit: I figured a solution which is changing helper.py such as:

from pathlib import Path
import sys

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

from class_container import MyClass
def func():
   # some code using MyClass

Basically I added the directory of helper.py to sys.path by using sys and pathlib modules and __file__ object. And unlike import statement's behaviour, __file__ will not forget it's roots when imported/used from a different module (i.e it won't become the path of script.py when imported into it. It'll always be the path of helper.py since it was initiated there.).

Though I'd appreciate if someone can show another way that doesn't involve messing with sys.path, it feels like an illegal, 'unpythonic' tool.

UpTheIrons
  • 73
  • 1
  • 6
  • `ModuleNotFoundError: No module named 'class_container.py'` Are you sure that is the exact error message? There shouldn't be a `.py` at the end. – John Gordon Nov 28 '22 at 23:34
  • @JohnGordon You are right. I fixed it. The actual name isn't class_container so I made a typo while changing the name – UpTheIrons Nov 28 '22 at 23:37
  • Or https://stackoverflow.com/questions/279237/import-a-module-from-a-relative-path - it seems you are lacking an `__init__.py` file in `helpers`. – mkrieger1 Nov 29 '22 at 00:51
  • @mkrieger1 There was already a `__init__.py` in `helpers`. I added it to the post. It's not the solution unfortunately. The question still applies. – UpTheIrons Nov 30 '22 at 00:17

1 Answers1

1

You have to create a __init__.py file in the MyProject/helpers directory. Maybe you already have created it. If not, create an empty file.

Then in the MyProject/helpers/helper.py, access the module helpers.class_container like this.

from helpers.class_container import MyClass
def func():
   # some code using MyClass

You can also use a relative import like this.

from .class_container import MyClass

If you want to run the MyProject/helpers/helper.py independently, add test code in helper.py like this.

from helpers.class_container import MyClass
def func():
   # some code using MyClass

if __name__ == '__main__':
    func()

And run like this in the MyProject directory.(I assume a Linux environment.)

$ python3 -m helpers.helper

The point is to differentiate Python modules from Python scripts and treat them differently.

relent95
  • 3,703
  • 1
  • 14
  • 17
  • I already have the `__init__.py` in `MyProject/helpers`. I had already stated that I tried your solution in my post. I said that this only works when I run `script.py`. I also want this to work when I explicitly run `helpers.py` – UpTheIrons Nov 30 '22 at 00:13
  • I added a solution to that case. – relent95 Nov 30 '22 at 00:59
  • Strange that it worked: `$ python3 -m helpers.helper`. It assigns `__name__` to `'__main__'` but still loads it as a module such that the relative import statement `from .class_container import MyClass` in `helper.py` runs without error. – UpTheIrons Nov 30 '22 at 03:49
  • It's the standard way to run a module. See [PEP 338](https://peps.python.org/pep-0338/). – relent95 Nov 30 '22 at 05:45