1

I'm trying to run a python file interactively. The file imports other local modules. E.g., I have foo/bar.py and foo/baz.py. I want to run foo/bar.py interactively, and in foo/bar.py I have from foo import baz.

From foo's parent directory, I can run python -m foo.bar, and the import of baz works fine. But when I want to run interactively with python -i foo/bar.py, the import no longer works (the same is true if I do python foo/bar.py).

Why is that? Can I invoke python differently to get this to work? Am I organizing my code in a strange way?

alecbz
  • 6,292
  • 4
  • 30
  • 50
  • `python -m foo.bar` is calling `foo.py`, and its sub module `bar`. `python -i foo/bar.py` is directly calling `bar.py`. You are calling different files. – Sraw Jan 17 '18 at 01:09
  • @Sraw that's not really what's going on. `foo.py` doesn't even exist. – wim Jan 17 '18 at 01:22
  • @win I have realized that I used a wrong term, maybe it should be called `package` or `lib` or something else. Anyway, that is an unit in python's module path organization. – Sraw Jan 17 '18 at 01:35
  • Possible duplicate of [How to do relative imports in Python?](https://stackoverflow.com/questions/72852/how-to-do-relative-imports-in-python) – Davis Herring Jan 17 '18 at 03:15

1 Answers1

3

When using the -m option, the current directory will be added to the start of sys.path. In your case, that's the directory which contains foo. This allows the import statement in bar, i.e. from foo import baz, to be resolved correctly.

When not using the -m option, the first item in sys.path is the directory containing the script that was used to invoke the Python interpreter. In your case, that's one dir deeper than before - it's the directory /path/to/foo itself. This does not allow the import statement from foo import baz to be resolved correctly.

Can I invoke python differently to get this to work?

This should work:

PYTHONPATH=. python foo/bar.py

Am I organizing my code in a strange way?

Not really, but you'll need to get the parent directory of foo onto sys.path somehow. The best way is to write a setup.py file and then "install" your code with:

pip install --editable .
wim
  • 338,267
  • 99
  • 616
  • 750
  • A frequent example of `-m` : `timeit`'s command-line interface i.e. `~$ python3 -m timeit 'for i in range(10): oct(i)'`. – Brad Solomon Jan 17 '18 at 03:47
  • Beware that, with any of these approaches, `bar.py` can end up being executed **twice**: once as `__main__` and once as `foo.bar`. It’s best not to pretend that a normal module is sometimes a script; make a dedicated “module” to use with `-m`. You can call it `__main__.py` file and then use the _package_ name with `-m`. – Davis Herring Jan 17 '18 at 04:02
  • Assuming you're doing packaging anyway, then another option is to use the setuptools' `entry_points`/`console_scripts`. See [here](http://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation). It's easier to get right than the `__main__.py` stuff. – wim Jan 17 '18 at 04:12