diff --git a/.gitignore b/.gitignore index 640eb65128..fbd481b86a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ rpm-build coverage.xml pep8.txt .vagrant* - +.tox diff --git a/ansibleworks/main/admin.py b/ansibleworks/main/admin.py index 5e9927cf1c..b3f48309b0 100644 --- a/ansibleworks/main/admin.py +++ b/ansibleworks/main/admin.py @@ -12,12 +12,13 @@ from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ -from django.utils.html import format_html +from django.contrib.auth.models import User +from django.contrib.auth.admin import UserAdmin + +from ansibleworks.main.compat import format_html from ansibleworks.main.models import * from ansibleworks.main.forms import * -from django.contrib.auth.models import User -from django.contrib.auth.admin import UserAdmin class UserAdmin(UserAdmin): fieldsets = ( diff --git a/ansibleworks/main/compat.py b/ansibleworks/main/compat.py new file mode 100644 index 0000000000..3e4955b26d --- /dev/null +++ b/ansibleworks/main/compat.py @@ -0,0 +1,23 @@ +''' +Compability library for support of both Django 1.4.x and Django 1.5.x. +''' + +try: + from django.utils.html import format_html +except ImportError: + from django.utils.html import conditional_escape + from django.utils.safestring import mark_safe + def format_html(format_string, *args, **kwargs): + args_safe = map(conditional_escape, args) + kwargs_safe = dict([(k, conditional_escape(v)) for (k, v) in + kwargs.items()]) + return mark_safe(format_string.format(*args_safe, **kwargs_safe)) + +try: + from django.utils.log import RequireDebugTrue +except ImportError: + import logging + from django.conf import settings + class RequireDebugTrue(logging.Filter): + def filter(self, record): + return settings.DEBUG diff --git a/ansibleworks/main/models/__init__.py b/ansibleworks/main/models/__init__.py index eae5e6858d..f305211a9a 100644 --- a/ansibleworks/main/models/__init__.py +++ b/ansibleworks/main/models/__init__.py @@ -131,6 +131,17 @@ class PrimordialModel(models.Model): def __unicode__(self): return unicode("%s-%s"% (self.name, self.id)) + def save(self, *args, **kwargs): + # For compatibility with Django 1.4.x, attempt to handle any calls to + # save that pass update_fields. + try: + super(PrimordialModel, self).save(*args, **kwargs) + except TypeError: + if 'update_fields' not in kwargs: + raise + kwargs.pop('update_fields') + super(PrimordialModel, self).save(*args, **kwargs) + def mark_inactive(self, save=True): '''Use instead of delete to rename and mark inactive.''' if self.active: @@ -845,10 +856,13 @@ class Job(CommonModel): self.status = 'pending' self.save(update_fields=['status']) task_result = RunJob().delay(self.pk, **opts) + # Reload job from database so we don't clobber results from RunJob + # (mainly from tests when using Djanog 1.4.x). + job = Job.objects.get(pk=self.pk) # The TaskMeta instance in the database isn't created until the worker # starts processing the task, so we can only store the task ID here. - self.celery_task_id = task_result.task_id - self.save(update_fields=['celery_task_id']) + job.celery_task_id = task_result.task_id + job.save(update_fields=['celery_task_id']) return True @property diff --git a/ansibleworks/settings/defaults.py b/ansibleworks/settings/defaults.py index 7a8003435f..06190a2f26 100644 --- a/ansibleworks/settings/defaults.py +++ b/ansibleworks/settings/defaults.py @@ -252,7 +252,7 @@ LOGGING = { '()': 'django.utils.log.RequireDebugFalse', }, 'require_debug_true': { - '()': 'django.utils.log.RequireDebugTrue', + '()': 'ansibleworks.main.compat.RequireDebugTrue', }, }, 'formatters': { diff --git a/requirements/test.txt b/requirements/test.txt new file mode 100644 index 0000000000..b1fd4e7555 --- /dev/null +++ b/requirements/test.txt @@ -0,0 +1,15 @@ +# PIP requirements for AnsibleWorks testing using Tox (downloaded from +# PyPI). Install using "pip -r test.txt". + +ansible==1.2 +django-celery +django-extensions +django-jsonfield +django-taggit +djangorestframework>=2.3.0,<2.4.0 +Markdown +pexpect +PyYAML +python-dateutil +requests +South>=0.8,<2.0 diff --git a/setup.py b/setup.py index cd8b28b760..7e8fe85ac3 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ # Copyright (c) 2013 AnsibleWorks, Inc. # All Rights Reserved. -import os, datetime, glob +import os, datetime, glob, sys from setuptools import setup, find_packages from ansibleworks import __version__ @@ -42,6 +42,12 @@ def proc_data_files(data_files): """ result = [] + + # Ff running in a virtualenv, don't return data files that would install to + # system paths (mainly useful for running tests via tox). + if hasattr(sys, 'real_prefix'): + return result + for dir,files in data_files: includes = [] for item in files: diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000000..d1a5ac7239 --- /dev/null +++ b/tox.ini @@ -0,0 +1,37 @@ +[tox] +envlist = + py26-dj14, py27-dj14, py26-dj15, py27-dj15 + +[testenv] +commands = python manage.py test main +deps = + -r{toxinidir}/requirements/test.txt +setenv = + DJANGO_SETTINGS_MODULE = ansibleworks.settings.development + # For OS X to be able to install pycrypto. + CFLAGS=-I/opt/local/include +downloadcache = {toxworkdir}/cache + +[testenv:py26-dj14] +basepython = python2.6 +deps = + Django==1.4.5 + {[testenv]deps} + +[testenv:py27-dj14] +basepython = python2.7 +deps = + Django==1.4.5 + {[testenv]deps} + +[testenv:py26-dj15] +basepython = python2.6 +deps = + Django==1.5.1 + {[testenv]deps} + +[testenv:py27-dj15] +basepython = python2.7 +deps = + Django==1.5.1 + {[testenv]deps}