Django 1.5 has experimental support for Python 3. This is great news, and means that we should all be upgrading our Django apps to be able to support both Python 2 and 3. Django has some documentation on how to go about this process. I recently went through this for one of my apps and, while the changes to the app itself were easy enough, I had to figure some things out myself for my testing setup.
I like to use Travis for continuous integration.
- It's easy to set up (I usually just copy a .travis.yaml file from another project and tweak a couple settings)
- It integrates really well with GitHub, even automatically running tests against people's pull requests
The .travis.yml for my projects usually looks something like this:
language: python
python:
- "2.6"
- "2.7"
env:
- DJANGO="Django==1.3"
- DJANGO="Django==1.4"
- DJANGO="git+git://github.com/django/django.git@master#egg=django"
# command to install dependencies
install: pip install -r requirements.txt
# command to run tests
script: python setup.py test
Then I'll list my requirements in requirements.txt, and put a test command in setup.py. Travis, when given this configuration, will run tests in six different environments:
- Python 2.6 with Django 1.3
- Python 2.6 with Django 1.4
- Python 2.6 with Django's trunk
- Python 2.7 with Django 1.3
- Python 2.7 with Django 1.4
- Python 2.7 with Django's trunk
Up until now, this has served me well.
With the introduction of Python 3 support, I introduce an additional Python environment for my tests:
python:
- "2.6"
- "2.7"
- "3.2"
Now Travis will run tests in 9 environments. However, in some of those environments, the tests will fail, even after I've done the work to make my app Python 3 compatible. So what happened?
The environments that Travis sets up are:
- Python 2.6 with Django 1.3
- Python 2.6 with Django 1.4
- Python 2.6 with Django's trunk
- Python 2.7 with Django 1.3
- Python 2.7 with Django 1.4
- Python 2.7 with Django's trunk
- Python 3.2 with Django 1.3
- Python 3.2 with Django 1.4
- Python 3.2 with Django's trunk
But notice that, even though my app may be compatible with Python 3, Django 1.3 and 1.4 are not. So, these environments will produce errors when even trying to install Django.
My solution to this is to introduce a couple of checks in my install and test running scripts. First, I have to make my Travis configuration a little smarter. I use a separate install script that checks whether the Django version is compatible with the Python version, and immediately exits if not:
.travis.yml:
language: python
python:
- "2.6"
- "2.7"
- "3.2"
env:
- DJANGO="Django==1.3"
- DJANGO="Django==1.4"
- DJANGO="Django==1.5"
- DJANGO="git+git://github.com/django/django.git@master#egg=django"
# command to install dependencies
install: ci/install.sh
# command to run tests
script: python setup.py test
ci/install.sh:
#!/bin/bash
PYTHON_VERSION=$(python --version 2>&1)
if [[ "$PYTHON_VERSION" > "Python 3" ]]
then
if [[ "$DJANGO" < "Django==1.5" ]]
then
echo "Cannot install $DJANGO on $PYTHON_VERSION"
exit
fi
fi
pip install six==1.2.0 mock==0.7.2 $DJANGO --use-mirrors
That line if [[ "$DJANGO" < "Django==1.5" ]]
in the ci/install.sh script does a lexicographic comparison against the $DJANGO
environment variable, so even when I'm using the Django trunk from git, it evaluates correctly as being greater than "Django==1.5"
(upper-case "D" is less than lower-case "g"). Now, Travis will immediately exit
(with an implicit exit code of 0
, indicating everything is alright) from installing my dependencies before installing six and Django, which is important for the next step.
Next, in the script that runs my tests, I want to check whether Django has been installed:
runtests.py:
try:
import six
from django.conf import settings
except ImportError:
print("Django has not been installed.")
sys.exit(0)
Here, it tries to import those things that did not finish installing above. If it cannot import them, then it will immediately exit. The exit code 0
tells Travis that all the tests for this environment have passed, which, since there are no tests for this [invalid] environment, they have.
Now my project is ready for testing with Travis under Python 2 and 3!
Update (11 March 2013):
I just learned about the matrix
section of a Travis config file! See here for an example.
No comments:
Post a Comment