From e5dc7f65b332d73c1ef8361500146e78bd282f88 Mon Sep 17 00:00:00 2001 From: Christopher Grebs Date: Wed, 27 Apr 2016 23:12:49 +0200 Subject: [PATCH] Facelift, support for py3 and modern django version, wheel support. (#61) * Facelift, support for py3 and modern django version, wheel support. * Add support for Python 3 and PyPy * Move to pytest for testing * Add wheel build support * Drops support for Django < 1.6, adds support for Django 1.6, 1.7 1.8 and 1.9 This adds a proper tox config and uses this in travis, moves to pytest instead of nose and cleans up some old stuff. --- .gitignore | 2 + .travis.yml | 33 +++- CHANGES | 11 ++ csp/__init__.py | 2 - csp/tests/__init__.py | 3 - csp/tests/settings.py | 19 +++ csp/tests/test_decorators.py | 105 +++++++------ csp/tests/test_middleware.py | 139 ++++++++--------- csp/tests/test_utils.py | 283 +++++++++++++++++++---------------- csp/utils.py | 22 +-- docs/contributing.rst | 11 +- requirements.txt | 4 - run.sh | 20 --- setup.cfg | 7 + setup.py | 64 +++++++- test_settings.py | 24 --- tox.ini | 89 +++++++++++ 17 files changed, 506 insertions(+), 332 deletions(-) create mode 100644 csp/tests/settings.py delete mode 100755 run.sh create mode 100644 setup.cfg create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index eaf6e11..9bdc04d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ *.egg-info *.db *.sw[po] +.cache +.tox dist diff --git a/.travis.yml b/.travis.yml index b50f8c2..4af67d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,28 @@ +sudo: false + language: python -env: - - DJANGO_VERSION=1.4.5 - - DJANGO_VERSION=1.5.1 + python: - - "2.6" - - "2.7" -install: pip install -q Django==${DJANGO_VERSION} --use-mirrors -script: ./run.sh test + - '2.7' + - '3.4' + - '3.5' + - pypy + +env: + - DJANGO_VERSION=1.9.x + - DJANGO_VERSION=1.8.x + - DJANGO_VERSION=1.7.x + - DJANGO_VERSION=1.6.x + +matrix: + exclude: + - python: '3.5' + env: DJANGO_VERSION=1.7.x + - python: '3.5' + env: DJANGO_VERSION=1.6.x + +install: + - pip install tox + +script: + - tox -e "$TRAVIS_PYTHON_VERSION-$DJANGO_VERSION" diff --git a/CHANGES b/CHANGES index 395d76b..2aa45a9 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,17 @@ CHANGES ======= +v3.0 +==== + +Currenty in development. + + * Add support for Python 3 and PyPy + * Move to pytest for testing + * Add wheel build support + * Drops support for Django < 1.6, adds support for Django 1.6, 1.7, 1.8 and 1.9 + + v2.0.3 ====== diff --git a/csp/__init__.py b/csp/__init__.py index 7e951ca..e69de29 100644 --- a/csp/__init__.py +++ b/csp/__init__.py @@ -1,2 +0,0 @@ -VERSION = (2, 0, 3) -__version__ = '.'.join(map(str, VERSION)) diff --git a/csp/tests/__init__.py b/csp/tests/__init__.py index c20ba94..e69de29 100644 --- a/csp/tests/__init__.py +++ b/csp/tests/__init__.py @@ -1,3 +0,0 @@ -from csp.tests.test_decorators import DecoratorTests # noqa -from csp.tests.test_middleware import MiddlewareTests # noqa -from csp.tests.test_utils import UtilsTests # noqa diff --git a/csp/tests/settings.py b/csp/tests/settings.py new file mode 100644 index 0000000..43ad748 --- /dev/null +++ b/csp/tests/settings.py @@ -0,0 +1,19 @@ +CSP_REPORT_ONLY = False + +DATABASES = { + 'default': { + 'NAME': 'test.db', + 'ENGINE': 'django.db.backends.sqlite3', + } +} + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'csp', +) + +SECRET_KEY = 'csp-test-key' diff --git a/csp/tests/test_decorators.py b/csp/tests/test_decorators.py index 1ac9eb6..3da50ab 100644 --- a/csp/tests/test_decorators.py +++ b/csp/tests/test_decorators.py @@ -1,58 +1,65 @@ from django.http import HttpResponse -from django.test import RequestFactory, TestCase +from django.test import RequestFactory from django.test.utils import override_settings -from nose.tools import eq_ - from csp.decorators import csp, csp_replace, csp_update, csp_exempt from csp.middleware import CSPMiddleware + REQUEST = RequestFactory().get('/') mw = CSPMiddleware() -class DecoratorTests(TestCase): - def test_csp_exempt(self): - @csp_exempt - def view(request): - return HttpResponse() - response = view(REQUEST) - assert response._csp_exempt - - @override_settings(CSP_IMG_SRC=['foo.com']) - def test_csp_update(self): - @csp_update(IMG_SRC='bar.com') - def view(request): - return HttpResponse() - response = view(REQUEST) - eq_(response._csp_update, {'img-src': 'bar.com'}) - - @override_settings(CSP_IMG_SRC=['foo.com']) - def test_csp_replace(self): - @csp_replace(IMG_SRC='bar.com') - def view(request): - return HttpResponse() - response = view(REQUEST) - eq_(response._csp_replace, {'img-src': 'bar.com'}) - - def test_csp(self): - @csp(IMG_SRC=['foo.com'], FONT_SRC=['bar.com']) - def view(request): - return HttpResponse() - response = view(REQUEST) - eq_(response._csp_config, - {'img-src': ['foo.com'], 'font-src': ['bar.com']}) - mw.process_response(REQUEST, response) - policy_list = sorted(response['Content-Security-Policy'].split("; ")) - eq_(policy_list, ["font-src bar.com", "img-src foo.com"]) - - def test_csp_string_values(self): - # Test backwards compatibility where values were strings - @csp(IMG_SRC='foo.com', FONT_SRC='bar.com') - def view(request): - return HttpResponse() - response = view(REQUEST) - eq_(response._csp_config, - {'img-src': ['foo.com'], 'font-src': ['bar.com']}) - mw.process_response(REQUEST, response) - policy_list = sorted(response['Content-Security-Policy'].split("; ")) - eq_(policy_list, ["font-src bar.com", "img-src foo.com"]) + +def test_csp_exempt(): + @csp_exempt + def view(request): + return HttpResponse() + response = view(REQUEST) + assert response._csp_exempt + + +@override_settings(CSP_IMG_SRC=['foo.com']) +def test_csp_update(): + @csp_update(IMG_SRC='bar.com') + def view(request): + return HttpResponse() + response = view(REQUEST) + assert response._csp_update == {'img-src': 'bar.com'} + + +@override_settings(CSP_IMG_SRC=['foo.com']) +def test_csp_replace(): + @csp_replace(IMG_SRC='bar.com') + def view(request): + return HttpResponse() + response = view(REQUEST) + assert response._csp_replace == {'img-src': 'bar.com'} + + +def test_csp(): + @csp(IMG_SRC=['foo.com'], FONT_SRC=['bar.com']) + def view(request): + return HttpResponse() + response = view(REQUEST) + assert response._csp_config == { + 'img-src': ['foo.com'], 'font-src': ['bar.com'] + } + + mw.process_response(REQUEST, response) + policy_list = sorted(response['Content-Security-Policy'].split("; ")) + assert policy_list == ["font-src bar.com", "img-src foo.com"] + + +def test_csp_string_values(): + # Test backwards compatibility where values were strings + @csp(IMG_SRC='foo.com', FONT_SRC='bar.com') + def view(request): + return HttpResponse() + response = view(REQUEST) + assert response._csp_config == { + 'img-src': ['foo.com'], 'font-src': ['bar.com'] + } + + mw.process_response(REQUEST, response) + policy_list = sorted(response['Content-Security-Policy'].split("; ")) + assert policy_list == ["font-src bar.com", "img-src foo.com"] diff --git a/csp/tests/test_middleware.py b/csp/tests/test_middleware.py index dea150f..7482734 100644 --- a/csp/tests/test_middleware.py +++ b/csp/tests/test_middleware.py @@ -1,9 +1,7 @@ from django.http import HttpResponse, HttpResponseServerError -from django.test import RequestFactory, TestCase +from django.test import RequestFactory from django.test.utils import override_settings -from nose.tools import eq_ - from csp.middleware import CSPMiddleware @@ -12,67 +10,74 @@ rf = RequestFactory() -class MiddlewareTests(TestCase): - def test_add_header(self): - request = rf.get('/') - response = HttpResponse() - mw.process_response(request, response) - assert HEADER in response - - def test_exempt(self): - request = rf.get('/') - response = HttpResponse() - response._csp_exempt = True - mw.process_response(request, response) - assert HEADER not in response - - @override_settings(CSP_EXCLUDE_URL_PREFIXES=('/inlines-r-us')) - def text_exclude(self): - request = rf.get('/inlines-r-us/foo') - response = HttpResponse() - mw.process_response(request, response) - assert HEADER not in response - - @override_settings(CSP_REPORT_ONLY=True) - def test_report_only(self): - request = rf.get('/') - response = HttpResponse() - mw.process_response(request, response) - assert HEADER not in response - assert HEADER + '-Report-Only' in response - - def test_dont_replace(self): - request = rf.get('/') - response = HttpResponse() - response[HEADER] = 'default-src example.com' - mw.process_response(request, response) - eq_(response[HEADER], 'default-src example.com') - - def test_use_config(self): - request = rf.get('/') - response = HttpResponse() - response._csp_config = {'default-src': ['example.com']} - mw.process_response(request, response) - eq_(response[HEADER], 'default-src example.com') - - def test_use_update(self): - request = rf.get('/') - response = HttpResponse() - response._csp_update = {'default-src': ['example.com']} - mw.process_response(request, response) - eq_(response[HEADER], "default-src 'self' example.com") - - @override_settings(CSP_IMG_SRC=['foo.com']) - def test_use_replace(self): - request = rf.get('/') - response = HttpResponse() - response._csp_replace = {'img-src': ['bar.com']} - mw.process_response(request, response) - eq_(response[HEADER], "default-src 'self'; img-src bar.com") - - @override_settings(DEBUG=True) - def test_debug_exempt(self): - request = rf.get('/') - response = HttpResponseServerError() - mw.process_response(request, response) - assert HEADER not in response +def test_add_header(): + request = rf.get('/') + response = HttpResponse() + mw.process_response(request, response) + assert HEADER in response + + +def test_exempt(): + request = rf.get('/') + response = HttpResponse() + response._csp_exempt = True + mw.process_response(request, response) + assert HEADER not in response + + +@override_settings(CSP_EXCLUDE_URL_PREFIXES=('/inlines-r-us')) +def text_exclude(): + request = rf.get('/inlines-r-us/foo') + response = HttpResponse() + mw.process_response(request, response) + assert HEADER not in response + + +@override_settings(CSP_REPORT_ONLY=True) +def test_report_only(): + request = rf.get('/') + response = HttpResponse() + mw.process_response(request, response) + assert HEADER not in response + assert HEADER + '-Report-Only' in response + + +def test_dont_replace(): + request = rf.get('/') + response = HttpResponse() + response[HEADER] = 'default-src example.com' + mw.process_response(request, response) + assert response[HEADER] == 'default-src example.com' + + +def test_use_config(): + request = rf.get('/') + response = HttpResponse() + response._csp_config = {'default-src': ['example.com']} + mw.process_response(request, response) + assert response[HEADER] == 'default-src example.com' + + +def test_use_update(): + request = rf.get('/') + response = HttpResponse() + response._csp_update = {'default-src': ['example.com']} + mw.process_response(request, response) + assert response[HEADER] == "default-src 'self' example.com" + + +@override_settings(CSP_IMG_SRC=['foo.com']) +def test_use_replace(): + request = rf.get('/') + response = HttpResponse() + response._csp_replace = {'img-src': ['bar.com']} + mw.process_response(request, response) + assert response[HEADER] == "default-src 'self'; img-src bar.com" + + +@override_settings(DEBUG=True) +def test_debug_exempt(): + request = rf.get('/') + response = HttpResponseServerError() + mw.process_response(request, response) + assert HEADER not in response diff --git a/csp/tests/test_utils.py b/csp/tests/test_utils.py index 5de9385..afdd880 100644 --- a/csp/tests/test_utils.py +++ b/csp/tests/test_utils.py @@ -1,8 +1,5 @@ -from django.test import TestCase from django.test.utils import override_settings -from nose.tools import eq_ - from csp.utils import build_policy @@ -12,132 +9,154 @@ def policy_eq(a, b, msg='%r != %r'): assert parts_a == parts_b, msg % (a, b) -class UtilsTests(TestCase): - def test_empty_policy(self): - policy = build_policy() - eq_("default-src 'self'", policy) - - @override_settings(CSP_DEFAULT_SRC=['example.com', 'example2.com']) - def test_default_src(self): - policy = build_policy() - eq_('default-src example.com example2.com', policy) - - @override_settings(CSP_SCRIPT_SRC=['example.com']) - def test_script_src(self): - policy = build_policy() - policy_eq("default-src 'self'; script-src example.com", policy) - - @override_settings(CSP_FORM_ACTION=['example.com']) - def test_form_action(self): - policy = build_policy() - policy_eq("default-src 'self'; form-action example.com", policy) - - @override_settings(CSP_BASE_URI=['example.com']) - def test_base_uri(self): - policy = build_policy() - policy_eq("default-src 'self'; base-uri example.com", policy) - - @override_settings(CSP_CHILD_SRC=['example.com']) - def test_child_src(self): - policy = build_policy() - policy_eq("default-src 'self'; child-src example.com", policy) - - @override_settings(CSP_FRAME_ANCESTORS=['example.com']) - def test_frame_ancestors(self): - policy = build_policy() - policy_eq("default-src 'self'; frame-ancestors example.com", policy) - - @override_settings(CSP_OBJECT_SRC=['example.com']) - def test_object_src(self): - policy = build_policy() - policy_eq("default-src 'self'; object-src example.com", policy) - - @override_settings(CSP_STYLE_SRC=['example.com']) - def test_style_src(self): - policy = build_policy() - policy_eq("default-src 'self'; style-src example.com", policy) - - @override_settings(CSP_IMG_SRC=['example.com']) - def test_img_src(self): - policy = build_policy() - policy_eq("default-src 'self'; img-src example.com", policy) - - @override_settings(CSP_MEDIA_SRC=['example.com']) - def test_media_src(self): - policy = build_policy() - policy_eq("default-src 'self'; media-src example.com", policy) - - @override_settings(CSP_FRAME_SRC=['example.com']) - def test_frame_src(self): - policy = build_policy() - policy_eq("default-src 'self'; frame-src example.com", policy) - - @override_settings(CSP_FONT_SRC=['example.com']) - def test_font_src(self): - policy = build_policy() - policy_eq("default-src 'self'; font-src example.com", policy) - - @override_settings(CSP_CONNECT_SRC=['example.com']) - def test_connect_src(self): - policy = build_policy() - policy_eq("default-src 'self'; connect-src example.com", policy) - - @override_settings(CSP_SANDBOX=['allow-scripts']) - def test_sandbox(self): - policy = build_policy() - policy_eq("default-src 'self'; sandbox allow-scripts", policy) - - @override_settings(CSP_SANDBOX=[]) - def test_sandbox_empty(self): - policy = build_policy() - policy_eq("default-src 'self'; sandbox ", policy) - - @override_settings(CSP_REPORT_URI='/foo') - def test_report_uri(self): - policy = build_policy() - policy_eq("default-src 'self'; report-uri /foo", policy) - - @override_settings(CSP_IMG_SRC=['example.com']) - def test_update_img(self): - policy = build_policy(update={'img-src': 'example2.com'}) - policy_eq("default-src 'self'; img-src example.com example2.com", - policy) - - def test_update_missing_setting(self): - """update should work even if the setting is not defined.""" - policy = build_policy(update={'img-src': 'example.com'}) - policy_eq("default-src 'self'; img-src example.com", policy) - - @override_settings(CSP_IMG_SRC=['example.com']) - def test_replace_img(self): - policy = build_policy(replace={'img-src': 'example2.com'}) - policy_eq("default-src 'self'; img-src example2.com", policy) - - def test_replace_missing_setting(self): - """replace should work even if the setting is not defined.""" - policy = build_policy(replace={'img-src': 'example.com'}) - policy_eq("default-src 'self'; img-src example.com", policy) - - def test_config(self): - policy = build_policy( - config={'default-src': ["'none'"], 'img-src': ["'self'"]}) - policy_eq("default-src 'none'; img-src 'self'", policy) - - @override_settings(CSP_IMG_SRC=('example.com',)) - def test_update_string(self): - """ - GitHub issue #40 - given project settings as a tuple, and - an update/replace with a string, concatenate correctly. - """ - policy = build_policy(update={'img-src': 'example2.com'}) - policy_eq("default-src 'self'; img-src example.com example2.com", - policy) - - @override_settings(CSP_IMG_SRC=('example.com',)) - def test_replace_string(self): - """ - Demonstrate that GitHub issue #40 doesn't affect replacements - """ - policy = build_policy(replace={'img-src': 'example2.com'}) - policy_eq("default-src 'self'; img-src example2.com", - policy) +def test_empty_policy(): + policy = build_policy() + assert "default-src 'self'" == policy + + +@override_settings(CSP_DEFAULT_SRC=['example.com', 'example2.com']) +def test_default_src(): + policy = build_policy() + assert 'default-src example.com example2.com' == policy + + +@override_settings(CSP_SCRIPT_SRC=['example.com']) +def test_script_src(): + policy = build_policy() + policy_eq("default-src 'self'; script-src example.com", policy) + + +@override_settings(CSP_OBJECT_SRC=['example.com']) +def test_object_src(): + policy = build_policy() + policy_eq("default-src 'self'; object-src example.com", policy) + + +@override_settings(CSP_STYLE_SRC=['example.com']) +def test_style_src(): + policy = build_policy() + policy_eq("default-src 'self'; style-src example.com", policy) + + +@override_settings(CSP_IMG_SRC=['example.com']) +def test_img_src(): + policy = build_policy() + policy_eq("default-src 'self'; img-src example.com", policy) + + +@override_settings(CSP_MEDIA_SRC=['example.com']) +def test_media_src(): + policy = build_policy() + policy_eq("default-src 'self'; media-src example.com", policy) + + +@override_settings(CSP_FRAME_SRC=['example.com']) +def test_frame_src(): + policy = build_policy() + policy_eq("default-src 'self'; frame-src example.com", policy) + + +@override_settings(CSP_FONT_SRC=['example.com']) +def test_font_src(): + policy = build_policy() + policy_eq("default-src 'self'; font-src example.com", policy) + + +@override_settings(CSP_CONNECT_SRC=['example.com']) +def test_connect_src(): + policy = build_policy() + policy_eq("default-src 'self'; connect-src example.com", policy) + + +@override_settings(CSP_SANDBOX=['allow-scripts']) +def test_sandbox(): + policy = build_policy() + policy_eq("default-src 'self'; sandbox allow-scripts", policy) + + +@override_settings(CSP_SANDBOX=[]) +def test_sandbox_empty(): + policy = build_policy() + policy_eq("default-src 'self'; sandbox ", policy) + + +@override_settings(CSP_REPORT_URI='/foo') +def test_report_uri(): + policy = build_policy() + policy_eq("default-src 'self'; report-uri /foo", policy) + + +@override_settings(CSP_IMG_SRC=['example.com']) +def test_update_img(): + policy = build_policy(update={'img-src': 'example2.com'}) + policy_eq("default-src 'self'; img-src example.com example2.com", + policy) + + +def test_update_missing_setting(): + """update should work even if the setting is not defined.""" + policy = build_policy(update={'img-src': 'example.com'}) + policy_eq("default-src 'self'; img-src example.com", policy) + + +@override_settings(CSP_IMG_SRC=['example.com']) +def test_replace_img(): + policy = build_policy(replace={'img-src': 'example2.com'}) + policy_eq("default-src 'self'; img-src example2.com", policy) + + +def test_replace_missing_setting(): + """replace should work even if the setting is not defined.""" + policy = build_policy(replace={'img-src': 'example.com'}) + policy_eq("default-src 'self'; img-src example.com", policy) + + +def test_config(): + policy = build_policy( + config={'default-src': ["'none'"], 'img-src': ["'self'"]}) + policy_eq("default-src 'none'; img-src 'self'", policy) + + +@override_settings(CSP_IMG_SRC=('example.com',)) +def test_update_string(): + """ + GitHub issue #40 - given project settings as a tuple, and + an update/replace with a string, concatenate correctly. + """ + policy = build_policy(update={'img-src': 'example2.com'}) + policy_eq("default-src 'self'; img-src example.com example2.com", + policy) + + +@override_settings(CSP_IMG_SRC=('example.com',)) +def test_replace_string(): + """ + Demonstrate that GitHub issue #40 doesn't affect replacements + """ + policy = build_policy(replace={'img-src': 'example2.com'}) + policy_eq("default-src 'self'; img-src example2.com", + policy) + + +@override_settings(CSP_FORM_ACTION=['example.com']) +def test_form_action(): + policy = build_policy() + policy_eq("default-src 'self'; form-action example.com", policy) + + +@override_settings(CSP_BASE_URI=['example.com']) +def test_base_uri(): + policy = build_policy() + policy_eq("default-src 'self'; base-uri example.com", policy) + + +@override_settings(CSP_CHILD_SRC=['example.com']) +def test_child_src(): + policy = build_policy() + policy_eq("default-src 'self'; child-src example.com", policy) + + +@override_settings(CSP_FRAME_ANCESTORS=['example.com']) +def test_frame_ancestors(): + policy = build_policy() + policy_eq("default-src 'self'; frame-ancestors example.com", policy) diff --git a/csp/utils.py b/csp/utils.py index 15755e7..43caf1a 100644 --- a/csp/utils.py +++ b/csp/utils.py @@ -29,24 +29,24 @@ def build_policy(config=None, update=None, replace=None): # Update rules from settings. if update is not None: - for k, v in update.items(): - if not isinstance(v, (list, tuple)): - v = (v,) - if config[k] is not None: - config[k] += v + for key, value in update.items(): + if not isinstance(value, (list, tuple)): + value = (value,) + if config[key] is not None: + config[key] += value else: - config[k] = v + config[key] = value # Replace rules from settings. if replace is not None: - for k, v in replace.items(): - if v is not None and not isinstance(v, (list, tuple)): - v = [v] - config[k] = v + for key, value in replace.items(): + if value is not None and not isinstance(value, (list, tuple)): + value = [value] + config[key] = value report_uri = config.pop('report-uri', None) policy = ['%s %s' % (k, ' '.join(v)) for k, v in - config.items() if v is not None] + sorted(config.items()) if v is not None] if report_uri: policy.append('report-uri %s' % report_uri) return '; '.join(policy) diff --git a/docs/contributing.rst b/docs/contributing.rst index 97437d3..fbfa109 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -23,16 +23,17 @@ Patches fixing bugs should include regression tests (ideally tests that fail without the rest of the patch). Patches adding new features should test those features thoroughly. -To run the tests, install the requirements from ``requirements.txt`` -(probably into a virtualenv_):: +To run the tests, install the requirements (probably into a virtualenv_):: - pip install -r requirements.txt + pip install -e . + pip install -e .[tests] -Then just use the test-running shell script:: +Then just `py.test`_ to run the tests:: - ./run.sh test + py.test .. _PEP8: http://www.python.org/dev/peps/pep-0008/ .. _flake8: https://pypi.python.org/pypi/flake8 .. _virtualenv: http://www.virtualenv.org/ +.. _py.test: https://pytest.org/latest/usage.html diff --git a/requirements.txt b/requirements.txt index edefa76..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +0,0 @@ -# These are required to run the tests. -Django==1.5.1 -nose -mock diff --git a/run.sh b/run.sh deleted file mode 100755 index df71b5f..0000000 --- a/run.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -export PYTHONPATH=".:$PYTHONPATH" -export DJANGO_SETTINGS_MODULE="test_settings" - -usage() { - echo "USAGE: $0 [command]" - echo " test - run the django-csp tests" - echo " shell - open the Django shell" - exit 1 -} - -case "$1" in - "test" ) - django-admin.py test csp ;; - "shell" ) - django-admin.py shell ;; - * ) - usage ;; -esac diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..383e0ed --- /dev/null +++ b/setup.cfg @@ -0,0 +1,7 @@ +[wheel] +universal = 1 + +[pytest] +addopts = -vs --tb=short --pep8 --flakes + +DJANGO_SETTINGS_MODULE = csp.tests.settings diff --git a/setup.py b/setup.py index c5a1a9d..4942d07 100644 --- a/setup.py +++ b/setup.py @@ -1,29 +1,77 @@ +import sys +import os +import codecs from setuptools import setup, find_packages -import csp + +version = '3.0' + + +if sys.argv[-1] == 'publish': + os.system('python setup.py sdist upload') + os.system('python setup.py bdist_wheel upload') + print('You probably want to also tag the version now:') + print(' git tag -a %s -m "version %s"' % (version, version)) + print(' git push --tags') + sys.exit() + + +def read(*parts): + filename = os.path.join(os.path.dirname(__file__), *parts) + with codecs.open(filename, encoding='utf-8') as fp: + return fp.read() + + +install_requires = [ + 'Django>=1.6,<1.10', +] + + +test_requires = [ + 'pytest==2.9.1', + 'pytest-django==2.9.1', + 'pytest-flakes==1.0.1', + 'pytest-pep8==1.0.6', + 'pep8==1.4.6', + 'mock==1.0.1', +] + setup( name='django_csp', - version=csp.__version__, + version=version, description='Django Content Security Policy support.', - long_description=open('README.rst').read(), + long_description=read('README.rst'), author='James Socol', author_email='me@jamessocol.com', + maintainer='Christopher Grebs', + maintainer_email='cg@webshox.org', url='http://github.com/mozilla/django-csp', license='BSD', packages=find_packages(), + install_requires=install_requires, + extras_require={ + 'tests': test_requires, + }, include_package_data=True, - package_data={'': ['README.rst']}, zip_safe=False, classifiers=[ - 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Environment :: Web Environment :: Mozilla', - 'Intended Audience :: Developers', - 'Framework :: Django', + 'Programming Language :: Python', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', - 'Programming Language :: Python', + 'Intended Audience :: Developers', 'Topic :: Software Development :: Libraries :: Python Modules', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: Implementation :: PyPy', + 'Programming Language :: Python :: Implementation :: CPython', + 'Framework :: Django', ] ) diff --git a/test_settings.py b/test_settings.py index dbeb76c..e69de29 100644 --- a/test_settings.py +++ b/test_settings.py @@ -1,24 +0,0 @@ -DEBUG = True -TEMPLATE_DEBUG = True - -CSP_REPORT_ONLY = False - -SITE_ID = 1 - -DATABASES = { - 'default': { - 'NAME': 'test.db', - 'ENGINE': 'django.db.backends.sqlite3', - } -} - -INSTALLED_APPS = ( - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'csp', -) - -SECRET_KEY = 'csp-test-key' diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..54650e2 --- /dev/null +++ b/tox.ini @@ -0,0 +1,89 @@ +[testenv] +setenv = + PYTHONPATH={toxinidir} + PYTHONDONTWRITEBYTECODE=1 +commands = + pip install --upgrade pip setuptools wheel + pip install --use-wheel -e . + pip install --use-wheel -e .[tests] + py.test {toxinidir}/csp + +deps16 = + https://github.com/django/django/archive/stable/1.6.x.tar.gz#egg=django +deps17 = + https://github.com/django/django/archive/stable/1.7.x.tar.gz#egg=django +deps18 = + https://github.com/django/django/archive/stable/1.8.x.tar.gz#egg=django +deps19 = + https://github.com/django/django/archive/stable/1.9.x.tar.gz#egg=django + + +[testenv:2.7-1.6.x] +basepython = python2.7 +deps = + {[testenv]deps16} + +[testenv:2.7-1.7.x] +basepython = python2.7 +deps = + {[testenv]deps17} + +[testenv:2.7-1.8.x] +basepython = python2.7 +deps = + {[testenv]deps18} + +[testenv:2.7-1.9.x] +basepython = python2.7 +deps = + {[testenv]deps19} + +[testenv:3.4-1.6.x] +basepython = python3.4 +deps = + {[testenv]deps16} + +[testenv:3.4-1.7.x] +basepython = python3.4 +deps = + {[testenv]deps17} + +[testenv:3.4-1.8.x] +basepython = python3.4 +deps = + {[testenv]deps18} + +[testenv:3.4-1.9.x] +basepython = python3.4 +deps = + {[testenv]deps19} + +[testenv:3.5-1.8.x] +basepython = python3.5 +deps = + {[testenv]deps18} + +[testenv:3.5-1.9.x] +basepython = python3.5 +deps = + {[testenv]deps19} + +[testenv:pypy-1.6.x] +basepython = pypy +deps = + {[testenv]deps16} + +[testenv:pypy-1.7.x] +basepython = pypy +deps = + {[testenv]deps17} + +[testenv:pypy-1.8.x] +basepython = pypy +deps = + {[testenv]deps18} + +[testenv:pypy-1.9.x] +basepython = pypy +deps = + {[testenv]deps19}