diff --git a/.travis.yml b/.travis.yml index e2fd9a3..9bc8f89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ dist: xenial language: python cache: pip python: -- 2.7 - 3.7 - 3.6 install: pip install tox-travis codecov diff --git a/CHANGES.rst b/CHANGES.rst index b97922b..fa85438 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,7 @@ CHANGES ------------------ - Remove hacks for previously supported Django versions. (Fixes GH-390) +- Dropped support for Python 2.7. (Fixes GH-393) 3.3.0 (2019.08.19) ------------------ diff --git a/docs/setup.rst b/docs/setup.rst index 1fca10c..832f656 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -18,6 +18,6 @@ Dependencies ============ ``django-model-utils`` supports `Django`_ 1.8 through 2.1 (latest bugfix -release in each series only) on Python 2.7, 3.4, 3.5 and 3.6. +release in each series only) on Python 3.4, 3.5 and 3.6. .. _Django: http://www.djangoproject.com/ diff --git a/model_utils/choices.py b/model_utils/choices.py index 31d5aa1..2219a25 100644 --- a/model_utils/choices.py +++ b/model_utils/choices.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import copy -class Choices(object): +class Choices: """ A class to encapsulate handy functionality for lists of choices for a Django model field. diff --git a/model_utils/fields.py b/model_utils/fields.py index 693242d..7e79855 100644 --- a/model_utils/fields.py +++ b/model_utils/fields.py @@ -5,7 +5,6 @@ import uuid from django.db import models from django.conf import settings from django.core.exceptions import ValidationError -from django.utils.encoding import python_2_unicode_compatible from django.utils.timezone import now DEFAULT_CHOICES_NAME = 'STATUS' @@ -178,8 +177,7 @@ def get_excerpt(content): return '\n'.join(default_excerpt) -@python_2_unicode_compatible -class SplitText(object): +class SplitText: def __init__(self, instance, field_name, excerpt_field_name): # instead of storing actual values store a reference to the instance # along with field names, this makes assignment possible @@ -210,7 +208,7 @@ class SplitText(object): return self.content -class SplitDescriptor(object): +class SplitDescriptor: def __init__(self, field): self.field = field self.excerpt_field_name = _excerpt_field_name(self.field.name) diff --git a/model_utils/managers.py b/model_utils/managers.py index 9f34bdd..22985a9 100644 --- a/model_utils/managers.py +++ b/model_utils/managers.py @@ -44,7 +44,7 @@ class InheritanceIterable(ModelIterable): yield obj -class InheritanceQuerySetMixin(object): +class InheritanceQuerySetMixin: def __init__(self, *args, **kwargs): super(InheritanceQuerySetMixin, self).__init__(*args, **kwargs) self._iterable_class = InheritanceIterable @@ -216,7 +216,8 @@ class InheritanceQuerySet(InheritanceQuerySetMixin, QuerySet): return self.select_subclasses(*models).extra(where=[' OR '.join(where_queries)]) -class InheritanceManagerMixin(object): + +class InheritanceManagerMixin: _queryset_class = InheritanceQuerySet def get_queryset(self): @@ -235,7 +236,7 @@ class InheritanceManager(InheritanceManagerMixin, models.Manager): pass -class QueryManagerMixin(object): +class QueryManagerMixin: def __init__(self, *args, **kwargs): if args: @@ -260,7 +261,7 @@ class QueryManager(QueryManagerMixin, models.Manager): pass -class SoftDeletableQuerySetMixin(object): +class SoftDeletableQuerySetMixin: """ QuerySet for SoftDeletableModel. Instead of removing instance sets its ``is_removed`` field to True. @@ -278,7 +279,7 @@ class SoftDeletableQuerySet(SoftDeletableQuerySetMixin, QuerySet): pass -class SoftDeletableManagerMixin(object): +class SoftDeletableManagerMixin: """ Manager that limits the queryset by default to show only not removed instances of model. @@ -393,7 +394,7 @@ class JoinQueryset(models.QuerySet): return new_qs -class JoinManagerMixin(object): +class JoinManagerMixin: """ Manager that adds a method join. This method allows you to join two querysets together. diff --git a/model_utils/tracker.py b/model_utils/tracker.py index fa93a74..4a806f4 100644 --- a/model_utils/tracker.py +++ b/model_utils/tracker.py @@ -9,7 +9,7 @@ from django.db.models.fields.files import FileDescriptor from django.db.models.query_utils import DeferredAttribute -class DescriptorMixin(object): +class DescriptorMixin: tracker_instance = None def __get__(self, instance, owner): @@ -29,7 +29,7 @@ class DescriptorMixin(object): return self.field_name -class DescriptorWrapper(object): +class DescriptorWrapper: def __init__(self, field_name, descriptor, tracker_attname): self.field_name = field_name @@ -88,7 +88,7 @@ class FullDescriptorWrapper(DescriptorWrapper): self.descriptor.__delete__(obj) -class FieldInstanceTracker(object): +class FieldInstanceTracker: def __init__(self, instance, fields, field_map): self.instance = instance self.fields = fields @@ -190,7 +190,7 @@ class FieldInstanceTracker(object): setattr(self.instance.__class__, field, field_tracker) -class FieldTracker(object): +class FieldTracker: tracker_class = FieldInstanceTracker diff --git a/setup.py b/setup.py index 5b61beb..9c27fe8 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,6 @@ setup( 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.6', diff --git a/tests/models.py b/tests/models.py index 9c5e374..c3c85db 100644 --- a/tests/models.py +++ b/tests/models.py @@ -4,7 +4,6 @@ import django from django.db import models from django.db.models.query_utils import DeferredAttribute from django.db.models import Manager -from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from model_utils import Choices @@ -36,7 +35,6 @@ class InheritanceManagerTestRelated(models.Model): pass -@python_2_unicode_compatible class InheritanceManagerTestParent(models.Model): # FileField is just a handy descriptor-using field. Refs #6. non_related_field_using_descriptor = models.FileField(upload_to="test") @@ -206,12 +204,12 @@ class NoRendered(models.Model): body = SplitField(no_excerpt_field=True) -class AuthorMixin(object): +class AuthorMixin: def by_author(self, name): return self.filter(author=name) -class PublishedMixin(object): +class PublishedMixin: def published(self): return self.filter(published=True) @@ -373,7 +371,7 @@ class CustomSoftDelete(SoftDeletableModel): objects = CustomSoftDeleteManager() -class StringyDescriptor(object): +class StringyDescriptor: """ Descriptor that returns a string version of the underlying integer value. """ diff --git a/tests/test_fields/test_field_tracker.py b/tests/test_fields/test_field_tracker.py index 40cf1ab..b877bf2 100644 --- a/tests/test_fields/test_field_tracker.py +++ b/tests/test_fields/test_field_tracker.py @@ -45,7 +45,7 @@ class FieldTrackerTestCase(TestCase): self.instance.save() -class FieldTrackerCommonTests(object): +class FieldTrackerCommonTests: def test_pre_save_previous(self): self.assertPrevious(name=None, number=None) diff --git a/tox.ini b/tox.ini index ca2f2e3..21f13dc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,5 @@ [tox] envlist = - py27-django{111} py37-django{202,201} py36-django{111,202,201,trunk} flake8