Skip to content

Commit

Permalink
Merge pull request #340 from intgr-forks/remove-python2-compat
Browse files Browse the repository at this point in the history
Modernize and remove Python 2.x compatibility code
  • Loading branch information
alanjds authored May 9, 2024
2 parents 024156f + b1ab349 commit a636f45
Show file tree
Hide file tree
Showing 12 changed files with 39 additions and 68 deletions.
13 changes: 3 additions & 10 deletions rest_framework_nested/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,12 @@
These fields allow you to specify the style that should be used to represent
model relationships with hyperlinks.
"""
from __future__ import unicode_literals
from functools import reduce # import reduce from functools for compatibility with python 3
from functools import reduce

import rest_framework.relations
from rest_framework.relations import ObjectDoesNotExist, ObjectValueError, ObjectTypeError
from rest_framework.exceptions import ValidationError

# fix for basestring
try:
basestring
except NameError:
basestring = str


class NestedHyperlinkedRelatedField(rest_framework.relations.HyperlinkedRelatedField):
lookup_field = 'pk'
Expand All @@ -26,7 +19,7 @@ class NestedHyperlinkedRelatedField(rest_framework.relations.HyperlinkedRelatedF

def __init__(self, *args, **kwargs):
self.parent_lookup_kwargs = kwargs.pop('parent_lookup_kwargs', self.parent_lookup_kwargs)
super(NestedHyperlinkedRelatedField, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)

def get_url(self, obj, view_name, request, format):
"""
Expand Down Expand Up @@ -103,4 +96,4 @@ def __init__(self, view_name=None, **kwargs):
assert view_name is not None, 'The `view_name` argument is required.'
kwargs['read_only'] = True
kwargs['source'] = '*'
super(NestedHyperlinkedIdentityField, self).__init__(view_name=view_name, **kwargs)
super().__init__(view_name=view_name, **kwargs)
16 changes: 6 additions & 10 deletions rest_framework_nested/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
urlpatterns = router.urls
"""

from __future__ import unicode_literals
import sys
import re
from rest_framework.routers import SimpleRouter, DefaultRouter # noqa: F401
Expand All @@ -37,22 +36,22 @@
IDENTIFIER_REGEX = re.compile(r"^[^\d\W]\w*$", re.UNICODE)


class LookupMixin(object):
class LookupMixin:
"""
Deprecated.
No method override is needed since Django Rest Framework 2.4.
"""


class NestedMixin(object):
class NestedMixin:
def __init__(self, parent_router, parent_prefix, *args, **kwargs):
self.parent_router = parent_router
self.parent_prefix = parent_prefix
self.nest_count = getattr(parent_router, 'nest_count', 0) + 1
self.nest_prefix = kwargs.pop('lookup', 'nested_%i' % self.nest_count) + '_'
self.nest_prefix = kwargs.pop('lookup', f'nested_{self.nest_count}') + '_'

super(NestedMixin, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)

if 'trailing_slash' not in kwargs:
# Inherit trailing_slash only when not specified explicitly.
Expand Down Expand Up @@ -84,10 +83,7 @@ def __init__(self, parent_router, parent_prefix, *args, **kwargs):
nested_routes = []
parent_lookup_regex = parent_router.get_lookup_regex(parent_viewset, self.nest_prefix)

self.parent_regex = '{parent_prefix}/{parent_lookup_regex}/'.format(
parent_prefix=parent_prefix,
parent_lookup_regex=parent_lookup_regex
)
self.parent_regex = f'{parent_prefix}/{parent_lookup_regex}/'
# If there is no parent prefix, the first part of the url is probably
# controlled by the project's urls.py and the router is in an app,
# so a slash in the beginning will (A) cause Django to give warnings
Expand All @@ -111,7 +107,7 @@ def __init__(self, parent_router, parent_prefix, *args, **kwargs):

def check_valid_name(self, value):
if IDENTIFIER_REGEX.match(value) is None:
raise ValueError("lookup argument '{}' needs to be valid python identifier".format(value))
raise ValueError(f"lookup argument '{value}' needs to be valid python identifier")


class NestedSimpleRouter(NestedMixin, SimpleRouter):
Expand Down
4 changes: 2 additions & 2 deletions rest_framework_nested/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ class NestedHyperlinkedModelSerializer(rest_framework.serializers.HyperlinkedMod

def __init__(self, *args, **kwargs):
self.parent_lookup_kwargs = kwargs.pop('parent_lookup_kwargs', self.parent_lookup_kwargs)
super(NestedHyperlinkedModelSerializer, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)

def build_url_field(self, field_name, model_class):
field_class, field_kwargs = super(NestedHyperlinkedModelSerializer, self).build_url_field(
field_class, field_kwargs = super().build_url_field(
field_name,
model_class
)
Expand Down
4 changes: 2 additions & 2 deletions rest_framework_nested/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def _force_mutable(querydict: dict) -> dict:
querydict._mutable = initial_mutability


class NestedViewSetMixin(object):
class NestedViewSetMixin:
def _get_parent_lookup_kwargs(self) -> dict:
"""
Locates and returns the `parent_lookup_kwargs` dict informing
Expand All @@ -44,7 +44,7 @@ def get_queryset(self):
Filter the `QuerySet` based on its parents as defined in the
`serializer_class.parent_lookup_kwargs` or `viewset.parent_lookup_kwargs`
"""
queryset = super(NestedViewSetMixin, self).get_queryset()
queryset = super().get_queryset()

if getattr(self, 'swagger_fake_view', False):
return queryset
Expand Down
4 changes: 1 addition & 3 deletions runtests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#! /usr/bin/env python
from __future__ import print_function

import pytest
import sys
import os
Expand Down Expand Up @@ -32,7 +30,7 @@ def flake8_main(args):

def split_class_and_function(string):
class_string, function_string = string.split('.', 1)
return "%s and %s" % (class_string, function_string)
return f"{class_string} and {function_string}"


def is_function(string):
Expand Down
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
import os
import sys
Expand Down Expand Up @@ -59,8 +58,8 @@ def get_package_data(package):
os.system("python setup.py sdist bdist_wheel")
os.system("twine upload dist/*")
print("You probably want to also tag the version now:")
print(" git tag -a v{0} -m 'version {0}'".format(version))
print(" git push origin v{0}".format(version))
print(f" git tag -a v{version} -m 'version {version}'")
print(f" git push origin v{version}")
sys.exit()


Expand Down
8 changes: 0 additions & 8 deletions tests/description.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
# -- coding: utf-8 --

# Apparently there is a python 2.6 issue where docstrings of imported view classes
# do not retain their encoding information even if a module has a proper
# encoding declaration at the top of its source file. Therefore for tests
# to catch unicode related errors, a mock view has to be declared in a separate
# module.

from rest_framework.views import APIView


Expand Down
3 changes: 1 addition & 2 deletions tests/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from __future__ import unicode_literals
from django.db import models
from rest_framework import serializers

Expand All @@ -11,7 +10,7 @@ class CustomField(models.CharField):

def __init__(self, *args, **kwargs):
kwargs['max_length'] = 12
super(CustomField, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)


class RESTFrameworkModel(models.Model):
Expand Down
2 changes: 1 addition & 1 deletion tests/serializers/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def setUpClass(cls):
GrandChild1.objects.create(parent=child2, name='Child2-GrandChild1-C')

Parent.objects.create(name='Parent2')
return super(TestSerializers, cls).setUpClass()
return super().setUpClass()

def test_default(self):
url = reverse('parent1-detail', kwargs={'pk': 1})
Expand Down
6 changes: 0 additions & 6 deletions tests/test_dummy.py

This file was deleted.

16 changes: 8 additions & 8 deletions tests/test_dynamic_routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,41 +82,41 @@ def test_dynamic_routes(self):
self.assertFalse(hasattr(self.router, 'parent_regex'))
urls = map_by_name(self.router.urls)
self.assertEqual(
get_regex_pattern(urls['basicmodel-list']), u'^detail/$'
get_regex_pattern(urls['basicmodel-list']), '^detail/$'
)
self.assertEqual(
get_regex_pattern(urls['basicmodel-detail']),
u'^detail/(?P<pk>[^/.]+)/$'
'^detail/(?P<pk>[^/.]+)/$'
)
self.assertEqual(
get_regex_pattern(urls['basicmodel-set-password']),
u'^detail/(?P<pk>[^/.]+)/set_password/$'
'^detail/(?P<pk>[^/.]+)/set_password/$'
)

def test_nested_parent(self):
self.assertEqual(
self.detail_router.parent_regex,
u'detail/(?P<detail_pk>[^/.]+)/'
'detail/(?P<detail_pk>[^/.]+)/'
)
urls = map_by_name(self.detail_router.urls)

self.assertEqual(
get_regex_pattern(urls['basicmodel-list']),
u'^detail/(?P<detail_pk>[^/.]+)/list/$'
'^detail/(?P<detail_pk>[^/.]+)/list/$'
)

self.assertEqual(
get_regex_pattern(urls['basicmodel-recent-users']),
u'^detail/(?P<detail_pk>[^/.]+)/list/recent_users/$'
'^detail/(?P<detail_pk>[^/.]+)/list/recent_users/$'
)

self.assertEqual(
get_regex_pattern(urls['basicmodel-detail']),
u'^detail/(?P<detail_pk>[^/.]+)/list/(?P<pk>[^/.]+)/$'
'^detail/(?P<detail_pk>[^/.]+)/list/(?P<pk>[^/.]+)/$'
)

def test_nested_child(self):
self.assertEqual(
self.list_router.parent_regex,
u'detail/(?P<detail_pk>[^/.]+)/list/(?P<list_pk>[^/.]+)/'
'detail/(?P<detail_pk>[^/.]+)/list/(?P<list_pk>[^/.]+)/'
)
26 changes: 13 additions & 13 deletions tests/test_routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,20 @@ def test_recursive_nested_simple_routers(self):
self.assertFalse(hasattr(self.router, 'parent_regex'))
urls = self.router.urls
self.assertEqual(len(urls), 2)
self.assertEqual(get_regex_pattern(urls[0]), u'^a/$')
self.assertEqual(get_regex_pattern(urls[1]), u'^a/(?P<pk>[0-9a-f]{32})/$')
self.assertEqual(get_regex_pattern(urls[0]), '^a/$')
self.assertEqual(get_regex_pattern(urls[1]), '^a/(?P<pk>[0-9a-f]{32})/$')

self.assertEqual(self.a_router.parent_regex, u'a/(?P<a_pk>[0-9a-f]{32})/')
self.assertEqual(self.a_router.parent_regex, 'a/(?P<a_pk>[0-9a-f]{32})/')
urls = self.a_router.urls
self.assertEqual(len(urls), 2)
self.assertEqual(get_regex_pattern(urls[0]), u'^a/(?P<a_pk>[0-9a-f]{32})/b/$')
self.assertEqual(get_regex_pattern(urls[1]), u'^a/(?P<a_pk>[0-9a-f]{32})/b/(?P<pk>[^/.]+)/$')
self.assertEqual(get_regex_pattern(urls[0]), '^a/(?P<a_pk>[0-9a-f]{32})/b/$')
self.assertEqual(get_regex_pattern(urls[1]), '^a/(?P<a_pk>[0-9a-f]{32})/b/(?P<pk>[^/.]+)/$')

self.assertEqual(self.b_router.parent_regex, u'a/(?P<a_pk>[0-9a-f]{32})/b/(?P<b_pk>[^/.]+)/')
self.assertEqual(self.b_router.parent_regex, 'a/(?P<a_pk>[0-9a-f]{32})/b/(?P<b_pk>[^/.]+)/')
urls = self.b_router.urls
self.assertEqual(len(urls), 2)
self.assertEqual(get_regex_pattern(urls[0]), u'^a/(?P<a_pk>[0-9a-f]{32})/b/(?P<b_pk>[^/.]+)/c/$')
self.assertEqual(get_regex_pattern(urls[1]), u'^a/(?P<a_pk>[0-9a-f]{32})/b/(?P<b_pk>[^/.]+)/c/(?P<pk>[^/.]+)/$')
self.assertEqual(get_regex_pattern(urls[0]), '^a/(?P<a_pk>[0-9a-f]{32})/b/(?P<b_pk>[^/.]+)/c/$')
self.assertEqual(get_regex_pattern(urls[1]), '^a/(?P<a_pk>[0-9a-f]{32})/b/(?P<b_pk>[^/.]+)/c/(?P<pk>[^/.]+)/$')


class TestEmptyPrefix(TestCase):
Expand All @@ -101,8 +101,8 @@ def test_empty_prefix(self):
urls = self.router.urls
urls = self.a_router.urls
self.assertEqual(len(urls), 2)
self.assertEqual(get_regex_pattern(urls[0]), u'^(?P<a_pk>[0-9a-f]{32})/b/$')
self.assertEqual(get_regex_pattern(urls[1]), u'^(?P<a_pk>[0-9a-f]{32})/b/(?P<pk>[^/.]+)/$')
self.assertEqual(get_regex_pattern(urls[0]), '^(?P<a_pk>[0-9a-f]{32})/b/$')
self.assertEqual(get_regex_pattern(urls[1]), '^(?P<a_pk>[0-9a-f]{32})/b/(?P<pk>[^/.]+)/$')


class TestBadLookupValue(TestCase):
Expand Down Expand Up @@ -134,12 +134,12 @@ class TestRouterSettingInheritance(TestCase):
"""

def _assertHasTrailingSlash(self, router):
self.assertEqual(router.trailing_slash, u'/', "router does not have trailing slash when it should")
self.assertEqual(router.trailing_slash, '/', "router does not have trailing slash when it should")
self.assertTrue(pattern_from_url(router.urls[0]).endswith('/$'),
"router created url without trailing slash when it should have")

def _assertDoesNotHaveTrailingSlash(self, router):
self.assertEqual(router.trailing_slash, u'', "router has trailing slash when it should not")
self.assertEqual(router.trailing_slash, '', "router has trailing slash when it should not")
self.assertFalse(pattern_from_url(router.urls[0]).endswith('/$'),
"router created url with trailing slash when it should not have")

Expand Down Expand Up @@ -198,6 +198,6 @@ def test_inherits_nonstandard_trailing_slash(self):
a_router = NestedSimpleRouter(router, 'a', lookup='a')
a_router.register('b', BViewSet)

self.assertEqual(a_router.trailing_slash, u'/?', "router does not have trailing slash when it should")
self.assertEqual(a_router.trailing_slash, '/?', "router does not have trailing slash when it should")
self.assertTrue(pattern_from_url(a_router.urls[0]).endswith('/?$'),
"router created url without trailing slash when it should have")

0 comments on commit a636f45

Please sign in to comment.