Python imports in deployed/local packages

Advertisements

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 numba, pandas, 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.

>Solution :

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.

Leave a Reply Cancel reply