ソースを参照

chore(api): update code style (mostly PEP 8), no functional changes

fixes too broad exception clauses
renames unused method variables to _, *_
uses "not in" instead of "not .. in .."
fixes number of blank lines
fixes line length
fixes import order
fixes spaces in func arguments
changes doc strings to use """
changes function names, variable names to lowercase with underscores
removes redundant paranthesis
fixes indentation
adds hints for pycharm type checker
fixes shadowed names
marks some only privately used methods as private
Nils Wisiol 6 年 前
コミット
9219915fa7

+ 1 - 0
api/api/settings_quick_test.py

@@ -1,5 +1,6 @@
 from api.settings import *
 
+# noinspection PyUnresolvedReferences
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.sqlite3',

+ 4 - 4
api/desecapi/authentication.py

@@ -48,7 +48,7 @@ class BasicTokenAuthentication(BaseAuthentication):
         try:
             user, key = base64.b64decode(basic).decode(HTTP_HEADER_ENCODING).split(':')
             token = self.model.objects.get(key=key)
-        except:
+        except Exception:
             raise exceptions.AuthenticationFailed(invalid_token_message)
 
         if not token.user.is_active:
@@ -72,16 +72,16 @@ class URLParamAuthentication(BaseAuthentication):
         using URL parameters.  Otherwise returns `None`.
         """
 
-        if not 'username' in request.query_params:
+        if 'username' not in request.query_params:
             msg = 'No username URL parameter provided.'
             raise exceptions.AuthenticationFailed(msg)
-        if not 'password' in request.query_params:
+        if 'password' not in request.query_params:
             msg = 'No password URL parameter provided.'
             raise exceptions.AuthenticationFailed(msg)
 
         return self.authenticate_credentials(request.query_params['username'], request.query_params['password'])
 
-    def authenticate_credentials(self, userid, key):
+    def authenticate_credentials(self, _, key):
         try:
             token = self.model.objects.get(key=key)
         except self.model.DoesNotExist:

+ 2 - 0
api/desecapi/emails.py

@@ -2,6 +2,7 @@ from django.template.loader import get_template
 from django.core.mail import EmailMessage
 from rest_framework.reverse import reverse
 
+
 def send_account_lock_email(request, user):
     content_tmpl = get_template('emails/captcha/content.txt')
     subject_tmpl = get_template('emails/captcha/subject.txt')
@@ -16,6 +17,7 @@ def send_account_lock_email(request, user):
                          [user.email])
     email.send()
 
+
 def send_token_email(context, user):
     content_tmpl = get_template('emails/user-token/content.txt')
     subject_tmpl = get_template('emails/user-token/subject.txt')

+ 4 - 2
api/desecapi/exceptions.py

@@ -1,5 +1,7 @@
-from rest_framework.exceptions import APIException
 import json
+from json import JSONDecodeError
+
+from rest_framework.exceptions import APIException
 
 
 class PdnsException(APIException):
@@ -11,5 +13,5 @@ class PdnsException(APIException):
         else:
             try:
                 self.detail = json.loads(response.text)['error']
-            except:
+            except (JSONDecodeError, KeyError):
                 self.detail = response.text

+ 5 - 3
api/desecapi/management/commands/privacy-chores.py

@@ -1,8 +1,10 @@
+from datetime import timedelta
+
 from django.core.management import BaseCommand
-from desecapi.models import User
-from api import settings
 from django.utils import timezone
-from datetime import timedelta
+
+from api import settings
+from desecapi.models import User
 
 
 class Command(BaseCommand):

+ 3 - 1
api/desecapi/management/commands/sync-from-pdns.py

@@ -1,4 +1,5 @@
 from django.core.management import BaseCommand, CommandError
+
 from desecapi.models import Domain
 
 
@@ -6,7 +7,8 @@ class Command(BaseCommand):
     help = 'Import authoritative data from pdns, making the local database consistent with pdns.'
 
     def add_arguments(self, parser):
-        parser.add_argument('domain-name', nargs='*', help='Domain name to import. If omitted, will import all domains that are known locally.')
+        parser.add_argument('domain-name', nargs='*',
+                            help='Domain name to import. If omitted, will import all domains that are known locally.')
 
     def handle(self, *args, **options):
         domains = Domain.objects.all()

+ 28 - 25
api/desecapi/models.py

@@ -1,16 +1,20 @@
+import datetime
+import random
+import time
+import uuid
+from base64 import b64encode
+from collections import OrderedDict
+from os import urandom
+
+import rest_framework.authtoken.models
 from django.conf import settings
-from django.db import models, transaction
 from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
-from django.utils import timezone
 from django.core.exceptions import SuspiciousOperation, ValidationError
-from desecapi import pdns, mixins
-import datetime, uuid
 from django.core.validators import MinValueValidator, RegexValidator
-from collections import OrderedDict
-import rest_framework.authtoken.models
-import time, random
-from os import urandom
-from base64 import b64encode
+from django.db import models, transaction
+from django.utils import timezone
+
+from desecapi import pdns, mixins
 
 
 def validate_lower(value):
@@ -52,9 +56,7 @@ class MyUserManager(BaseUserManager):
         Creates and saves a superuser with the given email, date of
         birth and password.
         """
-        user = self.create_user(email,
-                                password=password
-        )
+        user = self.create_user(email, password=password)
         user.is_admin = True
         user.save(using=self._db)
         return user
@@ -72,8 +74,8 @@ class Token(rest_framework.authtoken.models.Token):
 
     def save(self, *args, **kwargs):
         if not self.user_specific_id:
-            self.user_specific_id = random.randrange(16**8)
-        super().save(*args, **kwargs) # Call the "real" save() method.
+            self.user_specific_id = random.randrange(16 ** 8)
+        super().save(*args, **kwargs)  # Call the "real" save() method.
 
     def generate_key(self):
         return b64encode(urandom(21)).decode('utf-8').replace('/', '-').replace('=', '_').replace('+', '.')
@@ -92,9 +94,9 @@ class User(AbstractBaseUser):
     is_active = models.BooleanField(default=True)
     is_admin = models.BooleanField(default=False)
     registration_remote_ip = models.CharField(max_length=1024, blank=True)
-    locked = models.DateTimeField(null=True,blank=True)
+    locked = models.DateTimeField(null=True, blank=True)
     created = models.DateTimeField(auto_now_add=True)
-    limit_domains = models.IntegerField(default=settings.LIMIT_USER_DOMAIN_COUNT_DEFAULT,null=True,blank=True)
+    limit_domains = models.IntegerField(default=settings.LIMIT_USER_DOMAIN_COUNT_DEFAULT, null=True, blank=True)
     dyn = models.BooleanField(default=False)
 
     objects = MyUserManager()
@@ -118,19 +120,21 @@ class User(AbstractBaseUser):
     def __str__(self):
         return self.email
 
-    def has_perm(self, perm, obj=None):
-        "Does the user have a specific permission?"
+    # noinspection PyMethodMayBeStatic
+    def has_perm(self, *_):
+        """Does the user have a specific permission?"""
         # Simplest possible answer: Yes, always
         return True
 
-    def has_module_perms(self, app_label):
-        "Does the user have permissions to view the app `app_label`?"
+    # noinspection PyMethodMayBeStatic
+    def has_module_perms(self, *_):
+        """Does the user have permissions to view the app `app_label`?"""
         # Simplest possible answer: Yes, always
         return True
 
     @property
     def is_staff(self):
-        "Is the user a member of staff?"
+        """Is the user a member of staff?"""
         # Simplest possible answer: All admins are staff
         return self.is_admin
 
@@ -222,7 +226,7 @@ class Domain(models.Model, mixins.SetterMixin):
             except Domain.DoesNotExist:
                 pass
             else:
-                rrsets = RRset.plain_to_RRsets([
+                rrsets = RRset.plain_to_rrsets([
                     {'subname': subname, 'type': 'NS', 'ttl': 3600,
                      'contents': settings.DEFAULT_NS},
                     {'subname': subname, 'type': 'DS', 'ttl': 60,
@@ -458,9 +462,8 @@ class RRset(models.Model, mixins.SetterMixin):
     DEAD_TYPES = ('ALIAS', 'DNAME')
     RESTRICTED_TYPES = ('SOA', 'RRSIG', 'DNSKEY', 'NSEC3PARAM', 'OPT')
 
-
     class Meta:
-        unique_together = (("domain","subname","type"),)
+        unique_together = (("domain", "subname", "type"),)
 
     def __init__(self, *args, **kwargs):
         self._dirties = set()
@@ -526,7 +529,7 @@ class RRset(models.Model, mixins.SetterMixin):
             self._dirties = {}
 
     @staticmethod
-    def plain_to_RRsets(datas, *, domain):
+    def plain_to_rrsets(datas, *, domain):
         rrsets = {}
         for data in datas:
             rrset = RRset(domain=domain, subname=data['subname'],

+ 17 - 10
api/desecapi/pdns.py

@@ -1,10 +1,11 @@
-import requests
 import json
 import random
+
+import requests
+
 from api import settings
 from desecapi.exceptions import PdnsException
 
-
 headers_nslord = {
     'Accept': 'application/json',
     'User-Agent': 'desecapi',
@@ -40,7 +41,7 @@ def _pdns_delete_zone(domain):
         else:
             raise PdnsException(r2)
 
-    return (r1, r2)
+    return r1, r2
 
 
 def _pdns_request(method, path, body=None, acceptable_range=range(200, 300)):
@@ -126,13 +127,19 @@ def get_rrset_datas(domain):
 
 
 def set_rrsets(domain, rrsets, notify=True):
-    data = {'rrsets':
-        [{'name': rrset.name, 'type': rrset.type, 'ttl': rrset.ttl,
-          'changetype': 'REPLACE',
-          'records': [{'content': record.content, 'disabled': False}
-                      for record in rrset.records.all()]
-          }
-         for rrset in rrsets]
+    data = {
+        'rrsets':
+        [
+            {
+                'name': rrset.name, 'type': rrset.type, 'ttl': rrset.ttl,
+                'changetype': 'REPLACE',
+                'records': [
+                    {'content': record.content, 'disabled': False}
+                    for record in rrset.records.all()
+                ]
+            }
+            for rrset in rrsets
+        ]
     }
     _pdns_patch('/zones/' + domain.pdns_id, data)
 

+ 23 - 15
api/desecapi/serializers.py

@@ -1,14 +1,16 @@
+import re
+
+import django.core.exceptions
 from django.core.validators import RegexValidator
+from django.db import models, transaction
+from djoser import serializers as djoser_serializers
 from rest_framework import serializers
 from rest_framework.exceptions import ValidationError
-from desecapi.models import Domain, Donation, User, RR, RRset, Token
-from djoser import serializers as djoserSerializers
-from django.db import models, transaction
-import django.core.exceptions
-from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
-import re
 from rest_framework.fields import empty
 from rest_framework.settings import api_settings
+from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
+
+from desecapi.models import Domain, Donation, User, RR, RRset, Token
 
 
 class TokenSerializer(serializers.ModelSerializer):
@@ -44,10 +46,12 @@ class RRsetBulkListSerializer(BulkListSerializer):
         rrsets = {(obj.subname, obj.type): obj for obj in queryset.filter(q)}
         instance = [rrsets.get((data.get('subname', ''), data['type']), None)
                     for data in validated_data]
+        # noinspection PyUnresolvedReferences,PyProtectedMember
         return self.child._save(instance, validated_data)
 
     @transaction.atomic
     def create(self, validated_data):
+        # noinspection PyUnresolvedReferences,PyProtectedMember
         return self.child._save([None] * len(validated_data), validated_data)
 
 
@@ -122,7 +126,7 @@ class RRsetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
                 rrset.ttl = data.get('ttl', rrset.ttl)
             else:
                 # No known instance (creation or meaningless request)
-                if not 'ttl' in data:
+                if 'ttl' not in data:
                     if records:
                         # If we have records, this is a creation request, so we
                         # need a TTL.
@@ -202,7 +206,8 @@ class RRsetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
     def create(self, validated_data):
         return self._save(None, validated_data)
 
-    def validate_type(self, value):
+    @staticmethod
+    def validate_type(value):
         if value in RRset.DEAD_TYPES:
             raise serializers.ValidationError(
                 "The %s RRset type is currently unsupported." % value)
@@ -234,17 +239,19 @@ class DonationSerializer(serializers.ModelSerializer):
         model = Donation
         fields = ('name', 'iban', 'bic', 'amount', 'message', 'email')
 
-    def validate_bic(self, value):
+    @staticmethod
+    def validate_bic(value):
         return re.sub(r'[\s]', '', value)
 
-    def validate_iban(self, value):
+    @staticmethod
+    def validate_iban(value):
         return re.sub(r'[\s]', '', value)
 
 
-class UserSerializer(djoserSerializers.UserSerializer):
+class UserSerializer(djoser_serializers.UserSerializer):
     locked = serializers.SerializerMethodField()
 
-    class Meta(djoserSerializers.UserSerializer.Meta):
+    class Meta(djoser_serializers.UserSerializer.Meta):
         fields = tuple(User.REQUIRED_FIELDS) + (
             User.USERNAME_FIELD,
             'dyn',
@@ -253,13 +260,14 @@ class UserSerializer(djoserSerializers.UserSerializer):
         )
         read_only_fields = ('dyn', 'limit_domains', 'locked',)
 
-    def get_locked(self, obj):
+    @staticmethod
+    def get_locked(obj):
         return bool(obj.locked)
 
 
-class UserCreateSerializer(djoserSerializers.UserCreateSerializer):
+class UserCreateSerializer(djoser_serializers.UserCreateSerializer):
 
-    class Meta(djoserSerializers.UserCreateSerializer.Meta):
+    class Meta(djoser_serializers.UserCreateSerializer.Meta):
         fields = tuple(User.REQUIRED_FIELDS) + (
             User.USERNAME_FIELD,
             'password',

+ 6 - 3
api/desecapi/templatetags/sepa_extras.py

@@ -1,14 +1,17 @@
-from django import template
-import unicodedata
 import re
+import unicodedata
+
+from django import template
 
 register = template.Library()
 
+
 def clean(value):
     """Replaces non-ascii characters with their closest ascii
        representation and then removes everything but [A-Za-z0-9 ]"""
     normalized = unicodedata.normalize('NFKD', value)
-    cleaned = re.sub(r'[^A-Za-z0-9 ]','',normalized)
+    cleaned = re.sub(r'[^A-Za-z0-9 ]', '', normalized)
     return cleaned
 
+
 register.filter('clean', clean)

+ 7 - 1
api/desecapi/tests/base.py

@@ -301,7 +301,6 @@ class MockPDNSTestCase(APITestCase):
         request.pop('status')
         return request
 
-
     @classmethod
     def request_pdns_zone_retrieve(cls, name=None):
         return {
@@ -646,6 +645,13 @@ class DomainOwnerTestCase(DesecTestCase):
     NUM_OWNED_DOMAINS = 2
     NUM_OTHER_DOMAINS = 20
 
+    owner = None
+    my_domains = None
+    other_domains = None
+    my_domain = None
+    other_domain = None
+    token = None
+
     @classmethod
     def setUpTestDataWithPdns(cls):
         super().setUpTestDataWithPdns()

+ 3 - 3
api/desecapi/tests/testdomains.py

@@ -1,14 +1,14 @@
-import random
 import json
+import random
 
-from django.core import mail
 from django.conf import settings
+from django.core import mail
 from django.core.exceptions import ValidationError
 from rest_framework import status
 
 from desecapi.exceptions import PdnsException
-from desecapi.tests.base import DesecTestCase, DomainOwnerTestCase, LockedDomainOwnerTestCase
 from desecapi.models import Domain
+from desecapi.tests.base import DesecTestCase, DomainOwnerTestCase, LockedDomainOwnerTestCase
 
 
 class UnauthenticatedDomainTests(DesecTestCase):

+ 2 - 2
api/desecapi/tests/testdonations.py

@@ -1,6 +1,6 @@
-from rest_framework.reverse import reverse
-from rest_framework import status
 from django.core import mail
+from rest_framework import status
+from rest_framework.reverse import reverse
 
 from desecapi.tests.base import DesecTestCase
 

+ 1 - 1
api/desecapi/tests/testprivacychores.py

@@ -3,9 +3,9 @@ from datetime import timedelta
 from django.core.management import call_command
 from django.utils import timezone
 
+from api import settings
 from desecapi.models import User
 from desecapi.tests.base import DesecTestCase
-from api import settings
 
 
 class PrivacyChoresCommandTest(DesecTestCase):

+ 9 - 9
api/desecapi/tests/testregistration.py

@@ -1,24 +1,24 @@
 from datetime import timedelta
 
+from django.core import mail
 from django.test import RequestFactory
 from django.utils import timezone
-from django.core import mail
 from rest_framework.reverse import reverse
 from rest_framework.versioning import NamespaceVersioning
 
-from desecapi.tests.base import DesecTestCase
+from api import settings
 from desecapi import models
 from desecapi.emails import send_account_lock_email
-from api import settings
+from desecapi.tests.base import DesecTestCase
 
 
 class RegistrationTestCase(DesecTestCase):
 
-    def assertRegistration(self, REMOTE_ADDR='', status=201, **kwargs):
+    def assertRegistration(self, remote_addr='', status=201, **kwargs):
         url = reverse('v1:register')
         post_kwargs = {}
-        if REMOTE_ADDR:
-            post_kwargs['REMOTE_ADDR'] = REMOTE_ADDR
+        if remote_addr:
+            post_kwargs['REMOTE_ADDR'] = remote_addr
         response = self.client.post(url, kwargs, **post_kwargs)
         self.assertStatus(response, status)
         return response
@@ -32,7 +32,7 @@ class SingleRegistrationTestCase(RegistrationTestCase):
         self.assertRegistration(
             email=email,
             password=self.random_password(),
-            REMOTE_ADDR="1.3.3.7",
+            remote_addr="1.3.3.7",
         )
         self.user = models.User.objects.get(email=email)
 
@@ -59,7 +59,7 @@ class SingleRegistrationTestCase(RegistrationTestCase):
 class MultipleRegistrationTestCase(RegistrationTestCase):
 
     def _registrations(self):
-        pass
+        return []
 
     def setUp(self):
         super().setUp()
@@ -71,7 +71,7 @@ class MultipleRegistrationTestCase(RegistrationTestCase):
                 email=email,
                 password=self.random_password(),
                 dyn=True,
-                REMOTE_ADDR=ip,
+                remote_addr=ip,
             )
             user = models.User.objects.get(email=email)
             self.assertEqual(user.registration_remote_ip, ip)

+ 1 - 2
api/desecapi/urls/version_1.py

@@ -4,7 +4,6 @@ from rest_framework.routers import SimpleRouter
 
 from desecapi import views
 
-
 tokens_router = SimpleRouter()
 tokens_router.register(r'', views.TokenViewSet, base_name='token')
 
@@ -38,7 +37,7 @@ api_urls = [
     path('domains/<name>/rrsets/', views.RRsetList.as_view(), name='rrsets'),
     path('domains/<name>/rrsets/.../<type>/', views.RRsetDetail.as_view(), kwargs={'subname': ''}),
     re_path(r'domains/(?P<name>[^/]+)/rrsets/(?P<subname>[^/]*)\.\.\./(?P<type>[^/]+)/',
-                views.RRsetDetail.as_view(), name='rrset'),
+            views.RRsetDetail.as_view(), name='rrset'),
     path('domains/<name>/rrsets/@/<type>/', views.RRsetDetail.as_view(), kwargs={'subname': ''}),
     re_path(r'domains/(?P<name>[^/]+)/rrsets/(?P<subname>[^/]*)@/(?P<type>[^/]+)/',
             views.RRsetDetail.as_view(), name='rrset@'),

+ 114 - 93
api/desecapi/views.py

@@ -1,44 +1,45 @@
-from __future__ import unicode_literals
-from django.core.mail import EmailMessage
-from rest_framework.generics import get_object_or_404
+from __future__ import unicode_literals  # TODO remove!
 
-from desecapi.models import Domain, User, RRset, Token
-from desecapi.serializers import (
-    DomainSerializer, RRsetSerializer, DonationSerializer, TokenSerializer)
-from rest_framework import generics
-from desecapi.permissions import *
-from rest_framework import permissions
+import base64
+import binascii
+import ipaddress
+import os
+import re
+from datetime import timedelta
+
+import django.core.exceptions
+import djoser.views
+from django.contrib.auth import user_logged_in, user_logged_out
+from django.core.mail import EmailMessage
+from django.db.models import Q
 from django.http import Http404, HttpResponseRedirect
-from rest_framework.views import APIView
-from rest_framework.response import Response
-from rest_framework.reverse import reverse
-from rest_framework.authentication import get_authorization_header
-from desecapi.renderers import PlainTextRenderer
-from dns import resolver
+from django.shortcuts import render
 from django.template.loader import get_template
-import desecapi.authentication as auth
-import base64, binascii
-from api import settings
-from rest_framework.exceptions import (NotFound, PermissionDenied, ValidationError)
-import django.core.exceptions
-from djoser import views, signals
-from rest_framework import status
-from datetime import timedelta
 from django.utils import timezone
-from desecapi.forms import UnlockForm
-from django.shortcuts import render
-from django.db.models import Q
-from desecapi.emails import send_account_lock_email, send_token_email
-import re
-import ipaddress, os
-from rest_framework_bulk import ListBulkCreateUpdateAPIView
-from django.contrib.auth import user_logged_in, user_logged_out
-import djoser.views
+from djoser import views, signals
 from djoser.serializers import TokenSerializer as DjoserTokenSerializer
-from rest_framework.viewsets import GenericViewSet
+from dns import resolver
+from rest_framework import generics
 from rest_framework import mixins
+from rest_framework import status
+from rest_framework.authentication import get_authorization_header
+from rest_framework.exceptions import (NotFound, PermissionDenied, ValidationError)
+from rest_framework.generics import get_object_or_404
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
 from rest_framework.settings import api_settings
+from rest_framework.views import APIView
+from rest_framework.viewsets import GenericViewSet
+from rest_framework_bulk import ListBulkCreateUpdateAPIView
 
+import desecapi.authentication as auth
+from api import settings
+from desecapi.emails import send_account_lock_email, send_token_email
+from desecapi.forms import UnlockForm
+from desecapi.models import Domain, User, RRset, Token
+from desecapi.permissions import *
+from desecapi.renderers import PlainTextRenderer
+from desecapi.serializers import DomainSerializer, RRsetSerializer, DonationSerializer, TokenSerializer
 
 patternDyn = re.compile(r'^[A-Za-z-][A-Za-z0-9_-]*\.dedyn\.io$')
 patternNonDyn = re.compile(r'^([A-Za-z0-9-][A-Za-z0-9_-]*\.)+[A-Za-z]+$')
@@ -105,7 +106,10 @@ class DomainList(generics.ListCreateAPIView):
     def perform_create(self, serializer):
         pattern = patternDyn if self.request.user.dyn else patternNonDyn
         if pattern.match(serializer.validated_data['name']) is None:
-            ex = ValidationError(detail={"detail": "This domain name is not well-formed, by policy.", "code": "domain-illformed"})
+            ex = ValidationError(detail={
+                "detail": "This domain name is not well-formed, by policy.",
+                "code": "domain-illformed"}
+            )
             ex.status_code = status.HTTP_409_CONFLICT
             raise ex
 
@@ -124,8 +128,12 @@ class DomainList(generics.ListCreateAPIView):
             ex.status_code = status.HTTP_409_CONFLICT
             raise ex
 
-        if self.request.user.limit_domains is not None and self.request.user.domains.count() >= self.request.user.limit_domains:
-            ex = ValidationError(detail={"detail": "You reached the maximum number of domains allowed for your account.", "code": "domain-limit"})
+        if (self.request.user.limit_domains is not None and
+                self.request.user.domains.count() >= self.request.user.limit_domains):
+            ex = ValidationError(detail={
+                "detail": "You reached the maximum number of domains allowed for your account.",
+                "code": "domain-limit"
+            })
             ex.status_code = status.HTTP_403_FORBIDDEN
             raise ex
 
@@ -133,13 +141,16 @@ class DomainList(generics.ListCreateAPIView):
             obj = serializer.save(owner=self.request.user)
         except Exception as e:
             if str(e).endswith(' already exists'):
-                ex = ValidationError(detail={"detail": "This domain name is unavailable.", "code": "domain-unavailable"})
+                ex = ValidationError(detail={
+                    "detail": "This domain name is unavailable.",
+                    "code": "domain-unavailable"}
+                )
                 ex.status_code = status.HTTP_409_CONFLICT
                 raise ex
             else:
                 raise e
 
-        def sendDynDnsEmail(domain):
+        def send_dyn_dns_email(domain):
             content_tmpl = get_template('emails/domain-dyndns/content.txt')
             subject_tmpl = get_template('emails/domain-dyndns/subject.txt')
             from_tmpl = get_template('emails/from.txt')
@@ -156,7 +167,7 @@ class DomainList(generics.ListCreateAPIView):
             email.send()
 
         if obj.name.endswith('.dedyn.io'):
-            sendDynDnsEmail(obj)
+            send_dyn_dns_email(obj)
 
 
 class DomainDetail(generics.RetrieveUpdateDestroyAPIView):
@@ -220,7 +231,8 @@ class RRsetDetail(generics.RetrieveUpdateDestroyAPIView):
                 api_settings.NON_FIELD_ERRORS_KEY: ['Invalid input, expected a JSON object.']
             }, code='invalid')
 
-        if request.data.get('records') == []:
+        records = request.data.get('records')
+        if records is not None and len(records) == 0:
             return self.delete(request, *args, **kwargs)
 
         # Attach URL parameters (self.kwargs) to the request body object (request.data),
@@ -278,8 +290,10 @@ class RRsetList(ListBulkCreateUpdateAPIView):
         # default='' in the serializer field definition so that during PUT, the
         # subname value is retained if omitted.
         if isinstance(self.request.data, list):
-            serializer._validated_data = [{**{'subname': ''}, **data}
-                                         for data in serializer.validated_data]
+            serializer._validated_data = [
+                {**{'subname': ''}, **data}
+                for data in serializer.validated_data
+            ]
         else:
             serializer._validated_data = {**{'subname': ''}, **serializer.validated_data}
 
@@ -297,7 +311,7 @@ class RRsetList(ListBulkCreateUpdateAPIView):
 
 
 class Root(APIView):
-    def get(self, request, format=None):
+    def get(self, request, *_):
         if self.request.user and self.request.user.is_authenticated:
             return Response({
                 'domains': reverse('domain-list', request=request),
@@ -312,19 +326,21 @@ class Root(APIView):
 
 
 class DnsQuery(APIView):
-    def get(self, request, format=None):
-        desecio = resolver.Resolver()
 
-        if not 'domain' in request.GET:
+    @staticmethod
+    def get(request, *_):
+        dns_resolver = resolver.Resolver()
+
+        if 'domain' not in request.GET:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
         domain = str(request.GET['domain'])
 
-        def getRecords(domain, type_):
+        def get_records(domain_name, type_):
             records = []
             try:
-                for ip in desecio.query(domain, type_):
-                    records.append(str(ip))
+                for address in dns_resolver.query(domain_name, type_):
+                    records.append(str(address))
             except resolver.NoAnswer:
                 return []
             except resolver.NoNameservers:
@@ -334,24 +350,24 @@ class DnsQuery(APIView):
             return records
 
         # find currently active NS records
-        nsrecords = getRecords(domain, 'NS')
+        ns_records = get_records(domain, 'NS')
 
-        # find desec.io nameserver IP address with standard nameserver
-        ips = desecio.query('ns2.desec.io')
-        desecio.nameservers = []
+        # find desec.io name server IP address with standard name server
+        ips = dns_resolver.query('ns2.desec.io')
+        dns_resolver.nameservers = []
         for ip in ips:
-            desecio.nameservers.append(str(ip))
+            dns_resolver.nameservers.append(str(ip))
 
-        # query desec.io nameserver for A and AAAA records
-        arecords = getRecords(domain, 'A')
-        aaaarecords = getRecords(domain, 'AAAA')
+        # query desec.io name server for A and AAAA records
+        a_records = get_records(domain, 'A')
+        aaaa_records = get_records(domain, 'AAAA')
 
         return Response({
             'domain': domain,
-            'ns': nsrecords,
-            'a': arecords,
-            'aaaa': aaaarecords,
-            '_nameserver': desecio.nameservers
+            'ns': ns_records,
+            'a': a_records,
+            'aaaa': aaaa_records,
+            '_nameserver': dns_resolver.nameservers
         })
 
 
@@ -359,25 +375,26 @@ class DynDNS12Update(APIView):
     authentication_classes = (auth.TokenAuthentication, auth.BasicTokenAuthentication, auth.URLParamAuthentication,)
     renderer_classes = [PlainTextRenderer]
 
-    def findDomain(self, request):
+    def _find_domain(self, request):
         if self.request.user.locked:
             # Error code from https://help.dyn.com/remote-access-api/return-codes/
             raise PermissionDenied('abuse')
 
-        def findDomainname(request):
+        def find_domain_name(r):
             # 1. hostname parameter
-            if 'hostname' in request.query_params and request.query_params['hostname'] != 'YES':
-                return request.query_params['hostname']
+            if 'hostname' in r.query_params and r.query_params['hostname'] != 'YES':
+                return r.query_params['hostname']
 
             # 2. host_id parameter
-            if 'host_id' in request.query_params:
-                return request.query_params['host_id']
+            if 'host_id' in r.query_params:
+                return r.query_params['host_id']
 
             # 3. http basic auth username
             try:
-                domainname = base64.b64decode(get_authorization_header(request).decode().split(' ')[1].encode()).decode().split(':')[0]
-                if domainname:
-                    return domainname
+                domain_name = base64.b64decode(
+                    get_authorization_header(r).decode().split(' ')[1].encode()).decode().split(':')[0]
+                if domain_name:
+                    return domain_name
             except IndexError:
                 pass
             except UnicodeDecodeError:
@@ -386,31 +403,35 @@ class DynDNS12Update(APIView):
                 pass
 
             # 4. username parameter
-            if 'username' in request.query_params:
-                return request.query_params['username']
+            if 'username' in r.query_params:
+                return r.query_params['username']
 
             # 5. only domain associated with this user account
-            if len(request.user.domains.all()) == 1:
-                return request.user.domains.all()[0].name
-            if len(request.user.domains.all()) > 1:
-                ex = ValidationError(detail={"detail": "Request does not specify domain unambiguously.", "code": "domain-ambiguous"})
+            if len(r.user.domains.all()) == 1:
+                return r.user.domains.all()[0].name
+            if len(r.user.domains.all()) > 1:
+                ex = ValidationError(detail={
+                    "detail": "Request does not specify domain unambiguously.",
+                    "code": "domain-ambiguous"
+                })
                 ex.status_code = status.HTTP_409_CONFLICT
                 raise ex
 
             return None
 
-        name = findDomainname(request).lower()
+        name = find_domain_name(request).lower()
 
         try:
             return self.request.user.domains.get(name=name)
         except Domain.DoesNotExist:
             return None
 
-    def findIP(self, request, params, version=4):
+    @staticmethod
+    def find_ip(request, params, version=4):
         if version == 4:
-            lookfor = '.'
+            look_for = '.'
         elif version == 6:
-            lookfor = ':'
+            look_for = ':'
         else:
             raise Exception
 
@@ -419,31 +440,31 @@ class DynDNS12Update(APIView):
             if p in request.query_params:
                 if not len(request.query_params[p]):
                     return None
-                if lookfor in request.query_params[p]:
+                if look_for in request.query_params[p]:
                     return request.query_params[p]
 
         # Check remote IP address
         client_ip = get_client_ip(request)
-        if lookfor in client_ip:
+        if look_for in client_ip:
             return client_ip
 
         # give up
         return None
 
-    def findIPv4(self, request):
-        return self.findIP(request, ['myip', 'myipv4', 'ip'])
+    def _find_ip_v4(self, request):
+        return self.find_ip(request, ['myip', 'myipv4', 'ip'])
 
-    def findIPv6(self, request):
-        return self.findIP(request, ['myipv6', 'ipv6', 'myip', 'ip'], version=6)
+    def _find_ip_v6(self, request):
+        return self.find_ip(request, ['myipv6', 'ipv6', 'myip', 'ip'], version=6)
 
-    def get(self, request, format=None):
-        domain = self.findDomain(request)
+    def get(self, request, *_):
+        domain = self._find_domain(request)
 
         if domain is None:
             raise NotFound('nohost')
 
-        datas = {'A': self.findIPv4(request), 'AAAA': self.findIPv6(request)}
-        rrsets = RRset.plain_to_RRsets(
+        datas = {'A': self._find_ip_v4(request), 'AAAA': self._find_ip_v6(request)}
+        rrsets = RRset.plain_to_rrsets(
             [{'subname': '', 'type': type_, 'ttl': 60,
               'contents': [ip] if ip is not None else []}
              for type_, ip in datas.items()],
@@ -452,6 +473,7 @@ class DynDNS12Update(APIView):
 
         return Response('good', content_type='text/plain')
 
+
 class DonationList(generics.CreateAPIView):
     serializer_class = DonationSerializer
 
@@ -459,7 +481,7 @@ class DonationList(generics.CreateAPIView):
         iban = serializer.validated_data['iban']
         obj = serializer.save()
 
-        def sendDonationEmails(donation):
+        def send_donation_emails(donation):
             context = {
                 'donation': donation,
                 'creditoridentifier': settings.SEPA['CREDITOR_ID'],
@@ -493,9 +515,8 @@ class DonationList(generics.CreateAPIView):
                                      [donation.email])
                 email.send()
 
-
         # send emails
-        sendDonationEmails(obj)
+        send_donation_emails(obj)
 
 
 class UserCreateView(views.UserCreateView):