Skip to content

Commit

Permalink
Add: Django 3.0 support (Dropping Py2) (#35)
Browse files Browse the repository at this point in the history
* Removes future/six imports
* Updates tox to newer Python/Django/DRF versions
* Updates django-test-plus for Django 3.0 support
* Updates docs for newly-supported versions
* Updates Readme with changes
  • Loading branch information
ChrisBurch authored and evenicoulddoit committed Dec 12, 2019
1 parent e7a43cd commit f1883e4
Show file tree
Hide file tree
Showing 18 changed files with 43 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ dist: xenial
language: python

python:
- "2.7"
- "3.6"
- "3.7"
- "3.8"

sudo: false

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ API, we suggest you consider switching to HashIds instead.
## Requirements
Tested against:

* Python (2.7, 3.6, 3.7)
* [Django](https://github.com/django/django) (1.11, 2.1, 2.2)
* [Django REST Framework](https://github.com/tomchristie/django-rest-framework) (3.7, 3.8, 3.9)
* Python (3.6, 3.7, 3.8)
* [Django](https://github.com/django/django) (2.1, 2.2, 3.0)
* [Django REST Framework](https://github.com/tomchristie/django-rest-framework) (3.8, 3.9, 3.10)
* [HashIds](https://github.com/davidaurelio/hashids-python) (>1.0)


Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Minimum Django and REST framework version
Django>=1.11
djangorestframework>=3.7.7
Django>=2.0
djangorestframework>=3.8.2

# Test requirements
pytest-django==3.4.8
pytest==4.4.1
pytest-cov==2.6.1
flake8==3.7.7
django-test-plus==1.1.1
django-test-plus==1.4.0
mock==2.0.0

# App requirements
Expand Down
3 changes: 1 addition & 2 deletions rest_framework_serializer_extensions/fields.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django.core.exceptions import ObjectDoesNotExist
from django.utils import six
from rest_framework.fields import Field
from rest_framework.relations import (
HyperlinkedIdentityField, HyperlinkedRelatedField)
Expand Down Expand Up @@ -37,7 +36,7 @@ def get_model(self):
'No "model" value passed to field "{0}"'
.format(type(self).__name__)
)
elif isinstance(self.model, six.string_types):
elif isinstance(self.model, str):
return utils.model_from_definition(self.model)
else:
return self.model
Expand Down
20 changes: 9 additions & 11 deletions rest_framework_serializer_extensions/serializers.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from __future__ import absolute_import
from collections import OrderedDict
from pprint import pformat

from django.core.exceptions import FieldDoesNotExist
from django.db.models import Prefetch
from django.db.models.fields.related import ForeignKey
from django.db.models.query import QuerySet
from django.utils import six
from rest_framework import serializers
from rest_framework.fields import empty

Expand Down Expand Up @@ -336,7 +334,7 @@ def _construct_relations(self, matcher):
"""
expand_fields = self._get_expand_fields()

for field_name, field in six.iteritems(expand_fields):
for field_name, field in expand_fields.items():
if field_name.endswith('_id'):
field_definition = self.expandable_fields[field_name[:-3]]
else:
Expand Down Expand Up @@ -416,7 +414,7 @@ def _get_related_name(self, field):
def _standardise_expandable_definitions(self, expandable_fields):
return {
key: self._standardise_expandable_definition(definition)
for key, definition in six.iteritems(expandable_fields)
for key, definition in expandable_fields.items()
}

def _standardise_expandable_definition(self, definition):
Expand All @@ -427,7 +425,7 @@ def _standardise_expandable_definition(self, definition):
definition = dict(serializer=definition)

# Resolve string references to serializers
if isinstance(definition['serializer'], six.string_types):
if isinstance(definition['serializer'], str):
reference = definition['serializer']
serializer = utils.import_local(reference)
assert issubclass(serializer, serializers.BaseSerializer), (
Expand Down Expand Up @@ -471,7 +469,7 @@ def _parse_root_instructions(self):
def _validate_max_depth(self, root_instructions):
max_depth = self.get_max_expand_depth()

for nested_field_names in six.itervalues(root_instructions):
for nested_field_names in root_instructions.values():
for nested_field_name in nested_field_names:
depth = len(nested_field_name.split(EXPAND_DELIMITER))

Expand Down Expand Up @@ -500,7 +498,7 @@ def _expand_instructions(self, root_instructions):
hierarchy = _get_serializer_hierarchy(self)
instructions = {}

for method, root_nested_names in six.iteritems(root_instructions):
for method, root_nested_names in root_instructions.items():
instructions[method] = {
n.split(EXPAND_DELIMITER)[0]
for n in _get_nested_field_names(hierarchy, root_nested_names)
Expand Down Expand Up @@ -592,7 +590,7 @@ def _validate_instructions(self, instructions, standard_fields):
):
return

for method, field_names in six.iteritems(instructions):
for method, field_names in instructions.items():
valid_field_names = set(self.expandable_fields)

# Allow unmatched full expand instructions provided that the
Expand Down Expand Up @@ -625,7 +623,7 @@ def _get_expand_fields(self, standard_fields=None):
if standard_fields:
self._validate_instructions(instructions, standard_fields)

field_iterator = six.iteritems(self.expandable_fields)
field_iterator = self.expandable_fields.items()

# As we add <fieldname>_id fields for foreign keys, take note of any
# that require translation to model instances in the case of an update
Expand Down Expand Up @@ -739,7 +737,7 @@ def get_fields(self):

return OrderedDict(
(name, field)
for name, field in six.iteritems(fields)
for name, field in fields.items()
if name in only_names
)

Expand Down Expand Up @@ -779,7 +777,7 @@ def get_fields(self):

return OrderedDict(
(name, field)
for name, field in six.iteritems(fields)
for name, field in fields.items()
if name not in exclude_names
)

Expand Down
3 changes: 1 addition & 2 deletions rest_framework_serializer_extensions/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils import six


def import_local(path_to_object):
Expand Down Expand Up @@ -93,7 +92,7 @@ def model_from_definition(model_definition):
Returns:
(django.db.models.Model)
"""
if isinstance(model_definition, six.string_types):
if isinstance(model_definition, str):
model = import_local(model_definition)
else:
model = model_definition
Expand Down
1 change: 0 additions & 1 deletion runtests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#! /usr/bin/env python
from __future__ import print_function

import pytest
import sys
Expand Down
7 changes: 3 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,10 @@ def get_long_description():
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Natural Language :: English',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Internet :: WWW/HTTP',
]
)
2 changes: 0 additions & 2 deletions tests/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from django.test import TestCase
from hashids import Hashids

Expand Down
2 changes: 0 additions & 2 deletions tests/test_auto_optimise.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import django
from django.db import connection
from django.http import QueryDict
Expand Down
22 changes: 6 additions & 16 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
from __future__ import absolute_import

# Django 1 vs. 2
try:
from django.core.urlresolvers import reverse
except ImportError:
from django.urls import reverse

from django.test import override_settings, RequestFactory, TestCase
from django.utils import six
from django.urls import reverse
from rest_framework.exceptions import ValidationError
from rest_framework.serializers import Serializer, ModelSerializer

Expand Down Expand Up @@ -78,7 +70,7 @@ class HashIdFieldTests(BaseFieldsTestCase):
Unit tests for the HashIdField
"""
def test_representation_requires_model(self):
with six.assertRaisesRegex(self, AssertionError, 'No "model"'):
with self.assertRaisesRegex(AssertionError, 'No "model"'):
fields.HashIdField().to_representation(self.car_model.pk)

def test_representation_explicit_model_object(self):
Expand All @@ -96,7 +88,7 @@ def test_representation_explicit_model_reference(self):
self.assertEqual(self.external_id(self.car_model), representation)

def test_representation_explicit_invalid_model_reference(self):
with six.assertRaisesRegex(self, AssertionError, 'not a Django model'):
with self.assertRaisesRegex(AssertionError, 'not a Django model'):
(
fields.HashIdField(model='tests.base.TEST_HASH_IDS')
.to_representation(self.car_model.pk)
Expand All @@ -119,7 +111,7 @@ def test_representation_explicit_model_serializer_method(self):
self.assertEqual(self.external_id(self.manufacturer), representation)

def test_internal_value_requires_model(self):
with six.assertRaisesRegex(self, AssertionError, 'No "model"'):
with self.assertRaisesRegex(AssertionError, 'No "model"'):
fields.HashIdField().to_internal_value(
self.external_id(self.car_model)
)
Expand All @@ -139,16 +131,14 @@ def test_internal_value_explicit_model_reference(self):
self.assertEqual(self.car_model.pk, internal_value)

def test_internal_value_explicit_invalid_model_reference(self):
with six.assertRaisesRegex(self, AssertionError, 'not a Django model'):
with self.assertRaisesRegex(AssertionError, 'not a Django model'):
(
fields.HashIdField(model='tests.base.TEST_HASH_IDS')
.to_internal_value(self.external_id(self.car_model))
)

def test_internal_value_hash_id_validation(self):
with six.assertRaisesRegex(
self, ValidationError, 'not a valid HashId'
):
with self.assertRaisesRegex(ValidationError, 'not a valid HashId'):
(
fields.HashIdField(model='tests.models.CarModel')
.to_internal_value('abc123')
Expand Down
2 changes: 0 additions & 2 deletions tests/test_serializers__exclude_fields_mixin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from rest_framework import serializers

from rest_framework_serializer_extensions.serializers import ExcludeFieldsMixin
Expand Down
2 changes: 0 additions & 2 deletions tests/test_serializers__expandable_fields_mixin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from django.test import override_settings
from rest_framework import serializers

Expand Down
2 changes: 0 additions & 2 deletions tests/test_serializers__helpers_mixin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from django.test import TestCase
from rest_framework import serializers

Expand Down
2 changes: 0 additions & 2 deletions tests/test_serializers__only_fields_mixin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from rest_framework import serializers

from rest_framework_serializer_extensions.serializers import OnlyFieldsMixin
Expand Down
7 changes: 2 additions & 5 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from __future__ import absolute_import

from django.contrib.contenttypes.models import ContentType
from django.test import override_settings, TestCase
from django.utils import six

from rest_framework_serializer_extensions import fields, utils
from test_package.test_module.serializers import TestSerializer
Expand Down Expand Up @@ -71,7 +68,7 @@ class GetHashIdsSourceTests(TestCase):
Unit tests for the get_hash_ids_source() utility method.
"""
def test_unset_raises(self):
with six.assertRaisesRegex(self, AssertionError, 'No HASH_IDS_SOURCE'):
with self.assertRaisesRegex(AssertionError, 'No HASH_IDS_SOURCE'):
utils.get_hash_ids_source()

@override_settings(
Expand Down Expand Up @@ -169,7 +166,7 @@ def test_pass_non_model_class_raises_assertion_error(self):
"""
An error should be raised when a non-Django model class is passed.
"""
with six.assertRaisesRegex(self, AssertionError, 'not a Django model'):
with self.assertRaisesRegex(AssertionError, 'not a Django model'):
utils.model_from_definition(dict)

def test_pass_string_as_model_definition(self):
Expand Down
2 changes: 0 additions & 2 deletions tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from django.http import QueryDict
from django.test import override_settings, RequestFactory, TestCase
from mock import patch
Expand Down
28 changes: 14 additions & 14 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
[tox]
envlist =
py37-{flake8,codecov,docs},
{py27,py36}-django111-drf{37,38,39},
{py36,py37}-django{21,22}-drf{37,38,39}
py38-{flake8,codecov,docs},
{py36,py37,py38}-django{21,22}-drf{38,39,310}
{py36,py37,py38}-django{30}-drf{310}

[testenv]
passenv = CI TRAVIS TRAVIS_*
setenv =
PYTHONDONTWRITEBYTECODE=1
commands = ./runtests.py --fast {posargs} --coverage
deps =
django111: Django==1.11.8
django21: Django==2.1.8
django22: Django==2.2.0
drf37: djangorestframework==3.7.7
django21: Django==2.1.15
django22: Django==2.2.8
django30: Django==3.0.0
drf38: djangorestframework==3.8.2
drf39: djangorestframework==3.9.2
django-test-plus==1.1.1
drf39: djangorestframework==3.9.4
drf310: djangorestframework==3.10.3
django-test-plus==1.4.0
pytest==4.4.1
pytest-django==3.4.8
pytest-cov==2.6.1
hashids==1.1.0
mock==2.0.0

[testenv:py37-flake8]
[testenv:py38-flake8]
commands = ./runtests.py --lintonly
deps =
pytest==3.0.5
flake8==3.7.7

[testenv:py37-codecov]
[testenv:py38-codecov]
commands =
{[testenv]commands} # Run the tests and generate coverage report
codecov -e TOX_ENV # Post coverage stats to codecov.io
deps =
{[testenv]deps}
Django==2.2.0
djangorestframework==3.9.2
Django==3.0.0
djangorestframework==3.10.3
codecov==2.0.15

[testenv:py37-docs]
[testenv:py38-docs]
commands = mkdocs build
deps =
mkdocs>=0.16.1

0 comments on commit f1883e4

Please sign in to comment.