From cc84048c7eed36b8bde76f7d08849e648f21b785 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 16:48:46 +0000 Subject: [PATCH 01/12] Refactor as generic invite app --- invitations/adapters.py | 117 ++++++++++++++++++++++++++++++++++++ invitations/app_settings.py | 6 ++ invitations/forms.py | 5 +- invitations/models.py | 34 ++++++----- invitations/tests.py | 117 ++++++++++++++++++++++++------------ invitations/utils.py | 13 ++++ invitations/views.py | 19 +++--- requirements.txt | 1 - setup.py | 4 +- test_settings.py | 53 ++++++++++------ test_urls.py | 9 ++- tox.ini | 4 +- 12 files changed, 293 insertions(+), 89 deletions(-) create mode 100644 invitations/adapters.py create mode 100644 invitations/utils.py diff --git a/invitations/adapters.py b/invitations/adapters.py new file mode 100644 index 0000000..4ce4752 --- /dev/null +++ b/invitations/adapters.py @@ -0,0 +1,117 @@ +from django.template.loader import render_to_string +from django.contrib import messages +from django.conf import settings +from django.template import TemplateDoesNotExist +from django.core.mail import EmailMultiAlternatives, EmailMessage +from django.contrib.sites.models import Site + +try: + from django.utils.encoding import force_text +except ImportError: + from django.utils.encoding import force_unicode as force_text + +from .app_settings import app_settings + + +class BaseInvitationsAdapter(object): + + def stash_verified_email(self, request, email): + request.session['account_verified_email'] = email + + def unstash_verified_email(self, request): + ret = request.session.get('account_verified_email') + request.session['account_verified_email'] = None + return ret + + def format_email_subject(self, subject): + site = Site.objects.get_current() + prefix = "[{name}] ".format(name=site.name) + return prefix + force_text(subject) + + def render_mail(self, template_prefix, email, context): + """ + Renders an e-mail to `email`. `template_prefix` identifies the + e-mail that is to be sent, e.g. "account/email/email_confirmation" + """ + subject = render_to_string('{0}_subject.txt'.format(template_prefix), + context) + # remove superfluous line breaks + subject = " ".join(subject.splitlines()).strip() + subject = self.format_email_subject(subject) + + bodies = {} + for ext in ['html', 'txt']: + try: + template_name = '{0}_message.{1}'.format(template_prefix, ext) + bodies[ext] = render_to_string(template_name, + context).strip() + except TemplateDoesNotExist: + if ext == 'txt' and not bodies: + # We need at least one body + raise + if 'txt' in bodies: + msg = EmailMultiAlternatives(subject, + bodies['txt'], + settings.DEFAULT_FROM_EMAIL, + [email]) + if 'html' in bodies: + msg.attach_alternative(bodies['html'], 'text/html') + else: + msg = EmailMessage(subject, + bodies['html'], + settings.DEFAULT_FROM_EMAIL, + [email]) + msg.content_subtype = 'html' # Main content is now text/html + return msg + + def send_mail(self, template_prefix, email, context): + msg = self.render_mail(template_prefix, email, context) + msg.send() + + def is_open_for_signup(self, request): + if hasattr(request, 'session') and request.session.get( + 'account_verified_email'): + return True + elif app_settings.INVITATION_ONLY is True: + # Site is ONLY open for invites + return False + else: + # Site is open to signup + return True + + def clean_email(self, email): + """ + Validates an email value. You can hook into this if you want to + (dynamically) restrict what email addresses can be chosen. + """ + return email + + def add_message(self, request, level, message_template, + message_context=None, extra_tags=''): + """ + Wrapper of `django.contrib.messages.add_message`, that reads + the message text from a template. + """ + if 'django.contrib.messages' in settings.INSTALLED_APPS: + try: + if message_context is None: + message_context = {} + message = render_to_string(message_template, + message_context).strip() + if message: + messages.add_message(request, level, message, + extra_tags=extra_tags) + except TemplateDoesNotExist: + pass + + +def get_invitations_adapter(): + if hasattr(settings, 'ACCOUNT_ADAPTER'): + if settings.ACCOUNT_ADAPTER == 'invitations.models.InvitationsAdapter': + # defer to allauth + from allauth.account.adapter import get_adapter + return get_adapter() + else: + from .utils import import_attribute + # load an adapter from elsewhere + return import_attribute(app_settings.ADAPTER)() diff --git a/invitations/app_settings.py b/invitations/app_settings.py index 2c44c9d..9e743ca 100644 --- a/invitations/app_settings.py +++ b/invitations/app_settings.py @@ -32,4 +32,10 @@ def SIGNUP_REDIRECT(self): """ Where to redirect on email confirm of invite """ return self._setting('SIGNUP_REDIRECT', 'account_signup') + @property + def ADAPTER(self): + """ The adapter, setting ACCOUNT_ADAPTER overrides this default """ + return self._setting( + 'ADAPTER', 'invitations.adapters.BaseInvitationsAdapter') + app_settings = AppSettings('INVITATIONS_') diff --git a/invitations/forms.py b/invitations/forms.py index 0e769e1..2c25973 100644 --- a/invitations/forms.py +++ b/invitations/forms.py @@ -2,9 +2,8 @@ from django.utils.translation import ugettext_lazy as _ from django.contrib.auth import get_user_model -from allauth.account.adapter import get_adapter - from .models import Invitation +from .adapters import get_invitations_adapter from .exceptions import AlreadyInvited, AlreadyAccepted, UserRegisteredEmail @@ -24,7 +23,7 @@ def validate_invitation(self, email): def clean_email(self): email = self.cleaned_data["email"] - email = get_adapter().clean_email(email) + email = get_invitations_adapter().clean_email(email) errors = { "already_invited": _("This e-mail address has already been" diff --git a/invitations/models.py b/invitations/models.py index 656020d..979fe6a 100644 --- a/invitations/models.py +++ b/invitations/models.py @@ -9,11 +9,9 @@ from django.core.urlresolvers import reverse from django.conf import settings -from allauth.account.adapter import DefaultAccountAdapter -from allauth.account.adapter import get_adapter - from .managers import InvitationManager from .app_settings import app_settings +from .adapters import get_invitations_adapter from . import signals @@ -63,7 +61,7 @@ def send_invitation(self, request, **kwargs): email_template = 'invitations/email/email_invite' - get_adapter().send_mail( + get_invitations_adapter().send_mail( email_template, self.email, ctx) @@ -80,15 +78,21 @@ def __str__(self): return "Invite: {0}".format(self.email) -class InvitationsAdapter(DefaultAccountAdapter): +# here for backwards compatibility, historic allauth adapter +if hasattr(settings, 'ACCOUNT_ADAPTER'): + + if settings.ACCOUNT_ADAPTER == 'invitations.models.InvitationsAdapter': + from allauth.account.adapter import DefaultAccountAdapter + + class InvitationsAdapter(DefaultAccountAdapter): - def is_open_for_signup(self, request): - if hasattr(request, 'session') and request.session.get( - 'account_verified_email'): - return True - elif app_settings.INVITATION_ONLY is True: - # Site is ONLY open for invites - return False - else: - # Site is open to signup - return True + def is_open_for_signup(self, request): + if hasattr(request, 'session') and request.session.get( + 'account_verified_email'): + return True + elif app_settings.INVITATION_ONLY is True: + # Site is ONLY open for invites + return False + else: + # Site is open to signup + return True diff --git a/invitations/tests.py b/invitations/tests.py index fdda9b8..2322f73 100644 --- a/invitations/tests.py +++ b/invitations/tests.py @@ -11,13 +11,13 @@ from django.contrib.auth import get_user_model from django.core import mail from django.contrib.auth.models import AnonymousUser +from django.conf import settings -from allauth.account.adapter import get_adapter -from allauth.account.models import EmailAddress from freezegun import freeze_time from nose_parameterized import parameterized -from .models import Invitation, InvitationsAdapter +from .adapters import get_invitations_adapter, BaseInvitationsAdapter +from .models import Invitation from .app_settings import app_settings from .views import AcceptInvite, SendJSONInvite from .forms import InviteForm @@ -56,28 +56,31 @@ class InvitationsAdapterTests(TestCase): @classmethod def setUp(cls): - cls.adapter = get_adapter() - cls.signup_request = RequestFactory().get(reverse( - 'account_signup', urlconf='allauth.account.urls')) + cls.adapter = get_invitations_adapter() @classmethod def tearDownClass(cls): del cls.adapter def test_fetch_adapter(self): - self.assertIsInstance(self.adapter, InvitationsAdapter) - - def test_adapter_default_signup(self): - self.assertTrue(self.adapter.is_open_for_signup(self.signup_request)) + if hasattr(settings, 'ACCOUNT_ADAPTER'): + from models import InvitationsAdapter + self.assertIsInstance(self.adapter, InvitationsAdapter) + else: + self.assertIsInstance(self.adapter, BaseInvitationsAdapter) @override_settings( - INVITATIONS_INVITATION_ONLY=True + INVITATIONS_INVITATION_ONLY=True, ) - def test_adapter_invitations_only(self): - self.assertFalse(self.adapter.is_open_for_signup(self.signup_request)) - response = self.client.get( - reverse('account_signup')) - self.assertIn('Sign Up Closed', response.content.decode('utf8')) + def test_allauth_adapter_invitations_only(self): + if hasattr(settings, 'ACCOUNT_ADAPTER'): + signup_request = RequestFactory().get(reverse( + 'account_signup', urlconf='allauth.account.urls')) + self.assertFalse( + self.adapter.is_open_for_signup(signup_request)) + response = self.client.get( + reverse('account_signup')) + self.assertIn('Sign Up Closed', response.content.decode('utf8')) class InvitationsSendViewTests(TestCase): @@ -102,8 +105,14 @@ def test_auth(self): response = self.client.post( reverse('invitations:send-invite'), {'email': 'valid@example.com'}, follow=True) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.template_name, ['account/login.html']) + + if hasattr(settings, 'ACCOUNT_ADAPTER'): + self.assertEqual(response.status_code, 200) + self.assertEqual(response.template_name, ['account/login.html']) + + else: + self.assertEqual(response.status_code, 404) + self.assertEqual(response.request['PATH_INFO'], '/accounts/login/') @parameterized.expand([ ('invalid@example', 'Enter a valid email address'), @@ -179,7 +188,10 @@ def test_accept_invite_invalid_key(self, method): ('get'), ('post'), ]) - def test_accept_invite(self, method): + @override_settings( + INVITATIONS_SIGNUP_REDIRECT='/non-existent-url/' + ) + def test_accept_invite_vanilla(self, method): client_with_method = getattr(self.client, method) resp = client_with_method( reverse('invitations:accept-invite', @@ -187,26 +199,47 @@ def test_accept_invite(self, method): invite = Invitation.objects.get(email='email@example.com') self.assertTrue(invite.accepted) self.assertEqual(invite.inviter, self.user) - self.assertEqual(resp.request['PATH_INFO'], reverse('account_signup')) - - form = resp.context_data['form'] - self.assertEqual('email@example.com', form.fields['email'].initial) - messages = resp.context['messages'] - message_text = [message.message for message in messages] self.assertEqual( - message_text, [ - 'Invitation to - email@example.com - has been accepted']) - - resp = self.client.post( - reverse('account_signup'), - {'email': 'email@example.com', - 'username': 'username', - 'password1': 'password', - 'password2': 'password' - }) + resp.request['PATH_INFO'], '/non-existent-url/') - allauth_email_obj = EmailAddress.objects.get(email='email@example.com') - self.assertTrue(allauth_email_obj.verified) + @parameterized.expand([ + ('get'), + ('post'), + ]) + def test_accept_invite_allauth(self, method): + if hasattr(settings, 'ACCOUNT_ADAPTER'): + # allauth specific + from allauth.account.models import EmailAddress + + client_with_method = getattr(self.client, method) + resp = client_with_method( + reverse('invitations:accept-invite', + kwargs={'key': self.invitation.key}), follow=True) + invite = Invitation.objects.get(email='email@example.com') + self.assertTrue(invite.accepted) + self.assertEqual(invite.inviter, self.user) + self.assertEqual( + resp.request['PATH_INFO'], reverse('account_signup')) + + form = resp.context_data['form'] + self.assertEqual('email@example.com', form.fields['email'].initial) + messages = resp.context['messages'] + message_text = [message.message for message in messages] + self.assertEqual( + message_text, [ + 'Invitation to - email@example.com - has been accepted']) + + resp = self.client.post( + reverse('account_signup'), + {'email': 'email@example.com', + 'username': 'username', + 'password1': 'password', + 'password2': 'password' + }) + + allauth_email_obj = EmailAddress.objects.get( + email='email@example.com') + self.assertTrue(allauth_email_obj.verified) @override_settings( INVITATIONS_SIGNUP_REDIRECT='/non-existent-url/' @@ -245,6 +278,9 @@ def test_invite_url_sent_triggered_correctly(self, mock_signal): invite.delete() + @override_settings( + INVITATIONS_SIGNUP_REDIRECT='/non-existent-url/' + ) @patch('invitations.signals.invite_accepted.send') def test_invite_invite_accepted_triggered_correctly(self, mock_signal): invite = Invitation.create('email@example.com') @@ -339,7 +375,8 @@ class InvitationsJSONTests(TestCase): def setUp(cls): cls.user = get_user_model().objects.create_user( username='flibble', - password='password') + password='password', + email='mrflibble@example.com') cls.accepted_invite = Invitation.create('already@accepted.com') cls.accepted_invite.accepted = True cls.accepted_invite.save() @@ -365,6 +402,10 @@ def tearDownClass(cls): {u'valid': [], u'invalid': [{u'email3@example.com': u'pending invite'}]}, 400), + (['mrflibble@example.com'], + {u'valid': [], + u'invalid': [{u'mrflibble@example.com': u'user registered email'}]}, + 400), (['example@example.com'], {u'valid': [{u'example@example.com': u'invited'}], u'invalid': []}, diff --git a/invitations/utils.py b/invitations/utils.py new file mode 100644 index 0000000..2510e3c --- /dev/null +++ b/invitations/utils.py @@ -0,0 +1,13 @@ +from django.utils import six + +try: + import importlib +except: + from django.utils import importlib + + +def import_attribute(path): + assert isinstance(path, six.string_types) + pkg, attr = path.rsplit('.', 1) + ret = getattr(importlib.import_module(pkg), attr) + return ret diff --git a/invitations/views.py b/invitations/views.py index d76fceb..f22708c 100644 --- a/invitations/views.py +++ b/invitations/views.py @@ -9,13 +9,13 @@ from django.core.validators import validate_email from braces.views import LoginRequiredMixin -from allauth.account.adapter import get_adapter from .forms import InviteForm, CleanEmailMixin from .models import Invitation from . import signals -from .exceptions import AlreadyInvited, AlreadyAccepted +from .exceptions import AlreadyInvited, AlreadyAccepted, UserRegisteredEmail from .app_settings import app_settings +from .adapters import get_invitations_adapter class SendInvite(LoginRequiredMixin, FormView): @@ -70,6 +70,9 @@ def post(self, request, *args, **kwargs): except(AlreadyInvited): response['invalid'].append( {invitee: 'pending invite'}) + except(UserRegisteredEmail): + response['invalid'].append( + {invitee: 'user registered email'}) else: invite.send_invitation(request) response['valid'].append({invitee: 'invited'}) @@ -95,16 +98,18 @@ def post(self, *args, **kwargs): self.object = invitation = self.get_object() invitation.accepted = True invitation.save() - get_adapter().stash_verified_email(self.request, invitation.email) + get_invitations_adapter().stash_verified_email( + self.request, invitation.email) signals.invite_accepted.send(sender=self.__class__, request=self.request, email=invitation.email) - get_adapter().add_message(self.request, - messages.SUCCESS, - 'invitations/messages/invite_accepted.txt', - {'email': invitation.email}) + get_invitations_adapter().add_message( + self.request, + messages.SUCCESS, + 'invitations/messages/invite_accepted.txt', + {'email': invitation.email}) return redirect(app_settings.SIGNUP_REDIRECT) diff --git a/requirements.txt b/requirements.txt index d56c8c2..b2ccb2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -django-allauth tox>=2.1.1 mock>=1.3.0 coverage>=3.7.1 diff --git a/setup.py b/setup.py index c8d51ac..854f675 100644 --- a/setup.py +++ b/setup.py @@ -6,13 +6,13 @@ package_data={'invitations': ['templates/*.*']}, include_package_data=True, zip_safe=False, - version='1.4', + version='1.5', description='Django invitation integration for django-allauth', author='https://github.com/bee-keeper', author_email='none@none.com', url='https://github.com/bee-keeper/django-invitations.git', download_url='https://github.com/' - 'bee-keeper/django-invitations/tarball/1.4', + 'bee-keeper/django-invitations/tarball/1.5', keywords=['django', 'invitation', 'django-allauth', 'invite'], classifiers=[ 'Development Status :: 4 - Beta', diff --git a/test_settings.py b/test_settings.py index 06ea950..a06092b 100644 --- a/test_settings.py +++ b/test_settings.py @@ -50,26 +50,41 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.admin', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.messages', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'invitations', - 'django_nose', -) - EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' # EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' -AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'allauth.account.auth_backends.AuthenticationBackend', -) +try: + __import__('allauth') + INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.admin', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django_nose', + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + 'invitations', + ) + AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', + ) + ACCOUNT_ADAPTER = 'invitations.models.InvitationsAdapter' -ACCOUNT_ADAPTER = 'invitations.models.InvitationsAdapter' +except ImportError: + INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.admin', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django_nose', + 'invitations', + ) + AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + ) diff --git a/test_urls.py b/test_urls.py index 3e5cc2b..7c62344 100644 --- a/test_urls.py +++ b/test_urls.py @@ -1,11 +1,16 @@ from django.conf.urls import patterns, url, include from django.contrib import admin - +from django.conf import settings urlpatterns = patterns( '', url(r'^invitations/', include( 'invitations.urls', namespace='invitations')), - url(r'^accounts/', include('allauth.urls')), url(r'^admin/', include(admin.site.urls)), ) + +if 'allauth' in settings.INSTALLED_APPS: + urlpatterns += patterns( + '', + url(r'^accounts/', include('allauth.urls')) + ) diff --git a/tox.ini b/tox.ini index c35d690..4dd376e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{27,33,34,35}-django{16,17,18,19} +envlist = py{27,33,34,35}-django{16,17,18,19}-backend-{vanilla, allauth} [testenv] deps = @@ -8,12 +8,12 @@ deps = django17: Django<1.8 django18: Django<1.9 django19: Django==1.9rc1 + backend-allauth: django-allauth commands = python -V coverage run manage.py test coverage report - coverage html [testenv:flake8] basepython=python From 30e34e762569d26130e13929fe3432101174e3b4 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 19:25:32 +0000 Subject: [PATCH 02/12] fixes import --- invitations/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invitations/tests.py b/invitations/tests.py index 2322f73..b484f9e 100644 --- a/invitations/tests.py +++ b/invitations/tests.py @@ -64,7 +64,7 @@ def tearDownClass(cls): def test_fetch_adapter(self): if hasattr(settings, 'ACCOUNT_ADAPTER'): - from models import InvitationsAdapter + from .models import InvitationsAdapter self.assertIsInstance(self.adapter, InvitationsAdapter) else: self.assertIsInstance(self.adapter, BaseInvitationsAdapter) From 12a6270022416152b59c6d87b263e15984a0f9c5 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 19:35:43 +0000 Subject: [PATCH 03/12] tox --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 4dd376e..9e28666 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{27,33,34,35}-django{16,17,18,19}-backend-{vanilla, allauth} +envlist = py{27,33,34,35}-django{16,17,18,19}-backend{Vanilla, Allauth} [testenv] deps = @@ -8,7 +8,7 @@ deps = django17: Django<1.8 django18: Django<1.9 django19: Django==1.9rc1 - backend-allauth: django-allauth + backendAllauth: django-allauth commands = python -V From aaca83f056a96e0fb4a6992169390e4399c3a4f4 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 19:54:43 +0000 Subject: [PATCH 04/12] tox --- tox.ini | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 9e28666..2da43a8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,20 @@ [tox] -envlist = py{27,33,34,35}-django{16,17,18,19}-backend{Vanilla, Allauth} +envlist = py{27,33,34,35}-django{16,17,18,19} -[testenv] +[testenv:vanilla] +deps = + -rrequirements.txt + django16: Django<1.7 + django17: Django<1.8 + django18: Django<1.9 + django19: Django==1.9rc1 + +commands = + python -V + coverage run manage.py test + coverage report + +[testenv:allauth] deps = -rrequirements.txt django16: Django<1.7 From d45697c206edd1fbe6c01314f8fd6e6ff995814d Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 21:48:13 +0000 Subject: [PATCH 05/12] tox --- tox.ini | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tox.ini b/tox.ini index 2da43a8..8d3463a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,32 +1,30 @@ [tox] envlist = py{27,33,34,35}-django{16,17,18,19} -[testenv:vanilla] +[base] deps = -rrequirements.txt django16: Django<1.7 django17: Django<1.8 django18: Django<1.9 django19: Django==1.9rc1 - commands = python -V coverage run manage.py test coverage report -[testenv:allauth] +[testenv:vanilla] deps = - -rrequirements.txt - django16: Django<1.7 - django17: Django<1.8 - django18: Django<1.9 - django19: Django==1.9rc1 - backendAllauth: django-allauth + {[base]deps} +commands = + {[base]commands} +[testenv:allauth] +deps = + {[base]deps} + django-allauth commands = - python -V - coverage run manage.py test - coverage report + {[base]commands} [testenv:flake8] basepython=python From e5105dab88168fb2774b76782581fb893793a732 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 22:01:24 +0000 Subject: [PATCH 06/12] tox --- tox.ini | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tox.ini b/tox.ini index 8d3463a..c26e9d1 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,7 @@ deps = django17: Django<1.8 django18: Django<1.9 django19: Django==1.9rc1 + commands = python -V coverage run manage.py test @@ -19,13 +20,6 @@ deps = commands = {[base]commands} -[testenv:allauth] -deps = - {[base]deps} - django-allauth -commands = - {[base]commands} - [testenv:flake8] basepython=python deps=flake8 From 7080018944c98dc4796140b69f7fdeaa3645de1f Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 22:18:13 +0000 Subject: [PATCH 07/12] to --- tox.ini | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tox.ini b/tox.ini index c26e9d1..57eb33b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = py{27,33,34,35}-django{16,17,18,19} -[base] +[testenv] deps = -rrequirements.txt django16: Django<1.7 @@ -14,12 +14,6 @@ commands = coverage run manage.py test coverage report -[testenv:vanilla] -deps = - {[base]deps} -commands = - {[base]commands} - [testenv:flake8] basepython=python deps=flake8 From a02af73d824c1e62a105da14c756651d7adeeb53 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 22:25:52 +0000 Subject: [PATCH 08/12] testing --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 854f675..eb8e7f1 100644 --- a/setup.py +++ b/setup.py @@ -22,13 +22,12 @@ 'Topic :: Internet', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Framework :: Django', ], install_requires=[ - 'django-allauth>=0.22', 'django-braces>=1.8.0', ], ) From c3b95b42ff4d82619cd492ac13b7c396d93800be Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 22:41:17 +0000 Subject: [PATCH 09/12] more tox and travis --- .travis.yml | 1 + tox.ini | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index f954523..8d2327e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ env: - TOX_ENV=py34-django17 - TOX_ENV=py34-django18 - TOX_ENV=py34-django19 + - TOX_ENV=allauth - TOX_ENV=flake8 matrix: include: diff --git a/tox.ini b/tox.ini index 57eb33b..805f1f5 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,14 @@ commands = coverage run manage.py test coverage report +[testenv:allauth] +deps = + {[testenv]deps} + django-allauth + +commands = + {[testenv]commands} + [testenv:flake8] basepython=python deps=flake8 From 7266415ba67c7865c86a340c1ebeac9c3094e184 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 22:50:50 +0000 Subject: [PATCH 10/12] tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 805f1f5..2a68237 100644 --- a/tox.ini +++ b/tox.ini @@ -16,8 +16,8 @@ commands = [testenv:allauth] deps = - {[testenv]deps} django-allauth + {[testenv]deps} commands = {[testenv]commands} From 79cf61ddfcf6f444cab77bfba4cf960da56988ef Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Tue, 1 Dec 2015 23:04:42 +0000 Subject: [PATCH 11/12] to --- .travis.yml | 1 - tox.ini | 8 -------- 2 files changed, 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8d2327e..f954523 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ env: - TOX_ENV=py34-django17 - TOX_ENV=py34-django18 - TOX_ENV=py34-django19 - - TOX_ENV=allauth - TOX_ENV=flake8 matrix: include: diff --git a/tox.ini b/tox.ini index 2a68237..57eb33b 100644 --- a/tox.ini +++ b/tox.ini @@ -14,14 +14,6 @@ commands = coverage run manage.py test coverage report -[testenv:allauth] -deps = - django-allauth - {[testenv]deps} - -commands = - {[testenv]commands} - [testenv:flake8] basepython=python deps=flake8 From 795dc7bda2d9e324e83215901a57557449c3d5c7 Mon Sep 17 00:00:00 2001 From: bee_keeper Date: Wed, 2 Dec 2015 11:37:56 +0000 Subject: [PATCH 12/12] tox --- tox.ini | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tox.ini b/tox.ini index 57eb33b..677188f 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,20 @@ commands = coverage run manage.py test coverage report +[testenv:allauth] +deps = + -rrequirements.txt + django16: Django<1.7 + django17: Django<1.8 + django18: Django<1.9 + django19: Django==1.9rc1 + +commands = + python -V + pip install django-allauth + coverage run manage.py test + coverage report + [testenv:flake8] basepython=python deps=flake8