Browse Source

Merge pull request #3934 from gasman/cleanup/remove-python2

Remove Python 2 fallback code
Karl Hobley 7 năm trước cách đây
mục cha
commit
b350672585

+ 1 - 4
appveyor.yml

@@ -3,10 +3,7 @@ services:
 
 environment:
   matrix:
-    - TOXENV: py27-dj18-sqlite-noelasticsearch
-    - TOXENV: py27-dj110-sqlite-noelasticsearch
-    - TOXENV: py35-dj18-sqlite-noelasticsearch
-    - TOXENV: py35-dj110-sqlite-noelasticsearch
+    - TOXENV: py35-dj111-sqlite-noelasticsearch
 
 matrix:
   fast_finish: true

+ 1 - 4
docs/contributing/developing.rst

@@ -194,10 +194,7 @@ To start this simple server, run the following commands:
 .. code-block:: console
 
     $ cd docs/_build/html/
-    $ # Python 2
-    $ python2 -mSimpleHTTPServer 8080
-    $ # Python 3
-    $ python3 -mhttp.server 8080
+    $ python -mhttp.server 8080
 
 Now you can open <http://localhost:8080/> in your web browser to see the compiled documentation.
 

+ 2 - 2
docs/reference/contrib/modeladmin/indexview.rst

@@ -91,7 +91,7 @@ You have three possible values that can be used in list_display:
 A few special cases to note about ``list_display``:
 
 -   If the field is a ``ForeignKey``, Django will display the output of
-    ``__str__()`` (``__unicode__()`` on Python 2) of the related object.
+    ``__str__()`` of the related object.
 
 -   If the string provided is a method of the model or ``ModelAdmin`` class,
     Django will HTML-escape the output by default. To escape user input and
@@ -163,7 +163,7 @@ A few special cases to note about ``list_display``:
                 return super(self, PersonAdmin).get_empty_value_display(field_name)
 
 
-    The ``__str__()`` (``__unicode__()`` on Python 2) method is just as valid
+    The ``__str__()`` method is just as valid
     in ``list_display`` as any other model method, so it’s perfectly OK to do
     this:
 

+ 0 - 2
docs/topics/snippets.rst

@@ -16,7 +16,6 @@ Here's an example snippet model:
 .. code-block:: python
 
   from django.db import models
-  from django.utils.encoding import python_2_unicode_compatible
 
   from wagtail.wagtailadmin.edit_handlers import FieldPanel
   from wagtail.wagtailsnippets.models import register_snippet
@@ -24,7 +23,6 @@ Here's an example snippet model:
   ...
 
   @register_snippet
-  @python_2_unicode_compatible  # provide equivalent __unicode__ and __str__ methods on Python 2
   class Advert(models.Model):
       url = models.URLField(null=True, blank=True)
       text = models.CharField(max_length=255)

+ 1 - 2
wagtail/contrib/postgres_search/backend.py

@@ -7,7 +7,7 @@ from django.db import DEFAULT_DB_ALIAS, NotSupportedError, connections, transact
 from django.db.models import F, Manager, TextField, Value
 from django.db.models.constants import LOOKUP_SEP
 from django.db.models.functions import Cast
-from django.utils.encoding import force_text, python_2_unicode_compatible
+from django.utils.encoding import force_text
 from django.utils.six import string_types
 
 from wagtail.wagtailsearch.backends.base import (
@@ -23,7 +23,6 @@ from .utils import (
 # TODO: Add autocomplete.
 
 
-@python_2_unicode_compatible
 class Index(object):
     def __init__(self, backend, model, db_alias=None):
         self.backend = backend

+ 0 - 2
wagtail/contrib/postgres_search/models.py

@@ -7,7 +7,6 @@ from django.contrib.postgres.indexes import GinIndex
 from django.contrib.postgres.search import SearchVectorField
 from django.db.models import CASCADE, ForeignKey, Model, TextField
 from django.db.models.functions import Cast
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 
 from ...wagtailsearch.index import class_is_indexed
@@ -40,7 +39,6 @@ class TextIDGenericRelation(GenericRelation):
         return []
 
 
-@python_2_unicode_compatible
 class IndexEntry(Model):
     content_type = ForeignKey(ContentType, on_delete=CASCADE)
     # We do not use an IntegerField since primary keys are not always integers.

+ 0 - 4
wagtail/contrib/settings/context_processors.py

@@ -1,11 +1,8 @@
 from __future__ import absolute_import, unicode_literals
 
-from django.utils.encoding import python_2_unicode_compatible
-
 from .registry import registry
 
 
-@python_2_unicode_compatible
 class SettingsProxy(dict):
     """
     Get a SettingModuleProxy for an app using proxy['app_label']
@@ -21,7 +18,6 @@ class SettingsProxy(dict):
         return 'SettingsProxy'
 
 
-@python_2_unicode_compatible
 class SettingModuleProxy(dict):
     """
     Get a setting instance using proxy['modelname']

+ 0 - 5
wagtail/tests/modeladmintest/models.py

@@ -1,13 +1,11 @@
 from __future__ import absolute_import, unicode_literals
 
 from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
 
 from wagtail.wagtailcore.models import Page
 from wagtail.wagtailsearch import index
 
 
-@python_2_unicode_compatible
 class Author(models.Model):
     name = models.CharField(max_length=255)
     date_of_birth = models.DateField()
@@ -23,7 +21,6 @@ class Author(models.Model):
         return ''
 
 
-@python_2_unicode_compatible
 class Book(models.Model, index.Indexed):
     author = models.ForeignKey(Author, on_delete=models.PROTECT)
     title = models.CharField(max_length=255)
@@ -33,7 +30,6 @@ class Book(models.Model, index.Indexed):
         return self.title
 
 
-@python_2_unicode_compatible
 class Token(models.Model):
     key = models.CharField(max_length=40, primary_key=True)
 
@@ -41,7 +37,6 @@ class Token(models.Model):
         return self.key
 
 
-@python_2_unicode_compatible
 class Publisher(models.Model):
     name = models.CharField(max_length=50)
     headquartered_in = models.CharField(max_length=50, null=True, blank=True)

+ 0 - 3
wagtail/tests/snippets/models.py

@@ -1,7 +1,6 @@
 from __future__ import absolute_import, unicode_literals
 
 from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
 from modelcluster.fields import ParentalKey
 from modelcluster.models import ClusterableModel
 
@@ -19,7 +18,6 @@ from .forms import FancySnippetForm
 # to ensure specific [in]correct register ordering
 
 # AlphaSnippet is registered during TestSnippetOrdering
-@python_2_unicode_compatible
 class AlphaSnippet(models.Model):
     text = models.CharField(max_length=255)
 
@@ -28,7 +26,6 @@ class AlphaSnippet(models.Model):
 
 
 # ZuluSnippet is registered during TestSnippetOrdering
-@python_2_unicode_compatible
 class ZuluSnippet(models.Model):
     text = models.CharField(max_length=255)
 

+ 0 - 4
wagtail/tests/testapp/models.py

@@ -12,7 +12,6 @@ from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
 from django.core.serializers.json import DjangoJSONEncoder
 from django.db import models
 from django.shortcuts import render
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.six import text_type
 from modelcluster.contrib.taggit import ClusterTaggableManager
 from modelcluster.fields import ParentalKey, ParentalManyToManyField
@@ -199,7 +198,6 @@ class EventPageSpeaker(Orderable, LinkFields):
     ]
 
 
-@python_2_unicode_compatible
 class EventCategory(models.Model):
     name = models.CharField("Name", max_length=255)
 
@@ -519,7 +517,6 @@ class AdvertTag(TaggedItemBase):
     content_object = ParentalKey('Advert', related_name='tagged_items', on_delete=models.CASCADE)
 
 
-@python_2_unicode_compatible
 class Advert(ClusterableModel):
     url = models.URLField(null=True, blank=True)
     text = models.CharField(max_length=255)
@@ -539,7 +536,6 @@ class Advert(ClusterableModel):
 register_snippet(Advert)
 
 
-@python_2_unicode_compatible
 class AdvertWithTabbedInterface(models.Model):
     url = models.URLField(null=True, blank=True)
     text = models.CharField(max_length=255)

+ 0 - 2
wagtail/wagtailadmin/widgets.py

@@ -9,7 +9,6 @@ from django.core.urlresolvers import reverse
 from django.forms import widgets
 from django.forms.utils import flatatt
 from django.template.loader import render_to_string
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.formats import get_format
 from django.utils.functional import cached_property
 from django.utils.html import format_html
@@ -217,7 +216,6 @@ class AdminPageChooser(AdminChooser):
         )
 
 
-@python_2_unicode_compatible
 @total_ordering
 class Button(object):
     show = True

+ 1 - 8
wagtail/wagtailcore/blocks/base.py

@@ -8,7 +8,7 @@ from django.core import checks
 from django.core.exceptions import ImproperlyConfigured
 from django.template.loader import render_to_string
 from django.utils import six
-from django.utils.encoding import force_text, python_2_unicode_compatible
+from django.utils.encoding import force_text
 from django.utils.safestring import mark_safe
 from django.utils.text import capfirst
 
@@ -404,14 +404,7 @@ class Block(six.with_metaclass(BaseBlock, object)):
     def __ne__(self, other):
         return not self.__eq__(other)
 
-    # Making block instances hashable in a way that's consistent with __eq__ is non-trivial, because
-    # self.deconstruct() is liable to contain unhashable data (e.g. lists and dicts). So let's set
-    # Block to be explicitly unhashable - Python 3 will do this automatically when defining __eq__,
-    # but Python 2 won't, and we'd like the behaviour to be consistent on both.
-    __hash__ = None
 
-
-@python_2_unicode_compatible
 class BoundBlock(object):
     def __init__(self, block, value, prefix=None, errors=None):
         self.block = block

+ 0 - 2
wagtail/wagtailcore/blocks/stream_block.py

@@ -10,7 +10,6 @@ from django.forms.utils import ErrorList
 from django.template.loader import render_to_string
 # Must be imported from Django so we get the new implementation of with_metaclass
 from django.utils import six
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.html import format_html_join
 from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext as _
@@ -326,7 +325,6 @@ class StreamBlock(six.with_metaclass(DeclarativeSubBlocksMetaclass, BaseStreamBl
     pass
 
 
-@python_2_unicode_compatible  # provide equivalent __unicode__ and __str__ methods on Py2
 class StreamValue(collections.Sequence):
     """
     Custom type used to represent the value of a StreamBlock; behaves as a sequence of BoundBlocks

+ 0 - 19
wagtail/wagtailcore/blocks/utils.py

@@ -1,8 +1,6 @@
 from __future__ import absolute_import, unicode_literals
 
-import inspect
 import re
-import sys
 
 # helpers for Javascript expression formatting
 
@@ -24,20 +22,3 @@ def js_dict(d):
         for (k, v) in d.items()
     ]
     return "{\n%s\n}" % ',\n'.join(dict_items)
-
-
-def accepts_kwarg(func, kwarg):
-    """
-    Determine whether the callable `func` has a signature that accepts the keyword argument `kwarg`
-    """
-    if sys.version_info >= (3, 3):
-        signature = inspect.signature(func)
-        try:
-            signature.bind_partial(**{kwarg: None})
-            return True
-        except TypeError:
-            return False
-    else:
-        # Fall back on inspect.getargspec, available on Python 2.7 but deprecated since 3.5
-        argspec = inspect.getargspec(func)
-        return (kwarg in argspec.args) or (argspec.keywords is not None)

+ 0 - 7
wagtail/wagtailcore/models.py

@@ -20,7 +20,6 @@ from django.http import Http404
 from django.template.response import TemplateResponse
 # Must be imported from Django so we get the new implementation of with_metaclass
 from django.utils import six, timezone
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.functional import cached_property
 from django.utils.six import StringIO
 from django.utils.six.moves.urllib.parse import urlparse
@@ -47,7 +46,6 @@ class SiteManager(models.Manager):
         return self.get(hostname=hostname, port=port)
 
 
-@python_2_unicode_compatible
 class Site(models.Model):
     hostname = models.CharField(verbose_name=_('hostname'), max_length=255, db_index=True)
     port = models.IntegerField(
@@ -237,7 +235,6 @@ class AbstractPage(MP_Node):
         abstract = True
 
 
-@python_2_unicode_compatible
 class Page(six.with_metaclass(PageBase, AbstractPage, index.Indexed, ClusterableModel)):
     title = models.CharField(
         verbose_name=_('title'),
@@ -1390,7 +1387,6 @@ class SubmittedRevisionsManager(models.Manager):
         return super(SubmittedRevisionsManager, self).get_queryset().filter(submitted_for_moderation=True)
 
 
-@python_2_unicode_compatible
 class PageRevision(models.Model):
     page = models.ForeignKey('Page', verbose_name=_('page'), related_name='revisions', on_delete=models.CASCADE)
     submitted_for_moderation = models.BooleanField(
@@ -1543,7 +1539,6 @@ PAGE_PERMISSION_TYPE_CHOICES = [
 ]
 
 
-@python_2_unicode_compatible
 class GroupPagePermission(models.Model):
     group = models.ForeignKey(Group, verbose_name=_('group'), related_name='page_permissions', on_delete=models.CASCADE)
     page = models.ForeignKey('Page', verbose_name=_('page'), related_name='group_permissions', on_delete=models.CASCADE)
@@ -1928,7 +1923,6 @@ class CollectionViewRestriction(BaseViewRestriction):
         verbose_name_plural = _('collection view restrictions')
 
 
-@python_2_unicode_compatible
 class Collection(MP_Node):
     """
     A location in which resources such as images and documents can be grouped
@@ -1988,7 +1982,6 @@ class CollectionMember(models.Model):
         abstract = True
 
 
-@python_2_unicode_compatible
 class GroupCollectionPermission(models.Model):
     """
     A rule indicating that a group has permission for some action (e.g. "create document")

+ 0 - 2
wagtail/wagtailcore/rich_text.py

@@ -2,7 +2,6 @@ from __future__ import absolute_import, unicode_literals
 
 import re  # parsing HTML with regexes LIKE A BOSS.
 
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
 
@@ -182,7 +181,6 @@ def expand_db_html(html, for_editor=False):
     return html
 
 
-@python_2_unicode_compatible
 class RichText(object):
     """
     A custom object used to represent a renderable rich text value.

+ 6 - 12
wagtail/wagtailcore/utils.py

@@ -2,7 +2,6 @@ from __future__ import absolute_import, unicode_literals
 
 import inspect
 import re
-import sys
 import unicodedata
 
 from django.apps import apps
@@ -102,14 +101,9 @@ def accepts_kwarg(func, kwarg):
     """
     Determine whether the callable `func` has a signature that accepts the keyword argument `kwarg`
     """
-    if sys.version_info >= (3, 3):
-        signature = inspect.signature(func)
-        try:
-            signature.bind_partial(**{kwarg: None})
-            return True
-        except TypeError:
-            return False
-    else:
-        # Fall back on inspect.getargspec, available on Python 2.7 but deprecated since 3.5
-        argspec = inspect.getargspec(func)
-        return (kwarg in argspec.args) or (argspec.keywords is not None)
+    signature = inspect.signature(func)
+    try:
+        signature.bind_partial(**{kwarg: None})
+        return True
+    except TypeError:
+        return False

+ 0 - 2
wagtail/wagtaildocs/models.py

@@ -7,7 +7,6 @@ from django.core.exceptions import ImproperlyConfigured
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.dispatch import Signal
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 from taggit.managers import TaggableManager
 
@@ -21,7 +20,6 @@ class DocumentQuerySet(SearchableQuerySetMixin, models.QuerySet):
     pass
 
 
-@python_2_unicode_compatible
 class AbstractDocument(CollectionMember, index.Indexed, models.Model):
     title = models.CharField(max_length=255, verbose_name=_('title'))
     file = models.FileField(upload_to='documents', verbose_name=_('file'))

+ 0 - 2
wagtail/wagtaildocs/tests/test_views.py

@@ -187,8 +187,6 @@ class TestServeWithUnicodeFilename(TestCase):
     def setUp(self):
         self.document = models.Document(title="Test document")
 
-        # Setting this filename in the content-disposition header fails on Django <1.8, Python 2
-        # due to https://code.djangoproject.com/ticket/20889
         self.filename = 'docs\u0627\u0644\u0643\u0627\u062a\u062f\u0631\u0627'
         '\u064a\u064a\u0629_\u0648\u0627\u0644\u0633\u0648\u0642'
         try:

+ 2 - 8
wagtail/wagtaildocs/views/serve.py

@@ -4,10 +4,9 @@ from wsgiref.util import FileWrapper
 
 from django.conf import settings
 from django.core.urlresolvers import reverse
-from django.http import BadHeaderError, Http404, HttpResponse, StreamingHttpResponse
+from django.http import Http404, HttpResponse, StreamingHttpResponse
 from django.shortcuts import get_object_or_404, redirect
 from django.template.response import TemplateResponse
-from unidecode import unidecode
 
 from wagtail.utils import sendfile_streaming_backend
 from wagtail.utils.sendfile import sendfile
@@ -67,12 +66,7 @@ def serve(request, document_id, document_filename):
         wrapper = FileWrapper(doc.file)
         response = StreamingHttpResponse(wrapper, content_type='application/octet-stream')
 
-        try:
-            response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename
-        except BadHeaderError:
-            # Unicode filenames can fail on Django <1.8, Python 2 due to
-            # https://code.djangoproject.com/ticket/20889 - try with an ASCIIfied version of the name
-            response['Content-Disposition'] = 'attachment; filename=%s' % unidecode(doc.filename)
+        response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename
 
         # FIXME: storage backends are not guaranteed to implement 'size'
         response['Content-Length'] = doc.file.size

+ 0 - 3
wagtail/wagtailembeds/blocks.py

@@ -1,12 +1,9 @@
 from __future__ import absolute_import, unicode_literals
 
-from django.utils.encoding import python_2_unicode_compatible
-
 from wagtail.wagtailcore import blocks
 from wagtail.wagtailembeds.format import embed_to_frontend_html
 
 
-@python_2_unicode_compatible
 class EmbedValue(object):
     """
     Native value of an EmbedBlock. Should, at minimum, have a 'url' property

+ 0 - 2
wagtail/wagtailembeds/models.py

@@ -1,7 +1,6 @@
 from __future__ import absolute_import, division, unicode_literals
 
 from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 
 EMBED_TYPES = (
@@ -12,7 +11,6 @@ EMBED_TYPES = (
 )
 
 
-@python_2_unicode_compatible
 class Embed(models.Model):
     """
     When embed code is fetched from a provider (eg, youtube) we cache that code

+ 0 - 2
wagtail/wagtailforms/models.py

@@ -7,7 +7,6 @@ from django.contrib.contenttypes.models import ContentType
 from django.core.serializers.json import DjangoJSONEncoder
 from django.db import models
 from django.shortcuts import render
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.six import text_type
 from django.utils.text import slugify
 from django.utils.translation import ugettext_lazy as _
@@ -37,7 +36,6 @@ FORM_FIELD_CHOICES = (
 )
 
 
-@python_2_unicode_compatible
 class AbstractFormSubmission(models.Model):
     """
     Data for a form submission.

+ 0 - 2
wagtail/wagtailimages/models.py

@@ -11,7 +11,6 @@ from django.core.files import File
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.forms.utils import flatatt
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.functional import cached_property
 from django.utils.safestring import mark_safe
 from django.utils.six import BytesIO, string_types, text_type
@@ -62,7 +61,6 @@ def get_rendition_upload_to(instance, filename):
     return instance.get_upload_to(filename)
 
 
-@python_2_unicode_compatible
 class AbstractImage(CollectionMember, index.Indexed, models.Model):
     title = models.CharField(max_length=255, verbose_name=_('title'))
     file = models.ImageField(

+ 0 - 2
wagtail/wagtailsearch/models.py

@@ -5,13 +5,11 @@ import datetime
 from django.conf import settings
 from django.db import models
 from django.utils import timezone
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 
 from wagtail.wagtailsearch.utils import MAX_QUERY_STRING_LENGTH, normalise_query_string
 
 
-@python_2_unicode_compatible
 class Query(models.Model):
     query_string = models.CharField(max_length=MAX_QUERY_STRING_LENGTH, unique=True)
 

+ 0 - 2
wagtail/wagtailusers/models.py

@@ -2,11 +2,9 @@ from __future__ import absolute_import, unicode_literals
 
 from django.conf import settings
 from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 
 
-@python_2_unicode_compatible
 class UserProfile(models.Model):
     user = models.OneToOneField(
         settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='wagtail_userprofile'