diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a14705c..ce247d6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,3 +6,13 @@ repos: hooks: - id: black args: ['--target-version', 'py37', '--skip-string-normalization', '--line-length', '110'] +- repo: https://github.com/PyCQA/isort + rev: 5.7.0 + hooks: + - id: isort + args: ['--profile', 'black', '--line-length', '110'] +- repo: https://github.com/asottile/pyupgrade + rev: v2.20.0 + hooks: + - id: pyupgrade + args: ['--keep-percent-format', '--py37-plus'] diff --git a/README b/README index fba31ef..16180a8 100644 --- a/README +++ b/README @@ -319,7 +319,7 @@ Unit tests are written using pytest and launched using tox, and can be run with: tox Code Style ----------- +========== black is used to format the code, using thoses parameters: @@ -328,6 +328,18 @@ black is used to format the code, using thoses parameters: There is .pre-commit-config.yaml to use pre-commit to automatically run black before commits. (execute `pre-commit install` to install the git hook.) +isort is used to format the imports, using those parameter: + + isort --profile black --line-length 110 + +pyupgrade is used to automatically upgrade syntax, using those parameters: + + pyupgrade --keep-percent-format --py37-plus + +There is .pre-commit-config.yaml to use pre-commit to automatically run black, +isort and pyupgrade before commits. (execute `pre-commit install` to install +the git hook.) + Remarks ======= diff --git a/mellon/adapters.py b/mellon/adapters.py index 6ec5f80..ee95a2f 100644 --- a/mellon/adapters.py +++ b/mellon/adapters.py @@ -13,33 +13,29 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals -from xml.etree import ElementTree as ET import hashlib - import logging import os import threading import time import uuid +from xml.etree import ElementTree as ET import lasso import requests import requests.exceptions from atomicwrites import atomic_write - -from django.core.exceptions import PermissionDenied, FieldDoesNotExist -from django.core.files.storage import default_storage -from django.contrib import auth +from django.contrib import auth, messages from django.contrib.auth.models import Group -from django.contrib import messages +from django.core.exceptions import FieldDoesNotExist, PermissionDenied +from django.core.files.storage import default_storage from django.utils import six from django.utils.encoding import force_text from django.utils.six.moves.urllib.parse import urlparse from django.utils.translation import ugettext as _ -from . import utils, app_settings, models +from . import app_settings, models, utils User = auth.get_user_model() @@ -51,7 +47,7 @@ class UserCreationError(Exception): def display_truncated_list(l, max_length=10): - s = '[' + ', '.join(map(six.text_type, l)) + s = '[' + ', '.join(map(str, l)) if len(l) > max_length: s += '..truncated more than %d items (%d)]' % (max_length, len(l)) else: @@ -59,7 +55,7 @@ def display_truncated_list(l, max_length=10): return s -class DefaultAdapter(object): +class DefaultAdapter: def __init__(self, request=None): self.request = request @@ -153,7 +149,7 @@ class DefaultAdapter(object): idp['METADATA'] = fd.read() # use file cache mtime as last_update time, prevent too many loading from different workers last_update = max(last_update, os.stat(file_cache_path).st_mtime) - except (IOError, OSError): + except OSError: warning('metadata url %s : error when loading the file cache %s', url, file_cache_path) # fresh cache, skip loading @@ -305,7 +301,7 @@ class DefaultAdapter(object): if saml_attributes['name_id_format'] == lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT: if transient_federation_attribute and saml_attributes.get(transient_federation_attribute): name_id = saml_attributes[transient_federation_attribute] - if not isinstance(name_id, six.string_types): + if not isinstance(name_id, str): if len(name_id) == 1: name_id = name_id[0] else: diff --git a/mellon/app_settings.py b/mellon/app_settings.py index 82361ad..a177abf 100644 --- a/mellon/app_settings.py +++ b/mellon/app_settings.py @@ -1,7 +1,7 @@ import sys -class AppSettings(object): +class AppSettings: __PREFIX = 'MELLON_' __DEFAULTS = { 'IDENTITY_PROVIDERS': [], diff --git a/mellon/backends.py b/mellon/backends.py index 025a2f3..668f8c5 100644 --- a/mellon/backends.py +++ b/mellon/backends.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals + import logging from django.contrib.auth.backends import ModelBackend diff --git a/mellon/middleware.py b/mellon/middleware.py index 5db67ac..0bcd59f 100644 --- a/mellon/middleware.py +++ b/mellon/middleware.py @@ -13,12 +13,11 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals -from django.utils.http import urlencode from django.http import HttpResponseRedirect -from django.utils.deprecation import MiddlewareMixin from django.urls import reverse +from django.utils.deprecation import MiddlewareMixin +from django.utils.http import urlencode from . import app_settings, utils diff --git a/mellon/migrations/0001_initial.py b/mellon/migrations/0001_initial.py index ed281b4..2e04812 100644 --- a/mellon/migrations/0001_initial.py +++ b/mellon/migrations/0001_initial.py @@ -1,8 +1,5 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): @@ -40,6 +37,6 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='usersamlidentifier', - unique_together=set([('issuer', 'name_id')]), + unique_together={('issuer', 'name_id')}, ), ] diff --git a/mellon/migrations/0002_sessionindex.py b/mellon/migrations/0002_sessionindex.py index 41d0309..5e5ebc4 100644 --- a/mellon/migrations/0002_sessionindex.py +++ b/mellon/migrations/0002_sessionindex.py @@ -1,7 +1,7 @@ # Generated by Django 2.2.12 on 2020-04-24 05:14 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/mellon/models.py b/mellon/models.py index 755756b..740487c 100644 --- a/mellon/models.py +++ b/mellon/models.py @@ -13,13 +13,12 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals from importlib import import_module +from django.conf import settings from django.db import models from django.utils.translation import ugettext_lazy as _ -from django.conf import settings class UserSAMLIdentifier(models.Model): diff --git a/mellon/sessions_backends/db.py b/mellon/sessions_backends/db.py index a45eadf..dbec306 100644 --- a/mellon/sessions_backends/db.py +++ b/mellon/sessions_backends/db.py @@ -29,10 +29,10 @@ class SessionStore(SessionStore): session_not_on_or_after = self.get_session_not_on_or_after() if session_not_on_or_after and 'expiry' not in kwargs: kwargs['expiry'] = session_not_on_or_after - return super(SessionStore, self).get_expiry_age(**kwargs) + return super().get_expiry_age(**kwargs) def get_expiry_date(self, **kwargs): session_not_on_or_after = self.get_session_not_on_or_after() if session_not_on_or_after and 'expiry' not in kwargs: kwargs['expiry'] = session_not_on_or_after - return super(SessionStore, self).get_expiry_date(**kwargs) + return super().get_expiry_date(**kwargs) diff --git a/mellon/urls.py b/mellon/urls.py index dca25b5..0e44152 100644 --- a/mellon/urls.py +++ b/mellon/urls.py @@ -1,11 +1,8 @@ -from __future__ import unicode_literals - -from django.conf.urls import url import django +from django.conf.urls import url from . import views - urlpatterns = [ url('login/$', views.login, name='mellon_login'), url('login/debug/$', views.debug_login, name='mellon_debug_login'), diff --git a/mellon/utils.py b/mellon/utils.py index 77bb899..a05fcae 100644 --- a/mellon/utils.py +++ b/mellon/utils.py @@ -13,23 +13,22 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals -import logging import datetime import importlib +import logging from functools import wraps -import isodate from xml.parsers import expat +import isodate +import lasso +from django.conf import settings from django.contrib import auth from django.template.loader import render_to_string from django.urls import reverse from django.utils.encoding import force_text -from django.utils.timezone import make_aware, now, make_naive, is_aware, get_default_timezone -from django.conf import settings from django.utils.six.moves.urllib.parse import urlparse -import lasso +from django.utils.timezone import get_default_timezone, is_aware, make_aware, make_naive, now from . import app_settings @@ -133,8 +132,7 @@ def get_idp(entity_id): def get_idps(): for adapter in get_adapters(): if hasattr(adapter, 'get_idps'): - for idp in adapter.get_idps(): - yield idp + yield from adapter.get_idps() def flatten_datetime(d): diff --git a/mellon/views.py b/mellon/views.py index c78c517..aac7a75 100644 --- a/mellon/views.py +++ b/mellon/views.py @@ -13,53 +13,43 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals +import logging +import uuid +import xml.etree.ElementTree as ET from contextlib import contextmanager, nullcontext from importlib import import_module from io import StringIO -import logging -import requests -import lasso -import uuid -from requests.exceptions import RequestException from xml.sax.saxutils import escape -import xml.etree.ElementTree as ET import django.http -from django.views.generic import View -from django.views.generic.base import RedirectView -from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden -from django.contrib import auth -from django.contrib.auth import get_user_model +import lasso +import requests from django.conf import settings -from django.views.decorators.csrf import csrf_exempt +from django.contrib import auth +from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model +from django.db import transaction +from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect from django.shortcuts import render, resolve_url from django.urls import reverse -from django.utils.http import urlencode from django.utils import six -from django.utils.encoding import force_text, force_str -from django.contrib.auth import REDIRECT_FIELD_NAME -from django.db import transaction +from django.utils.encoding import force_str, force_text +from django.utils.http import urlencode from django.utils.translation import ugettext as _ +from django.views.decorators.csrf import csrf_exempt +from django.views.generic import View +from django.views.generic.base import RedirectView +from requests.exceptions import RequestException -from . import app_settings, utils, models - +from . import app_settings, models, utils RETRY_LOGIN_COOKIE = 'MELLON_RETRY_LOGIN' lasso.setFlag('thin-sessions') -if six.PY3: - def lasso_decode(x): - return x - - -else: - - def lasso_decode(x): - return x.decode('utf-8') +def lasso_decode(x): + return x EO_NS = 'https://www.entrouvert.com/' @@ -71,16 +61,16 @@ User = get_user_model() class HttpResponseBadRequest(django.http.HttpResponseBadRequest): def __init__(self, *args, **kwargs): kwargs['content_type'] = kwargs.get('content_type', 'text/plain') - super(HttpResponseBadRequest, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self['X-Content-Type-Options'] = 'nosniff' -class LogMixin(object): +class LogMixin: """Initialize a module logger in new objects""" def __init__(self, *args, **kwargs): self.log = logging.getLogger(__name__) - super(LogMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def check_next_url(request, next_url): @@ -101,7 +91,7 @@ def check_next_url(request, next_url): return next_url -class ProfileMixin(object): +class ProfileMixin: profile = None def set_next_url(self, next_url): @@ -507,7 +497,7 @@ class LoginView(ProfileMixin, LogMixin, View): # configure requested AuthnClassRef authn_classref = utils.get_setting(idp, 'AUTHN_CLASSREF') if authn_classref: - authn_classref = tuple([str(x) for x in authn_classref]) + authn_classref = tuple(str(x) for x in authn_classref) req_authncontext = lasso.Samlp2RequestedAuthnContext() authn_request.requestedAuthnContext = req_authncontext req_authncontext.authnContextClassRef = authn_classref @@ -550,8 +540,7 @@ class LoginView(ProfileMixin, LogMixin, View): assert hasattr(authn_request.extensions, 'any'), 'extension nodes need lasso > 2.5.1' serialized = ET.tostring(node, 'utf-8') # tostring return bytes in PY3, but lasso needs str - if six.PY3: - serialized = serialized.decode('utf-8') + serialized = serialized.decode('utf-8') extension_content = authn_request.extensions.any or () extension_content += (serialized,) authn_request.extensions.any = extension_content @@ -653,7 +642,7 @@ class LogoutView(ProfileMixin, LogMixin, View): return HttpResponseBadRequest('error processing logout request: %r' % e) issuer = force_text(logout.remoteProviderId) - session_indexes = set(force_text(sessionIndex) for sessionIndex in logout.request.sessionIndexes) + session_indexes = {force_text(sessionIndex) for sessionIndex in logout.request.sessionIndexes} saml_identifier = ( models.UserSAMLIdentifier.objects.filter( diff --git a/setup.py b/setup.py index 6fa8b1e..5a130b4 100755 --- a/setup.py +++ b/setup.py @@ -5,11 +5,12 @@ import os import subprocess -from setuptools import setup, find_packages -from setuptools.command.install_lib import install_lib as _install_lib -from distutils.command.build import build as _build -from setuptools.command.sdist import sdist as _sdist from distutils.cmd import Command +from distutils.command.build import build as _build + +from setuptools import find_packages, setup +from setuptools.command.install_lib import install_lib as _install_lib +from setuptools.command.sdist import sdist as _sdist class compile_translations(Command): @@ -24,6 +25,7 @@ class compile_translations(Command): def run(self): import os + from django.core.management import call_command os.environ.pop('DJANGO_SETTINGS_MODULE', None) @@ -68,7 +70,7 @@ def get_version(): tag exists, take 0.0.0- and add the length of the commit log. """ if os.path.exists('VERSION'): - with open('VERSION', 'r') as v: + with open('VERSION') as v: return v.read() if os.path.exists('.git'): p = subprocess.Popen( diff --git a/tests/conftest.py b/tests/conftest.py index 882e300..2e668be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import os import logging +import os -import pytest import django_webtest +import pytest @pytest.fixture(autouse=True) diff --git a/tests/test_default_adapter.py b/tests/test_default_adapter.py index a8e3b4d..771c885 100644 --- a/tests/test_default_adapter.py +++ b/tests/test_default_adapter.py @@ -13,24 +13,21 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals import datetime import re -import lasso import time from multiprocessing.pool import ThreadPool +from unittest import mock -import mock +import lasso import pytest - from django.contrib import auth from django.db import connection from mellon.adapters import DefaultAdapter from mellon.backends import SAMLBackend - pytestmark = pytest.mark.django_db User = auth.get_user_model() @@ -119,7 +116,7 @@ def test_lookup_user_transaction(transactional_db, concurrency, idp, saml_attrib users = p.map(f, range(concurrency)) assert len(users) == concurrency - assert len(set(user.pk for user in users)) == 1 + assert len({user.pk for user in users}) == 1 def test_provision_user_attributes(settings, django_user_model, idp, saml_attributes, caplog): diff --git a/tests/test_sso_slo.py b/tests/test_sso_slo.py index 61eae22..970d50c 100644 --- a/tests/test_sso_slo.py +++ b/tests/test_sso_slo.py @@ -13,32 +13,29 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals -import datetime -from html import unescape -import re import base64 -import zlib +import datetime +import re import xml.etree.ElementTree as ET +import zlib +from html import unescape import lasso - import pytest -from pytest import fixture - -from django.contrib.sessions.models import Session from django.contrib.auth.models import User +from django.contrib.sessions.models import Session from django.urls import reverse from django.utils import six -from django.utils.six.moves.urllib import parse as urlparse from django.utils.encoding import force_str +from django.utils.six.moves.urllib import parse as urlparse +from httmock import HTTMock, all_requests +from httmock import response as mock_response +from pytest import fixture from mellon.utils import create_metadata from mellon.views import lasso_decode -from httmock import all_requests, HTTMock, response as mock_response - @fixture def idp_metadata(): @@ -81,7 +78,7 @@ def sp_metadata(sp_settings, rf): return create_metadata(request) -class MockIdp(object): +class MockIdp: session_dump = None identity_dump = None @@ -240,10 +237,10 @@ def test_sso_slo(db, app, idp, caplog, sp_settings): assert 'created new user' in caplog.text assert 'logged in using SAML' in caplog.text assert urlparse.urlparse(response['Location']).path == '/whatever/' - response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': str('/some/path')}) + response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': '/some/path'}) assert urlparse.urlparse(response['Location']).path == '/singleLogout' # again, user is already logged out - response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': str('/some/path')}) + response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': '/some/path'}) assert urlparse.urlparse(response['Location']).path == '/some/path' @@ -433,18 +430,11 @@ def test_sso_request_denied(db, app, idp, caplog, sp_settings): assert not relay_state assert url.endswith(reverse('mellon_login')) response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state}) - if six.PY3: - assert ( - "status is not success codes: ['urn:oasis:names:tc:SAML:2.0:status:Responder',\ + assert ( + "status is not success codes: ['urn:oasis:names:tc:SAML:2.0:status:Responder',\ 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied']" - in caplog.text - ) - else: - assert ( - "status is not success codes: [u'urn:oasis:names:tc:SAML:2.0:status:Responder',\ - u'urn:oasis:names:tc:SAML:2.0:status:RequestDenied']" - in caplog.text - ) + in caplog.text + ) @pytest.mark.urls('urls_tests_template_base') @@ -663,7 +653,7 @@ def test_passive_auth_middleware_ok(db, app, idp, caplog, settings): settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION' assert 'MELLON_PASSIVE_TRIED' not in app.cookies # webtest-lint is against unicode - app.set_cookie(str('IDP_SESSION'), str('1')) + app.set_cookie('IDP_SESSION', '1') response = app.get('/', headers={'Accept': force_str('text/html')}, status=302) assert urlparse.urlparse(response.location).path == '/login/' assert urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) == { @@ -681,7 +671,7 @@ def test_passive_auth_middleware_ok(db, app, idp, caplog, settings): assert 'MELLON_PASSIVE_TRIED' not in app.cookies # check passive authentication is tried again - app.set_cookie(str('IDP_SESSION'), str('1')) + app.set_cookie('IDP_SESSION', '1') response = app.get('/', headers={'Accept': force_str('text/html')}, status=302) assert urlparse.urlparse(response.location).path == '/login/' assert urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) == { @@ -695,7 +685,7 @@ def test_passive_auth_middleware_no_passive_auth_parameter(db, app, idp, caplog, settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION' assert 'MELLON_PASSIVE_TRIED' not in app.cookies # webtest-lint is against unicode - app.set_cookie(str('IDP_SESSION'), str('1')) + app.set_cookie('IDP_SESSION', '1') app.get('/?no-passive-auth', headers={'Accept': force_str('text/html')}, status=200) diff --git a/tests/test_utils.py b/tests/test_utils.py index fb5fa21..d4f38cb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -13,17 +13,16 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals import datetime +from unittest import mock -import mock import lasso - -from mellon.utils import create_metadata, iso8601_to_datetime, flatten_datetime -from mellon.views import check_next_url from xml_utils import assert_xml_constraints +from mellon.utils import create_metadata, flatten_datetime, iso8601_to_datetime +from mellon.views import check_next_url + def test_create_metadata(rf, private_settings, caplog): ns = { @@ -144,17 +143,17 @@ def test_flatten_datetime(): 'y': 1, 'z': 'u', } - assert set(flatten_datetime(d).keys()) == set(['x', 'y', 'z']) + assert set(flatten_datetime(d).keys()) == {'x', 'y', 'z'} assert flatten_datetime(d)['x'] == '2010-10-10T10:10:34' assert flatten_datetime(d)['y'] == 1 assert flatten_datetime(d)['z'] == 'u' def test_check_next_url(rf): - assert not check_next_url(rf.get('/'), u'') + assert not check_next_url(rf.get('/'), '') assert not check_next_url(rf.get('/'), None) - assert not check_next_url(rf.get('/'), u'\x00') - assert not check_next_url(rf.get('/'), u'\u010e') - assert not check_next_url(rf.get('/'), u'https://example.invalid/') + assert not check_next_url(rf.get('/'), '\x00') + assert not check_next_url(rf.get('/'), '\u010e') + assert not check_next_url(rf.get('/'), 'https://example.invalid/') # default hostname is testserver - assert check_next_url(rf.get('/'), u'http://testserver/ok/') + assert check_next_url(rf.get('/'), 'http://testserver/ok/') diff --git a/tests/test_views.py b/tests/test_views.py index 451709e..3783ce4 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -13,23 +13,20 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from __future__ import unicode_literals -import pytest -import mock -import lasso -from django.utils.six.moves.urllib.parse import parse_qs, urlparse import base64 import hashlib -from httmock import HTTMock +from unittest import mock +import lasso +import pytest from django.urls import reverse from django.utils.encoding import force_text from django.utils.http import urlencode - -from xml_utils import assert_xml_constraints - +from django.utils.six.moves.urllib.parse import parse_qs, urlparse +from httmock import HTTMock from utils import error_500, html_response +from xml_utils import assert_xml_constraints pytestmark = pytest.mark.django_db @@ -207,7 +204,7 @@ def test_sp_initiated_login(private_settings, client): assert response.status_code == 302 params = parse_qs(urlparse(response['Location']).query) assert response['Location'].startswith('http://idp5/singleSignOn?') - assert set(params.keys()) == set(['SAMLRequest', 'RelayState']) + assert set(params.keys()) == {'SAMLRequest', 'RelayState'} assert len(params['SAMLRequest']) == 1 assert base64.b64decode(params['SAMLRequest'][0]) assert client.session['mellon_next_url_%s' % params['RelayState'][0]] == '/whatever' @@ -229,7 +226,7 @@ def test_sp_initiated_login_chosen(private_settings, client): assert response.status_code == 302 params = parse_qs(urlparse(response['Location']).query) assert response['Location'].startswith('http://idp5/singleSignOn?') - assert set(params.keys()) == set(['SAMLRequest', 'RelayState']) + assert set(params.keys()) == {'SAMLRequest', 'RelayState'} assert len(params['SAMLRequest']) == 1 assert base64.b64decode(params['SAMLRequest'][0]) assert client.session['mellon_next_url_%s' % params['RelayState'][0]] == '/whatever' diff --git a/tests/urls_tests.py b/tests/urls_tests.py index 16e7602..233848c 100644 --- a/tests/urls_tests.py +++ b/tests/urls_tests.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from django.conf.urls import url, include +from django.conf.urls import include, url from django.http import HttpResponse diff --git a/tests/urls_tests_template_base.py b/tests/urls_tests_template_base.py index 8279af0..d800036 100644 --- a/tests/urls_tests_template_base.py +++ b/tests/urls_tests_template_base.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from django.conf.urls import url, include +from django.conf.urls import include, url from django.http import HttpResponse diff --git a/tests/urls_tests_template_hook.py b/tests/urls_tests_template_hook.py index 0301d63..716c4d9 100644 --- a/tests/urls_tests_template_hook.py +++ b/tests/urls_tests_template_hook.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from django.conf.urls import url, include +from django.conf.urls import include, url from django.http import HttpResponse diff --git a/testsettings.py b/testsettings.py index 99e1726..104e585 100644 --- a/testsettings.py +++ b/testsettings.py @@ -1,4 +1,5 @@ import os + import django from django.conf import global_settings diff --git a/tox.ini b/tox.ini index 1c71d1c..4b137b8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = black,py3-django22-coverage +envlist = code-style,py3-django22-coverage toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/django-mellon/ [testenv] @@ -55,12 +55,12 @@ commands = ./getlasso3.sh django-admin {posargs:--help} -[testenv:black] +[testenv:code-style] skip_install = true deps = pre-commit commands = - pre-commit run black --all-files --show-diff-on-failure + pre-commit run --all-files --show-diff-on-failure [pytest] junit_family=legacy