Python's import situation is terrible and unusable. imsanity
makes it usable.
pip install imsanity
Say you have a python project (not a package), with the following structure:
project_dir/
|-- bin/
| |-- web_server.py
|-- lib/
| |-- __init__.py
| |-- email.py
| |-- test_email.py
|-- tools/
| |-- reports.py
import email
will break your code in unexpected ways if some other package imports email, the core python packagefrom . import email
works, but won't work if you try to runpython test_email.py
. You can't even runpython -m test_email
. You have to runpython -m lib.test_email
instead, which is annoying and unexpected.
from ..lib import email
is the only thing that works, but it doesn't allow you to run./reports.py
. You can't runpython -m reports
, either. You can't even runpython -m tools.reports
! You have to put an__init__.py
file in both project_dir and the tools directory, cd to project_dir's parent directory, and run it withpython -m rootdir.tools.reports
. This is just unusable.- If you move
reports.py
to a subfolder inside tools, you'll have to update all its imports
Sure, you could do that. But then you have to manage this on every machine you work on. And if you have multiple projects like this, you have to make sure the PYTHONPATH is set correctly for each one.
Maybe you can just add all the projects to your PYTHONPATH? But, god forbid you have a library with the same name in different projects, good luck figuring out that mess. So, that's probably not a good idea.
The only safe thing to do is add to the PYTHONPATH whenever you start working on a project, and remember to remove from the PYTHONPATH (and add the new project's base directory to the PYTHONPATH) whenever you switch projects. Sounds terrible, eh?
Simply place a .imsanity
file in the project root. Then, import imsanity
at the top of any file. PYTHONPATH will now include the project root.
from lib import email
works, and allows you to run python test_email.py
from lib import email
works, and allows you to run ./reports.py
imsanity
starts at the directory of the python file being executed (or if loaded interactively, the CWD), and goes up the directory tree until it finds a .imsanity
file. It then adds the directory of the .imsanity
file to the PYTHONPATH.
Well... there's PEP 0366, which recommends you put this:
if __name__ == "__main__" and __package__ is None:
__package__ = "expected.package.name"
at the top of every file. Key quotes:
- this boilerplate is sufficient only if the top level package is already accessible via
sys.path
- if the script is moved to a different package or subpackage, the boilerplate will need to be updated manually
My only response to this is: ahahahahahahahahaha :'(
It's so sad that such a beautiful language has such a broken import system.