diff --git a/.isort.cfg b/.isort.cfg index e7ddf30..1d02ebd 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -4,8 +4,7 @@ line_length = 120 lines_after_imports = 2 multi_line_output = 5 -sections = FUTURE,STDLIB,SIX,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER -known_six = six +sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER known_third_party = freezegun known_django = django known_first_party = django_otp diff --git a/Pipfile.lock b/Pipfile.lock index 5d58dda..574e6e6 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -330,13 +330,6 @@ ], "version": "==0.9.1" }, - "six": { - "hashes": [ - "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", - "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" - ], - "version": "==1.13.0" - }, "snowballstemmer": { "hashes": [ "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", diff --git a/docs/source/conf.py b/docs/source/conf.py index 5f0ec45..c664275 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # django-otp documentation build configuration file, created by # sphinx-quickstart on Fri Jul 13 09:48:33 2012. # @@ -81,8 +79,8 @@ master_doc = 'index' # General information about the project. -project = u'django-otp' -copyright = u'2012, Peter Sagerson' +project = 'django-otp' +copyright = '2012, Peter Sagerson' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -225,7 +223,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'django-otp.tex', u'django-otp Documentation', + ('index', 'django-otp.tex', 'django-otp Documentation', 'Peter Sagerson', 'manual'), ] @@ -255,7 +253,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'django-otp', u'django-otp Documentation', + ('index', 'django-otp', 'django-otp Documentation', ['Peter Sagerson'], 1) ] @@ -269,7 +267,7 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'django-otp', u'django-otp Documentation', + ('index', 'django-otp', 'django-otp Documentation', 'Peter Sagerson', 'django-otp', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/source/extend.rst b/docs/source/extend.rst index a6c750f..cf8f8e0 100644 --- a/docs/source/extend.rst +++ b/docs/source/extend.rst @@ -31,7 +31,7 @@ interesting. Here's a simple implementation of a generic TOTP device:: key = models.CharField(max_length=80, validators=[hex_validator()], default=lambda: random_hex(20), - help_text=u'A hex-encoded secret key of up to 40 bytes.') + help_text='A hex-encoded secret key of up to 40 bytes.') @property def bin_key(self): diff --git a/setup.cfg b/setup.cfg index 9212391..6129493 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,3 @@ ignore = W504 # line too long E501 - -[bdist_wheel] -universal = 1 diff --git a/setup.py b/setup.py index cfa7136..52276ff 100755 --- a/setup.py +++ b/setup.py @@ -54,8 +54,8 @@ def find_package_data(*args, **kwargs): "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", - "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", "Topic :: Security", "Topic :: Software Development :: Libraries :: Python Modules", ], @@ -72,7 +72,6 @@ def find_package_data(*args, **kwargs): install_requires=[ 'django >= 1.11', - 'six >= 1.10.0' ], extras_require={ 'qrcode': ['qrcode'], diff --git a/src/django_otp/admin.py b/src/django_otp/admin.py index aa63c35..c815175 100644 --- a/src/django_otp/admin.py +++ b/src/django_otp/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - import django from django import forms from django.contrib.admin.forms import AdminAuthenticationForm @@ -35,17 +33,8 @@ class OTPAdminAuthenticationForm(AdminAuthenticationForm, OTPAuthenticationFormM # the otp_challenge submit button. otp_challenge = forms.CharField(required=False) - def __init__(self, *args, **kwargs): - super(OTPAdminAuthenticationForm, self).__init__(*args, **kwargs) - - # A litle extra cheese to make it prettier. - minor_django_version = django.VERSION[:2] - - if minor_django_version < (1, 6): - self.fields['otp_token'].widget.attrs['style'] = 'width: 14em;' - def clean(self): - self.cleaned_data = super(OTPAdminAuthenticationForm, self).clean() + self.cleaned_data = super().clean() self.clean_otp(self.get_user()) return self.cleaned_data @@ -72,11 +61,11 @@ class OTPAdminSite(AdminSite): login_template = _admin_template_for_django_version() def __init__(self, name='otpadmin'): - super(OTPAdminSite, self).__init__(name) + super().__init__(name) def has_permission(self, request): """ In addition to the default requirements, this only allows access to users who have been verified by a registered OTP device. """ - return super(OTPAdminSite, self).has_permission(request) and request.user.is_verified() + return super().has_permission(request) and request.user.is_verified() diff --git a/src/django_otp/conf.py b/src/django_otp/conf.py index 40c5518..62a7db1 100644 --- a/src/django_otp/conf.py +++ b/src/django_otp/conf.py @@ -1,11 +1,7 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from six import iteritems - import django.conf -class Settings(object): +class Settings: """ This is a simple class to take the place of the global settings object. An instance will contain all of our settings as attributes, with default values @@ -20,7 +16,7 @@ def __init__(self): Loads our settings from django.conf.settings, applying defaults for any that are omitted. """ - for name, default in iteritems(self.defaults): + for name, default in self.defaults.items(): value = getattr(django.conf.settings, name, default) setattr(self, name, value) diff --git a/src/django_otp/decorators.py b/src/django_otp/decorators.py index f3f5f1d..1eb35bd 100644 --- a/src/django_otp/decorators.py +++ b/src/django_otp/decorators.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.contrib.auth.decorators import user_passes_test from django_otp import user_has_device diff --git a/src/django_otp/forms.py b/src/django_otp/forms.py index 581d4d3..37ae451 100644 --- a/src/django_otp/forms.py +++ b/src/django_otp/forms.py @@ -1,15 +1,13 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django import forms from django.contrib.auth.forms import AuthenticationForm -from django.utils.translation import ugettext_lazy as _ -from django.utils.translation import ungettext_lazy +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy from . import devices_for_user, match_token from .models import Device, VerifyNotAllowed -class OTPAuthenticationFormMixin(object): +class OTPAuthenticationFormMixin: """ Shared functionality for :class:`~django.contrib.auth.forms.AuthenticationForm` subclasses that wish @@ -62,7 +60,7 @@ class CustomAuthForm(OTPAuthenticationFormMixin, AuthenticationForm): 'not_interactive': _('The selected OTP device is not interactive'), 'challenge_message': _('OTP Challenge: {0}'), 'invalid_token': _('Invalid token. Please make sure you have entered it correctly.'), - 'n_failed_attempts': ungettext_lazy( + 'n_failed_attempts': ngettext_lazy( "Verification temporarily disabled because of %(failure_count)d failed attempt, please try again soon.", "Verification temporarily disabled because of %(failure_count)d failed attempts, please try again soon.", "failure_count"), @@ -235,7 +233,7 @@ class OTPAuthenticationForm(OTPAuthenticationFormMixin, AuthenticationForm): otp_challenge = forms.CharField(required=False) def clean(self): - self.cleaned_data = super(OTPAuthenticationForm, self).clean() + self.cleaned_data = super().clean() self.clean_otp(self.get_user()) return self.cleaned_data @@ -282,13 +280,13 @@ def verify(request): otp_challenge = forms.CharField(required=False) def __init__(self, user, request=None, *args, **kwargs): - super(OTPTokenForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.user = user self.fields['otp_device'].choices = self.device_choices(user) def clean(self): - super(OTPTokenForm, self).clean() + super().clean() self.clean_otp(self.user) diff --git a/src/django_otp/middleware.py b/src/django_otp/middleware.py index 5d36d7b..a047099 100644 --- a/src/django_otp/middleware.py +++ b/src/django_otp/middleware.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - import functools from django.utils.functional import SimpleLazyObject @@ -12,7 +10,7 @@ def is_verified(user): return user.otp_device is not None -class OTPMiddleware(object): +class OTPMiddleware: """ This must be installed after :class:`~django.contrib.auth.middleware.AuthenticationMiddleware` and diff --git a/src/django_otp/models.py b/src/django_otp/models.py index f09c4a3..d43f224 100644 --- a/src/django_otp/models.py +++ b/src/django_otp/models.py @@ -1,7 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import six - from django.apps import apps from django.conf import settings from django.core.exceptions import ObjectDoesNotExist @@ -77,16 +73,10 @@ class Device(models.Model): objects = DeviceManager() - class Meta(object): + class Meta: abstract = True def __str__(self): - if six.PY3: - return self.__unicode__() - else: - return self.__unicode__().encode('utf-8') - - def __unicode__(self): try: user = self.user except ObjectDoesNotExist: @@ -256,7 +246,7 @@ def verify_is_allowed(self): 'failure_count': self.throttling_failure_count, }) - return super(ThrottlingMixin, self).verify_is_allowed() + return super().verify_is_allowed() def throttle_reset(self, commit=True): """ diff --git a/src/django_otp/oath.py b/src/django_otp/oath.py index 31b2356..3e8aa80 100644 --- a/src/django_otp/oath.py +++ b/src/django_otp/oath.py @@ -1,19 +1,8 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from hashlib import sha1 import hmac from struct import pack from time import time -import six - - -if six.PY3: - iterbytes = iter -else: - def iterbytes(buf): - return (ord(b) for b in buf) - def hotp(key, counter, digits=6): """ @@ -43,7 +32,7 @@ def hotp(key, counter, digits=6): """ msg = pack(b'>Q', counter) hs = hmac.new(key, msg, sha1).digest() - hs = list(iterbytes(hs)) + hs = list(iter(hs)) offset = hs[19] & 0x0f bin_code = (hs[offset] & 0x7f) << 24 | hs[offset + 1] << 16 | hs[offset + 2] << 8 | hs[offset + 3] @@ -87,7 +76,7 @@ def totp(key, step=30, t0=0, digits=6, drift=0): return TOTP(key, step, t0, digits, drift).token() -class TOTP(object): +class TOTP: """ An alternate TOTP interface. diff --git a/src/django_otp/plugins/otp_email/admin.py b/src/django_otp/plugins/otp_email/admin.py index 438ff8e..486a5cb 100644 --- a/src/django_otp/plugins/otp_email/admin.py +++ b/src/django_otp/plugins/otp_email/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.contrib import admin from django.contrib.admin.sites import AlreadyRegistered diff --git a/src/django_otp/plugins/otp_email/conf.py b/src/django_otp/plugins/otp_email/conf.py index 237a31d..d9114ea 100644 --- a/src/django_otp/plugins/otp_email/conf.py +++ b/src/django_otp/plugins/otp_email/conf.py @@ -1,11 +1,7 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from six import iteritems - import django.conf -class OTPEmailSettings(object): +class OTPEmailSettings: """ This is a simple class to take the place of the global settings object. An instance will contain all of our settings as attributes, with default values @@ -21,7 +17,7 @@ def __init__(self): Loads our settings from django.conf.settings, applying defaults for any that are omitted. """ - for name, default in iteritems(self.defaults): + for name, default in self.defaults.items(): value = getattr(django.conf.settings, name, default) setattr(self, name, value) diff --git a/src/django_otp/plugins/otp_email/migrations/0001_initial.py b/src/django_otp/plugins/otp_email/migrations/0001_initial.py index 376393f..813a5ba 100644 --- a/src/django_otp/plugins/otp_email/migrations/0001_initial.py +++ b/src/django_otp/plugins/otp_email/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.conf import settings from django.db import migrations, models diff --git a/src/django_otp/plugins/otp_email/models.py b/src/django_otp/plugins/otp_email/models.py index afa0aa6..a09c246 100644 --- a/src/django_otp/plugins/otp_email/models.py +++ b/src/django_otp/plugins/otp_email/models.py @@ -1,11 +1,8 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from binascii import unhexlify from django.core.mail import send_mail from django.db import models from django.template.loader import render_to_string -from django.utils.encoding import force_text from django_otp.models import Device from django_otp.oath import totp @@ -15,7 +12,7 @@ def default_key(): - return force_text(random_hex(20)) + return random_hex(20) def key_validator(value): diff --git a/src/django_otp/plugins/otp_email/tests.py b/src/django_otp/plugins/otp_email/tests.py index 4810d37..2c921e9 100644 --- a/src/django_otp/plugins/otp_email/tests.py +++ b/src/django_otp/plugins/otp_email/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.core import mail from django.db import IntegrityError from django.test.utils import override_settings diff --git a/src/django_otp/plugins/otp_hotp/admin.py b/src/django_otp/plugins/otp_hotp/admin.py index 091854f..9ebafc0 100644 --- a/src/django_otp/plugins/otp_hotp/admin.py +++ b/src/django_otp/plugins/otp_hotp/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.conf.urls import url from django.contrib import admin from django.contrib.admin.sites import AlreadyRegistered @@ -40,7 +38,7 @@ class HOTPDeviceAdmin(admin.ModelAdmin): radio_fields = {'digits': admin.HORIZONTAL} def get_queryset(self, request): - queryset = super(HOTPDeviceAdmin, self).get_queryset(request) + queryset = super().get_queryset(request) queryset = queryset.select_related('user') return queryset @@ -67,7 +65,7 @@ def get_urls(self): urls = [ url(r'^(?P\d+)/config/$', self.admin_site.admin_view(self.config_view), name='otp_hotp_hotpdevice_config'), url(r'^(?P\d+)/qrcode/$', self.admin_site.admin_view(self.qrcode_view), name='otp_hotp_hotpdevice_qrcode'), - ] + super(HOTPDeviceAdmin, self).get_urls() + ] + super().get_urls() return urls diff --git a/src/django_otp/plugins/otp_hotp/migrations/0001_initial.py b/src/django_otp/plugins/otp_hotp/migrations/0001_initial.py index aa04deb..9bb81a5 100644 --- a/src/django_otp/plugins/otp_hotp/migrations/0001_initial.py +++ b/src/django_otp/plugins/otp_hotp/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.conf import settings from django.db import migrations, models diff --git a/src/django_otp/plugins/otp_hotp/models.py b/src/django_otp/plugins/otp_hotp/models.py index da20f9e..b4185f1 100644 --- a/src/django_otp/plugins/otp_hotp/models.py +++ b/src/django_otp/plugins/otp_hotp/models.py @@ -1,14 +1,9 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from base64 import b32encode from binascii import unhexlify - -from six import string_types -from six.moves.urllib.parse import quote, urlencode +from urllib.parse import quote, urlencode from django.conf import settings from django.db import models -from django.utils.encoding import force_text from django_otp.models import Device, ThrottlingMixin from django_otp.oath import hotp @@ -16,7 +11,7 @@ def default_key(): - return force_text(random_hex(20)) + return random_hex(20) def key_validator(value): @@ -113,7 +108,7 @@ def config_url(self): urlencoded_params = urlencode(params) issuer = getattr(settings, 'OTP_HOTP_ISSUER', None) - if isinstance(issuer, string_types) and (issuer != ''): + if isinstance(issuer, str) and (issuer != ''): issuer = issuer.replace(':', '') label = '{}:{}'.format(issuer, label) urlencoded_params += '&issuer={}'.format(quote(issuer)) # encode issuer as per RFC 3986, not quote_plus diff --git a/src/django_otp/plugins/otp_hotp/tests.py b/src/django_otp/plugins/otp_hotp/tests.py index 21a0b54..18afdfd 100644 --- a/src/django_otp/plugins/otp_hotp/tests.py +++ b/src/django_otp/plugins/otp_hotp/tests.py @@ -1,8 +1,5 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from datetime import timedelta - -from six.moves.urllib.parse import parse_qs, urlsplit +from urllib.parse import parse_qs, urlsplit from freezegun import freeze_time diff --git a/src/django_otp/plugins/otp_static/admin.py b/src/django_otp/plugins/otp_static/admin.py index 88c0765..15714fe 100644 --- a/src/django_otp/plugins/otp_static/admin.py +++ b/src/django_otp/plugins/otp_static/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.contrib import admin from django.contrib.admin.sites import AlreadyRegistered diff --git a/src/django_otp/plugins/otp_static/lib.py b/src/django_otp/plugins/otp_static/lib.py index e52aeca..e0a1ab7 100644 --- a/src/django_otp/plugins/otp_static/lib.py +++ b/src/django_otp/plugins/otp_static/lib.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.contrib.auth import get_user_model from .models import StaticDevice, StaticToken diff --git a/src/django_otp/plugins/otp_static/management/commands/addstatictoken.py b/src/django_otp/plugins/otp_static/management/commands/addstatictoken.py index cb72e3d..500d0e6 100644 --- a/src/django_otp/plugins/otp_static/management/commands/addstatictoken.py +++ b/src/django_otp/plugins/otp_static/management/commands/addstatictoken.py @@ -1,9 +1,7 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from textwrap import fill from django.core.management.base import BaseCommand, CommandError -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django_otp.plugins.otp_static.lib import add_static_token, get_user_model @@ -25,4 +23,4 @@ def handle(self, *args, **options): except get_user_model().DoesNotExist: raise CommandError('User "{0}" does not exist.'.format(username)) - self.stdout.write(force_text(statictoken.token)) + self.stdout.write(force_str(statictoken.token)) diff --git a/src/django_otp/plugins/otp_static/migrations/0001_initial.py b/src/django_otp/plugins/otp_static/migrations/0001_initial.py index 83d1198..c96afe0 100644 --- a/src/django_otp/plugins/otp_static/migrations/0001_initial.py +++ b/src/django_otp/plugins/otp_static/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.conf import settings from django.db import migrations, models diff --git a/src/django_otp/plugins/otp_static/models.py b/src/django_otp/plugins/otp_static/models.py index 65c21d4..3afcf43 100644 --- a/src/django_otp/plugins/otp_static/models.py +++ b/src/django_otp/plugins/otp_static/models.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from base64 import b32encode from os import urandom diff --git a/src/django_otp/plugins/otp_static/tests.py b/src/django_otp/plugins/otp_static/tests.py index 9c4a930..9da2961 100644 --- a/src/django_otp/plugins/otp_static/tests.py +++ b/src/django_otp/plugins/otp_static/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.db import IntegrityError from django_otp.forms import OTPAuthenticationForm diff --git a/src/django_otp/plugins/otp_totp/admin.py b/src/django_otp/plugins/otp_totp/admin.py index 9839226..7e62f0f 100644 --- a/src/django_otp/plugins/otp_totp/admin.py +++ b/src/django_otp/plugins/otp_totp/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.conf.urls import url from django.contrib import admin from django.contrib.admin.sites import AlreadyRegistered @@ -40,7 +38,7 @@ class TOTPDeviceAdmin(admin.ModelAdmin): radio_fields = {'digits': admin.HORIZONTAL} def get_queryset(self, request): - queryset = super(TOTPDeviceAdmin, self).get_queryset(request) + queryset = super().get_queryset(request) queryset = queryset.select_related('user') return queryset @@ -67,7 +65,7 @@ def get_urls(self): urls = [ url(r'^(?P\d+)/config/$', self.admin_site.admin_view(self.config_view), name='otp_totp_totpdevice_config'), url(r'^(?P\d+)/qrcode/$', self.admin_site.admin_view(self.qrcode_view), name='otp_totp_totpdevice_qrcode'), - ] + super(TOTPDeviceAdmin, self).get_urls() + ] + super().get_urls() return urls diff --git a/src/django_otp/plugins/otp_totp/migrations/0001_initial.py b/src/django_otp/plugins/otp_totp/migrations/0001_initial.py index aa11f02..59b3609 100644 --- a/src/django_otp/plugins/otp_totp/migrations/0001_initial.py +++ b/src/django_otp/plugins/otp_totp/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.conf import settings from django.db import migrations, models diff --git a/src/django_otp/plugins/otp_totp/models.py b/src/django_otp/plugins/otp_totp/models.py index 694b191..f1ebc19 100644 --- a/src/django_otp/plugins/otp_totp/models.py +++ b/src/django_otp/plugins/otp_totp/models.py @@ -1,15 +1,10 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from base64 import b32encode from binascii import unhexlify +from urllib.parse import quote, urlencode import time -from six import string_types -from six.moves.urllib.parse import quote, urlencode - from django.conf import settings from django.db import models -from django.utils.encoding import force_text from django_otp.models import Device, ThrottlingMixin from django_otp.oath import TOTP @@ -17,7 +12,7 @@ def default_key(): - return force_text(random_hex(20)) + return random_hex(20) def key_validator(value): @@ -141,7 +136,7 @@ def config_url(self): urlencoded_params = urlencode(params) issuer = getattr(settings, 'OTP_TOTP_ISSUER', None) - if isinstance(issuer, string_types) and (issuer != ''): + if isinstance(issuer, str) and (issuer != ''): issuer = issuer.replace(':', '') label = '{}:{}'.format(issuer, label) urlencoded_params += '&issuer={}'.format(quote(issuer)) # encode issuer as per RFC 3986, not quote_plus diff --git a/src/django_otp/plugins/otp_totp/tests.py b/src/django_otp/plugins/otp_totp/tests.py index dad8a37..fad4c8d 100644 --- a/src/django_otp/plugins/otp_totp/tests.py +++ b/src/django_otp/plugins/otp_totp/tests.py @@ -1,9 +1,6 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from datetime import timedelta from time import time - -from six.moves.urllib.parse import parse_qs, urlsplit +from urllib.parse import parse_qs, urlsplit from freezegun import freeze_time diff --git a/src/django_otp/tests.py b/src/django_otp/tests.py index 919b909..7e8b374 100644 --- a/src/django_otp/tests.py +++ b/src/django_otp/tests.py @@ -1,13 +1,10 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from doctest import DocTestSuite import pickle import unittest -import django from django.contrib.auth import get_user_model from django.db import IntegrityError -import django.test +from django.test import RequestFactory, TestCase as DjangoTestCase from django_otp import DEVICE_ID_SESSION_KEY, oath, util from django_otp.middleware import OTPMiddleware @@ -23,13 +20,13 @@ def load_tests(loader, tests, pattern): return suite -class TestCase(django.test.TestCase): +class TestCase(DjangoTestCase): """ Utilities for dealing with custom user models. """ @classmethod def setUpClass(cls): - super(TestCase, cls).setUpClass() + super().setUpClass() cls.User = get_user_model() cls.USERNAME_FIELD = cls.User.USERNAME_FIELD @@ -46,7 +43,7 @@ def create_user(self, username, password): class OTPMiddlewareTestCase(TestCase): def setUp(self): - self.factory = django.test.RequestFactory() + self.factory = RequestFactory() try: self.alice = self.create_user('alice', 'password') self.bob = self.create_user('bob', 'password') diff --git a/src/django_otp/util.py b/src/django_otp/util.py index 5aeb031..53b345b 100644 --- a/src/django_otp/util.py +++ b/src/django_otp/util.py @@ -1,10 +1,6 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from binascii import hexlify, unhexlify +from binascii import unhexlify from os import urandom -import six - from django.core.exceptions import ValidationError @@ -16,7 +12,7 @@ def hex_validator(length=0): def key_validator(value): return hex_validator(20)(value) - key = models.CharField(max_length=40, validators=[key_validator], help_text=u'A hex-encoded 20-byte secret key') + key = models.CharField(max_length=40, validators=[key_validator], help_text='A hex-encoded 20-byte secret key') :param int length: If greater than 0, validation will fail unless the decoded value is exactly this number of bytes. @@ -36,7 +32,7 @@ def key_validator(value): """ def _validator(value): try: - if isinstance(value, six.text_type): + if isinstance(value, str): value = value.encode() unhexlify(value) @@ -58,7 +54,7 @@ def random_hex(length=20): :param int length: The number of (decoded) bytes to return. :returns: A string of hex digits. - :rtype: bytes + :rtype: str """ - return hexlify(urandom(length)) + return urandom(length).hex() diff --git a/src/django_otp/views.py b/src/django_otp/views.py index 54bd131..eebecdc 100644 --- a/src/django_otp/views.py +++ b/src/django_otp/views.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from functools import partial from django.contrib.auth import BACKEND_SESSION_KEY @@ -42,7 +40,7 @@ def form_valid(self, form): if not hasattr(user, 'backend'): user.backend = self.request.session[BACKEND_SESSION_KEY] - return super(LoginView, self).form_valid(form) + return super().form_valid(form) # Backwards compatibility. diff --git a/test/test_project/backends.py b/test/test_project/backends.py index a4e1bca..3da186a 100644 --- a/test/test_project/backends.py +++ b/test/test_project/backends.py @@ -1,7 +1,4 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - - -class DummyBackend(object): +class DummyBackend: def authenticate(self, request): return None diff --git a/test/test_project/settings.py b/test/test_project/settings.py index e0a9ddb..15c503f 100644 --- a/test/test_project/settings.py +++ b/test/test_project/settings.py @@ -1,7 +1,5 @@ # django-otp test project -from __future__ import absolute_import, division, print_function, unicode_literals - from os.path import abspath, dirname, join diff --git a/test/test_project/urls.py b/test/test_project/urls.py index ac13dd5..c338b3c 100644 --- a/test/test_project/urls.py +++ b/test/test_project/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - from django.conf.urls import url from django.contrib import admin import django.contrib.auth.views diff --git a/tox.ini b/tox.ini index 3f9f069..5d66de9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = static - py{27,37}-django111 + py{37}-django111 py{35,36}-django22 py{37,38}-django30 coverage @@ -9,7 +9,6 @@ envlist = static setenv = PYTHONPATH = {env:PYTHONPATH:}{:}{toxinidir}/test DJANGO_SETTINGS_MODULE = test_project.settings deps = freezegun - mock ; python_version < "3.0" django111: Django==1.11.* django22: Django==2.2.* django30: Django==3.0.*