When hacking on even single file scripts, you almost always will want a virtual environment to pull in that library with that one function you don’t want to rewrite. That’s all fine and dandy, except there suddenly becomes a lot of mental overhead to managing and cleaning up after the virtual env. A simple makefile can go a long way to keep a simple script easy to run.

.PHONY: clean run shell test

SHELL = /bin/bash
FILE = main.py
ARGS =

venv: requirements.txt
	rm -rf venv && \
	python3 -m venv venv && \
	source venv/bin/activate && \
	pip install --upgrade pip && \
	pip install -r requirements.txt && \
	touch venv;

clean:
	rm -rf venv

shell: venv
	source venv/bin/activate && \
	python3

test: venv
	source venv/bin/activate && \
	python3 -B -m unittest discover -v;

run: venv
	source venv/bin/activate && \
	python3 -B $(FILE) $(ARGS)

There’s few nice things going on here:

  • You’ll get a new virtual environment venv whenever requirements.txt is modified.
  • make run will execute main.py in the virtual env by default, but you can point that to another file with make run FILE=foo/bar.py
  • python is run with -B to prevent generation of compiled bytecode files

The downside is that argument passing is more annoying: make run ARGS='baz qux' But I find this to be, in general, easier than using Pipenv or Poetry when working on small projects.