From 778c6c4c2478a4855c8c0d4d470a3eb7073cf714 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 13 Sep 2017 15:21:54 +0200 Subject: [PATCH 1/6] Fix compatibility issues from django 1.5 to 1.11. --- .travis.yml | 15 +++++++++--- README.rst | 2 +- manage.py | 10 ++++++++ requirements.txt | 7 +++--- runtests.py | 45 ---------------------------------- tests/settings.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++ tests/urls.py | 18 +++++++++++--- tests/views.py | 8 +++--- tox.ini | 19 +++++++++++++++ 9 files changed, 124 insertions(+), 62 deletions(-) create mode 100644 manage.py delete mode 100755 runtests.py create mode 100644 tests/settings.py create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index c6ca7fa..e8c998c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,17 @@ language: python python: - - 2.6 - 2.7 +env: + - DJANGO="django>=1.4,<1.5" + - DJANGO="django>=1.5,<1.6" + - DJANGO="django>=1.6,<1.7" + - DJANGO="django>=1.7,<1.8" + - DJANGO="django>=1.8,<1.9" + - DJANGO="django>=1.9,<1.10" + - DJANGO="django>=1.10,<1.11" + - DJANGO="django>=1.11,<2.0" install: -script: + - pip install ${DJANGO} - python setup.py develop - - ./runtests.py \ No newline at end of file +script: + - python manage.py test tests \ No newline at end of file diff --git a/README.rst b/README.rst index 5bd865c..9621d4f 100644 --- a/README.rst +++ b/README.rst @@ -127,7 +127,7 @@ Fork, clone and create a virtualenv. Then run:: Run tests with:: - ./runtests.py + python manage.py test tests Please submit pull requests using 'develop' as the target branch. diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..dc935d6 --- /dev/null +++ b/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/requirements.txt b/requirements.txt index 02986b3..02bf3a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -Django==1.4 -django-nose==1.1 -nose==1.1.2 -pinocchio==0.3.1 +django-nose>=1.1 +nose>=1.1.2 +pinocchio>=0.3.1 diff --git a/runtests.py b/runtests.py deleted file mode 100755 index 7a5730a..0000000 --- a/runtests.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -import sys -import os - -from django.conf import settings, global_settings - -if not settings.configured: - settings.configure( - DATABASES={ - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - } - }, - INSTALLED_APPS=[ - 'django.contrib.auth', - 'django.contrib.admin', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'async_messages', - 'tests', - ], - MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES + ( - 'async_messages.middleware.AsyncMiddleware', - ), - ROOT_URLCONF='tests.urls', - DEBUG=False, - SITE_ID=1, - ) - -from django.test.simple import DjangoTestSuiteRunner - - -def run_tests(): - # Modify path - parent = os.path.dirname(os.path.abspath(__file__)) - sys.path.insert(0, parent) - - # Run tests - test_runner = DjangoTestSuiteRunner(verbosity=2) - failures = test_runner.run_tests(['tests']) - sys.exit(failures) - -if __name__ == '__main__': - run_tests() diff --git a/tests/settings.py b/tests/settings.py new file mode 100644 index 0000000..750b196 --- /dev/null +++ b/tests/settings.py @@ -0,0 +1,62 @@ +import django +from os import path + +DEBUG = False + +ROOT_URLCONF = 'tests.urls' + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + } +} + +INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.admin', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'async_messages', + 'tests' +] + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'async_messages.middleware.AsyncMiddleware' +) + +SITE_ID = 1 + +if django.VERSION >= (1, 8): + TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [ + path.join(path.dirname(__file__), "templates"), + ], + 'OPTIONS': { + 'context_processors': [ + # Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this + # list if you haven't customized them: + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.debug', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.template.context_processors.tz', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, + ] +else: + TEMPLATE_DIRS = ( + path.join(path.dirname(__file__), "templates"), + ) + +SECRET_KEY = 'foobarbaz' diff --git a/tests/urls.py b/tests/urls.py index ba173c7..e853085 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,5 +1,15 @@ -from django.conf.urls.defaults import patterns, url +import django +from django.conf.urls import url -urlpatterns = patterns('', - url(r'^$', 'tests.views.index'), -) +if django.VERSION >= (1, 8): + from . import views + + urlpatterns = [ + url(r'^$', views.index, name='views_test'), + ] +else: + from django.conf.urls import patterns + + urlpatterns = patterns('', + url(r'^$', 'tests.views.index', name='views_test'), + ) diff --git a/tests/views.py b/tests/views.py index d252413..418fa4c 100644 --- a/tests/views.py +++ b/tests/views.py @@ -1,7 +1,5 @@ -from django.template.response import TemplateResponse -from django.template import Template - +from django.template import Template, RequestContext +from django.http import HttpResponse def index(request): - t = Template("") - return TemplateResponse(request, t, {'a': 1000}) + return HttpResponse(Template("").render(RequestContext(request, {'a': 1000}))) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..1f2c9a0 --- /dev/null +++ b/tox.ini @@ -0,0 +1,19 @@ +[tox] +envlist = py27-dj{14,18,19,110,111} + +[testenv] +deps = + -r{toxinidir}/requirements.txt + dj14: Django>=1.4,<1.5 + dj18: Django>=1.8,<1.9 + dj19: Django>=1.9,<1.10 + dj110: Django>=1.10,<1.11 + dj111: Django>=1.11,<2.0 +commands = + pip install -e . + python manage.py test tests +passenv = + PYTHONPATH +usedevelop = True +whitelist_externals= + pip From 0d8346c1abaacc724645897d3a36ffe511cf4fe6 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 13 Sep 2017 15:25:40 +0200 Subject: [PATCH 2/6] Fix travis conf --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e8c998c..df82392 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ env: - DJANGO="django>=1.10,<1.11" - DJANGO="django>=1.11,<2.0" install: - - pip install ${DJANGO} + - pip install $DJANGO - python setup.py develop script: - python manage.py test tests \ No newline at end of file From c0e92d1ce4487d54a38e4df1b9753865097c3d4e Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Mon, 29 Jan 2018 13:27:18 +0100 Subject: [PATCH 3/6] Fix issues with is_authenticated/is_anonymous as property. Fix behaviour for anonymous users. --- async_messages/__init__.py | 11 +++++++++++ async_messages/middleware.py | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/async_messages/__init__.py b/async_messages/__init__.py index b18a757..2b6a62b 100644 --- a/async_messages/__init__.py +++ b/async_messages/__init__.py @@ -2,6 +2,10 @@ from django.contrib.messages import constants +class AsyncMessageException(Exception): + pass + + def message_user(user, message, level=constants.INFO): """ Send a message to a particular user. @@ -12,6 +16,10 @@ def message_user(user, message, level=constants.INFO): """ # We store a list of messages in the cache so we can have multiple messages # queued up for a user. + + if user.id is None: + raise AsyncMessageException('Anonymous users cannot send messages.') + user_key = _user_key(user) messages = cache.get(user_key) or [] messages.append((message, level)) @@ -36,6 +44,9 @@ def get_messages(user): :param user: User instance """ + if user.id is None: + return None + key = _user_key(user) result = cache.get(key) if result: diff --git a/async_messages/middleware.py b/async_messages/middleware.py index a8e2804..147a791 100644 --- a/async_messages/middleware.py +++ b/async_messages/middleware.py @@ -1,16 +1,31 @@ +from distutils.version import StrictVersion + from django.contrib import messages +from django.utils.version import get_version from async_messages import get_messages class AsyncMiddleware(object): + # In Django>=1.10 User.is_authenticated is a property, not a method but a + # strange one : CallbableBool. + # - If default User is used you can use it as a boolean either a method. + # - If this property is overrided you may have a bool instead and an + # exception. + # Fix it by checking if the property is callable or not. + def is_authenticated(self, request): + if hasattr(request, "session") and hasattr(request, "user"): + auth = request.user.is_authenticated + return auth() if callable(auth) else auth + else: + return False def process_response(self, request, response): """ Check for messages for this user and, if it exists, call the messages API with it """ - if hasattr(request, "session") and hasattr(request, "user") and request.user.is_authenticated(): + if self.is_authenticated(request): msgs = get_messages(request.user) if msgs: for msg, level in msgs: From d1fb8bf260d307c3ac9a0f92ff8bf69ba7fcdd55 Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Mon, 29 Jan 2018 13:29:38 +0100 Subject: [PATCH 4/6] Add test. --- tests/tests.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index a03e609..d5854bb 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,9 +1,12 @@ +from django.contrib import auth +from django.contrib.auth.models import User +from django.contrib.messages import constants from django.test import TestCase from django.test.client import Client -from django.contrib.auth.models import User -from django.contrib.messages import constants, set_level -from async_messages import message_user, message_users, messages +from async_messages import ( + message_user, message_users, messages, AsyncMessageException, +) class MiddlewareTests(TestCase): @@ -44,6 +47,16 @@ def test_anonymous(self): msgs = list(response.context['messages']) self.assertEqual(0, len((msgs))) + def test_anonymous_message(self): + client = Client() + user = auth.get_user(client) + + with self.assertRaises(AsyncMessageException) as e: + message_user(user, "Second Message") + + self.assertEqual(str(e.exception), + 'Anonymous users cannot send messages.') + class TestMessagesApi(TestCase): def setUp(self): From 8e04e2b8d25d5e7fb85ba6d18c2de4d409aaf1ab Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Mon, 29 Jan 2018 13:31:10 +0100 Subject: [PATCH 5/6] Fix unused imports --- async_messages/middleware.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/async_messages/middleware.py b/async_messages/middleware.py index 147a791..7d27eb1 100644 --- a/async_messages/middleware.py +++ b/async_messages/middleware.py @@ -1,7 +1,4 @@ -from distutils.version import StrictVersion - from django.contrib import messages -from django.utils.version import get_version from async_messages import get_messages From 99077d690866b66fb42faf5b927181fe7e3c7e3d Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Thu, 6 Sep 2018 15:23:07 +0200 Subject: [PATCH 6/6] Backport work of @maurizi (see https://github.com/maurizi/django-async-messages) and add some improvements for Django>=2.0 and python 3.x support. --- .travis.yml | 36 ++++++++++++++++++++++++++---------- async_messages/middleware.py | 28 ++++++++++++++++++++++++---- setup.py | 2 +- tests/settings.py | 27 +++++++++++++++++++-------- tox.ini | 5 +++-- 5 files changed, 73 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index df82392..588ff8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,33 @@ language: python python: - 2.7 + - 3.6 + env: - - DJANGO="django>=1.4,<1.5" - - DJANGO="django>=1.5,<1.6" - - DJANGO="django>=1.6,<1.7" - - DJANGO="django>=1.7,<1.8" - - DJANGO="django>=1.8,<1.9" - - DJANGO="django>=1.9,<1.10" - - DJANGO="django>=1.10,<1.11" - - DJANGO="django>=1.11,<2.0" + - DJANGO=1.4 + - DJANGO=1.5 + - DJANGO=1.6 + - DJANGO=1.7 + - DJANGO=1.8 + - DJANGO=1.9 + - DJANGO=1.10 + - DJANGO=1.11 + - DJANGO=2.0 + +matrix: + exclude: + - python: 3.6 + env: DJANGO=1.4 + - python: 3.6 + env: DJANGO=1.5 + - python: 3.6 + env: DJANGO=1.6 + - python: 3.6 + env: DJANGO=1.7 + - python: 2.7 + env: DJANGO=2.0 install: - - pip install $DJANGO + - pip install Django==$DJANGO - python setup.py develop script: - - python manage.py test tests \ No newline at end of file + - python manage.py test tests diff --git a/async_messages/middleware.py b/async_messages/middleware.py index 7d27eb1..8339a21 100644 --- a/async_messages/middleware.py +++ b/async_messages/middleware.py @@ -1,19 +1,39 @@ +from distutils.version import StrictVersion + from django.contrib import messages +try: + from django.utils.version import get_version +except ImportError: + from django import VERSION as DJANGO_VERSION + + def get_version(): + return ".".join(str(n) for n in DJANGO_VERSION[:3]) + from async_messages import get_messages +if StrictVersion(get_version()) >= StrictVersion("1.10.0"): + from django.utils.deprecation import MiddlewareMixin as _MiddlewareBase + + def _is_user_authenticated(user): + return bool(user.is_authenticated) +else: + _MiddlewareBase = object -class AsyncMiddleware(object): + def _is_user_authenticated(user): + return user.is_authenticated() + + +class AsyncMiddleware(_MiddlewareBase): # In Django>=1.10 User.is_authenticated is a property, not a method but a # strange one : CallbableBool. # - If default User is used you can use it as a boolean either a method. # - If this property is overrided you may have a bool instead and an # exception. - # Fix it by checking if the property is callable or not. + def is_authenticated(self, request): if hasattr(request, "session") and hasattr(request, "user"): - auth = request.user.is_authenticated - return auth() if callable(auth) else auth + return _is_user_authenticated(request.user) else: return False diff --git a/setup.py b/setup.py index a9e2f22..7b7690d 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup(name='django-async-messages', - version='0.3.1', + version='0.3.2', url='https://github.com/codeinthehole/django-async-messages', author="David Winterbottom", author_email="david.winterbottom@gmail.com", diff --git a/tests/settings.py b/tests/settings.py index 750b196..8613bb9 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -21,14 +21,25 @@ 'tests' ] -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'async_messages.middleware.AsyncMiddleware' -) + +if django.VERSION >= (1, 10): + MIDDLEWARE = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'async_messages.middleware.AsyncMiddleware' + ) +else: + MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'async_messages.middleware.AsyncMiddleware' + ) SITE_ID = 1 diff --git a/tox.ini b/tox.ini index 1f2c9a0..5b15f61 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27-dj{14,18,19,110,111} +envlist = py27-dj{14,18,19,110,111},py36-dj{18,19,110,111,20} [testenv] deps = @@ -9,9 +9,10 @@ deps = dj19: Django>=1.9,<1.10 dj110: Django>=1.10,<1.11 dj111: Django>=1.11,<2.0 + dj20: Django>=2.0,<2.1 commands = pip install -e . - python manage.py test tests + python -Wd manage.py test tests passenv = PYTHONPATH usedevelop = True