How can I write
import foo.bar in
__init__.py so it will load the system-wide version of
foo/bar.py when run from most places, but will load the local version of
bar.py when run from within
foo‘s source directory?
# foo/__init__.py from foo.bar import baz baz()
# foo/bar.py def baz: print('Hello")
This will always load the site-wide version of
foo/bar.py, and never the local version.
From examples of other packages I have installed on-site, imports should be done with
import foo.bar. (examples
scipy), but I’m not sure how they manage to develop these libraries (perhaps they always use virtual environments).
I also tried:
# foo/__init__.py try: from bar import baz except ModuleNotFoundError: from foo.bar import baz baz()
This does work, but it is pretty verbose to repeat everywhere in the library, and it seems prone to name-clashes.
but I’m not sure how they manage to develop these libraries (perhaps they always use virtual environments)
Yes, I think 99% or so of python dev work uses virtualenvs. They’re really not too hard—you might want to have a look at something like poetry to manage them. There’s also the pep582 approach, but that hasn’t yet made it. However you can spin up a venv really easily these days:
cd path/to/project python3 -m venv .venv source .venv/bin/activate # and then something like pip install -e . # installs in editable mode
If you don’t want to do this, you can still have an
__init__ which loads the right files, by using relative paths:
# foo/__init__.py from .bar import baz
…at the cost of not being able to evaluate the
__init__ in an editor. But you can still run it locally without installing! Just use:
python -m foo
from the dir above foo. Likewise you can use
python -m foo.bar
To load and execute
foo/bar.py (assuming it does anything).
Note that if you take the usual approach of installing your package in editable mode inside a virtualenv and then working there, there’s no reason not to use the
foo.bar approach—and you can evaluate stuff without worrying about what
. means. Lots of people don’t like relative paths for exactly that reason.
If you do need to evaluate bits of
foo/__init__.py, first do the imports manually in your repl and then send only the lines you care about.