I'm a newbie to python(3), but not to programming in general.
I'd like to distribute a git repo with myprogram consisting of these files:
requirements.txt
myprogram.py
lib/modulea.py
lib/moduleb.py
My question is: What is the best-practice and least surprising way to let users run myprogram.py
using the dependencies in requirements.txt
? So that after git clone
, and some idiomatic installation command(s), ./myprogram.py
or /some/path/to/myprogram.py
"just works" without having to first set magical venv
or python3
environment variables?
I want to be able to run it using the #!
shebang so that /path/to/myprogram.py
and double-clicking it from the file manager GUI does the correct thing.
I already know I can create a wrapper.sh
or make a clever shebang line. But I'm looking for the best-practice approach, since I'm new to python.
I'm guessing that users would
git clone $url workdir
cd workdir
python3 -m venv .
./bin/pip install -r requirements.txt
And from now on this uses the modules from requirements.txt
:
./myprogram.py
If I knew that the project directory was always /home/peter/workdir
, I could start the myprogram.py
with:
#!/home/peter/workdir/bin/python3
but I'd like to avoid hard-coding the project directory in myprogram.py
.
This also seems to work in my tiny demo, but clearly this is brittle and not best-practice, but it illustrates what I'm trying to do:
#!/usr/bin/env python3
import os
import sys
print(os.path.join(os.path.dirname(__file__), 'lib', 'python3.10', 'site-packages'))
I'm sure I could come up with some home-grown shebang line that works, but what is the idiomatic way to do this in python3?
Again: After pip install
, I absolutely refuse to have to to set any environment variables or call any setup code in future shells before running myprogram.py
. (Unless that strongly conflicts with "idiomatic", which I hope isn't the case)...
Expanding @sinoroc's comment into an answer:
I've looked at https://packaging.python.org/en/latest/tutorials/packaging-projects/ and also at "entrypoints", and this is the smallest example I can think of. Create an empty directory with these two files:
pyproject.toml
:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "example_module_pmorch"
version = "0.0.1"
# Just a bogus dependency for illustration
dependencies = [
"tqdm"
]
[project.scripts]
runme = "example_module_pmorch:cli_main"
src/example_module_pmorch/__init__.py
:
def cli_main():
print("I'm the entrypoint")
Now if I run this:
$ python3 -m venv .
# Adding -e during development is optional
$ ./bin/pip install .
Then ./bin/runme
does the right thing and prints I'm the entrypoint
.
And during development:
PYTHONPATH=./src:$PYTHONPATH \
./bin/python -c 'import similar_images similar_images.cli_main()'