5

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

Diptangsu Goswami
  • 5,554
  • 3
  • 25
  • 36
Christoph
  • 81
  • 3
  • why don't you `from sub import bar\nbar.test()` in `test.py` – Alan Hoover Jun 14 '20 at 17:25
  • @AlanHoover that should give the same error. I think python doesn't consider the self directory as a package which is why it can't import `from .` . – Diptangsu Goswami Jun 14 '20 at 17:26
  • @DiptangsuGoswami it works for `foo` why not `bar`? – Alan Hoover Jun 14 '20 at 17:28
  • I got the last comment wrong. Sorry. And OP wants to call `foo.py` as a separate python file as well as `test.py`. `test.py` isn't throwing errors. – Diptangsu Goswami Jun 14 '20 at 17:32
  • My point was don't try to access `bar` from `foo` in two different ways (directly and as a pass-through). Instead of doing the pass-through in `foo` to access `bar` from `test`, access `bar` directly from `test`. Then there is no confusion in `foo` because `foo` only needs to access `bar` directly. – Alan Hoover Jun 14 '20 at 17:36
  • Why not use `python -m sub.foo` and preserve the package structure? (Not that there aren’t still [problems doing that](https://stackoverflow.com/q/56194893/8586227), but it’s progress.) – Davis Herring Jun 14 '20 at 17:55

1 Answers1

1

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
Diptangsu Goswami
  • 5,554
  • 3
  • 25
  • 36