If you have a ruby or node.js background, you probably came to appreciate gem and npm.
Python is lacking in this respect.
Current tools fail “There should be one– and preferably only one –obvious way to do it.”.
We show how to leverage pip and virtualenv in a non-obvious way.
There are several ways to deploy a python app with dependencies:
- Build a deb (or rpm,…)
- Install it into a docker container
- Use setup.py
- or use pip, virtualenv (and make) as shown here
I decided to go with bare pip and virtualenv since both tools have to be mastered in anyway. The rest of this 1approach is simple enough to be described here. We assume the project has a standard directory layout:
README.rst LICENSE run.py setup.py requirements.txt sample/__init__.py sample/core.py sample/helpers.py docs/conf.py docs/index.rst tests/test_basic.py tests/test_advanced.py
Now enter this directory and do (assuming it is a python3 project):
virtualenv -p python3 --prompt="(myproject)" env
This creates a directory env that holds your dependencies. You may want to add env to the .gitignore-file such that it does not end up under version control.
Next we activate the virtualenv.
source env/bin/activate
Note, that “(myproject)” became part of the prompt.
Then install the dependencies (into env) using pip:
pip install -r requitements.txt
Now you can run your project (here run.py) by:
python3 run.py #or ./run.py
Once you finished working with your project you can re-enable the global python environment by typing:
deactivate
Great, so whenever you want to run your project you have to setup the virtualenv (i.e. by using source env/bin/activate
).
You can wrap that in a shell-script:
#!/usr/bin/env bash # activate env-directory relative to script # cf. http://stackoverflow.com/questions/5137497/find-current-directory-and-files-directory DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source "$DIR/../env/bin/activate" python "$DIR/run.sh"
Or you can enforce the virtualenv from within your python script:
#!/usr/bin/env python3 ### activate virtualenv import os script_path = os.path.dirname(os.path.realpath(__file__)) activate_this = os.path.join(script_path,'env','bin','activate_this.py') with open(activate_this) as file_: exec(file_.read(), dict(__file__=activate_this)) ### # ... here comes your stuff
For convenience you may want to put the setup commands into a Makefile (make sure to indent by tab):
.PHONY: init requirements checkvirtualenv init: requirements requirements: env checkvirtualenv requirements.txt pip3 install -r requirements.txt env: virtualenv -p python3 env --prompt="(myproject)" checkvirtualenv: test -n "$(VIRTUAL_ENV)" || \ (echo "did you source env/bin/activate ?" && \ exit 1)
and after checkout the next developer may do:
make #sets up env, then fails because it needs to be activated. source env/bin/activate make #now the requirements are loaded python run.py
Further references
- A promising development to simplify it may be pipenv
- Taming dependencies with pip slides
- Smartly pin versions in requirements.txt
- I did not find this convincing hence wrote this post