Christoph
Christoph

Reputation: 91

Proper python import for multiple main

I have the following file structure

├── test.py
└── sub
    ├── foo.py
    └── bar.py

With test.py:

# test.py
from sub import foo
foo.test()

And foo.py:

# foo.py
from . import bar

def test():
  bar.test()

And bar.py:

# bar.py
def test():
  print('This is a test')

This works fine when invoking test.py from the command line.

home/$ python test.py
This is a test

Now I want to additionally be able to call foo.py directly from command line. So I change foo.py to:

# foo.py
from . import bar

def test():
  bar.test()

if __name__ == '__main__':
  test()

But when I call this it does not work

/home$ cd sub
/home/sub$ python bar.py
Traceback (most recent call last):
  File "foo.py", line 1, in <module>
    from . import bar
ImportError: cannot import name 'bar' from '__main__' (foo.py)

This makes sense since the python documentation states

Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.

So I can change the import statement in foo.py to

# foo.py
import bar

But then calling test.py does not work anymore.

/home$ python test.py
Traceback (most recent call last):
  File "test.py", line 1, in <module>
    from sub import foo
  File "/home/chr/code/import/sub/foo.py", line 1, in <module>
    import bar
ModuleNotFoundError: No module named 'bar'

Is there any way around this or is it simply not possible to use bar.py from two different mains?

Edit: One solution could be

if __name__ == '__main__':
    import bar
else:
    from . import bar

But this looks pretty ugly

Upvotes: 6

Views: 3066

Answers (1)

Diptangsu Goswami
Diptangsu Goswami

Reputation: 5945

The solution you provided in the question is definitely one of them.

I'm afraid the other solution I have to offer isn't pretty either. But since it's another solution, I'm writing an answer.

Make these few changes and it should work.

# bar.py
import bar  # use an absolute import

def test():
  bar.test()

if __name__ == '__main__':
  test()

Add these two lines to test.py. Adding a path to sys.path will make that location (module) available for imports. You can simply import the module after that.

import sys
sys.path.append('./sub')

import foo

foo.test()

This is my structure, same as yours.

.
├── sub
│   ├── bar.py
│   └── foo.py
└── test.py

Running both the files work.

$ python3 test.py 
This is a test
$ python3 sub/foo.py 
This is a test

Upvotes: 1

Related Questions