PythonPathSadness
Anthony Sottile edited this page May 18, 2022
·
4 revisions
python path/to/file.py
puts path/to
on the beginning of the PYTHONPATH
(sys.path
)
this has some somewhat odd consequences:
Here's a toy example of where this can go horrible:
$ tree
.
├── mod1
│ ├── submod1.py
│ ├── submod2.py
│ └── __init__.py
└── mod2.py
$ cat mod1/submod1.py
from __future__ import absolute_import
import sys
print('sys.argv[0]: {0!r}'.format(sys.argv[0]))
print('sys.path[0]: {0!r}'.format(sys.path[0]))
from mod2 import baz
print('got baz: {0}'.format(baz))
$ cat mod2.py
baz = 1
Running via file path
$ python mod1/submod1.py
sys.argv[0]: 'mod1/submod1.py'
sys.path[0]: '/tmp/foo/mod1'
Traceback (most recent call last):
File "mod1/submod1.py", line 8, in <module>
from mod2 import baz
ImportError: cannot import name baz
Running via -m
(which works great!)
$ python -m mod1.submod1
sys.argv[0]: '/tmp/foo/mod1/submod1.py'
sys.path[0]: ''
got baz: 1
Still a problem in python3 too \o/
The above problem can be worked around with PYTHONPATH, here's a related problem which cannot:
$ tree .
.
├── mod1
│ ├── __init__.py
│ ├── mod2.py
│ └── run.py
└── mod2
└── __init__.py
2 directories, 4 files
$ find -name '*.py' | xargs tail -n 999
==> ./mod1/run.py <==
from __future__ import absolute_import
from mod2 import IMPORT_TARGET
print(IMPORT_TARGET)
==> ./mod1/__init__.py <==
==> ./mod1/mod2.py <==
# no IMPORT_TARGET here!
==> ./mod2/__init__.py <==
IMPORT_TARGET = 'hello world!'
$ python -m mod1.run
hello world!
$ python mod1/run.py
Traceback (most recent call last):
File "mod1/run.py", line 3, in <module>
from mod2 import IMPORT_TARGET
ImportError: cannot import name IMPORT_TARGET