Fixed #27857 -- Dropped support for Python 3.4.

This commit is contained in:
Tim Graham 2017-02-17 19:45:34 -05:00
parent a80903b711
commit cfff2af02b
20 changed files with 37 additions and 116 deletions

View File

@ -1,6 +1,6 @@
Thanks for downloading Django.
To install it, make sure you have Python 3.4 or greater installed. Then run
To install it, make sure you have Python 3.5 or greater installed. Then run
this command from the command prompt:
python setup.py install

View File

@ -150,12 +150,6 @@ class BaseExpression:
if output_field is not None:
self.output_field = output_field
def __getstate__(self):
# This method required only for Python 3.4.
state = self.__dict__.copy()
state.pop('convert_value', None)
return state
def get_db_converters(self, connection):
return (
[]

View File

@ -1,20 +1,7 @@
import sys
from http import cookies
# Cookie pickling bug is fixed in Python 3.4.3+
# http://bugs.python.org/issue22775
if sys.version_info >= (3, 4, 3):
# For backwards compatibility in Django 2.1.
SimpleCookie = cookies.SimpleCookie
else:
Morsel = cookies.Morsel
class SimpleCookie(cookies.SimpleCookie):
def __setitem__(self, key, value):
if isinstance(value, Morsel):
# allow assignment of constructed Morsels (e.g. for pickling)
dict.__setitem__(self, key, value)
else:
super().__setitem__(key, value)
def parse_cookie(cookie):

View File

@ -1,8 +1,7 @@
"""Compare two HTML documents."""
import re
from django.utils.html_parser import HTMLParseError, HTMLParser
from html.parser import HTMLParser
WHITESPACE = re.compile(r'\s+')
@ -138,6 +137,10 @@ class RootElement(Element):
return ''.join(str(c) for c in self.children)
class HTMLParseError(Exception):
pass
class Parser(HTMLParser):
SELF_CLOSING_TAGS = (
'br', 'hr', 'input', 'img', 'meta', 'spacer', 'link', 'frame', 'base',
@ -145,7 +148,7 @@ class Parser(HTMLParser):
)
def __init__(self):
HTMLParser.__init__(self)
HTMLParser.__init__(self, convert_charrefs=False)
self.root = RootElement()
self.open_tags = []
self.element_positions = {}

View File

@ -60,10 +60,7 @@ def register_converter(converter, type_name):
@lru_cache.lru_cache(maxsize=None)
def get_converters():
converters = {}
converters.update(DEFAULT_CONVERTERS)
converters.update(REGISTERED_CONVERTERS)
return converters
return {**DEFAULT_CONVERTERS, **REGISTERED_CONVERTERS}
def get_converter(raw_converter):

View File

@ -343,9 +343,7 @@ class URLPattern:
'path.to.ClassBasedView').
"""
callback = self.callback
# Python 3.5 collapses nested partials, so can change "while" to "if"
# when it's the minimum supported version.
while isinstance(callback, functools.partial):
if isinstance(callback, functools.partial):
callback = callback.func
if not hasattr(callback, '__name__'):
return callback.__module__ + "." + callback.__class__.__name__

View File

@ -1,6 +1,7 @@
"""HTML utilities suitable for global use."""
import re
from html.parser import HTMLParser
from urllib.parse import (
parse_qsl, quote, unquote, urlencode, urlsplit, urlunsplit,
)
@ -11,8 +12,6 @@ from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
from django.utils.safestring import SafeData, SafeText, mark_safe
from django.utils.text import normalize_newlines
from .html_parser import HTMLParseError, HTMLParser
# Configuration for urlize() function.
TRAILING_PUNCTUATION_RE = re.compile(
'^' # Beginning of word
@ -132,7 +131,7 @@ def linebreaks(value, autoescape=False):
class MLStripper(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
HTMLParser.__init__(self, convert_charrefs=False)
self.reset()
self.fed = []
@ -154,15 +153,8 @@ def _strip_once(value):
Internal tag stripping utility used by strip_tags.
"""
s = MLStripper()
try:
s.feed(value)
except HTMLParseError:
return value
try:
s.close()
except HTMLParseError:
return s.get_data() + s.rawdata
else:
return s.get_data()

View File

@ -1,17 +0,0 @@
import html.parser
try:
HTMLParseError = html.parser.HTMLParseError
except AttributeError:
# create a dummy class for Python 3.5+ where it's been removed
class HTMLParseError(Exception):
pass
class HTMLParser(html.parser.HTMLParser):
"""Explicitly set convert_charrefs to be False.
This silences a deprecation warning on Python 3.4.
"""
def __init__(self, convert_charrefs=False, **kwargs):
html.parser.HTMLParser.__init__(self, convert_charrefs=convert_charrefs, **kwargs)

View File

@ -288,9 +288,9 @@ Once the tests complete, you should be greeted with a message informing you
whether the test suite passed or failed. Since you haven't yet made any changes
to Django's code, the entire test suite **should** pass. If you get failures or
errors make sure you've followed all of the previous steps properly. See
:ref:`running-unit-tests` for more information. If you're using Python 3.5+,
there will be a couple failures related to deprecation warnings that you can
ignore. These failures have since been fixed in Django.
:ref:`running-unit-tests` for more information. There will be a couple failures
related to deprecation warnings that you can ignore. These failures have since
been fixed in Django.
Note that the latest Django trunk may not always be stable. When developing
against trunk, you can check `Django's continuous integration builds`__ to

View File

@ -29,7 +29,7 @@ your operating system's package manager.
You can verify that Python is installed by typing ``python`` from your shell;
you should see something like::
Python 3.4.x
Python 3.x.y
[GCC 4.x] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

View File

@ -23,7 +23,7 @@ in a shell prompt (indicated by the $ prefix):
If Django is installed, you should see the version of your installation. If it
isn't, you'll get an error telling "No module named django".
This tutorial is written for Django |version| and Python 3.4 or later. If the
This tutorial is written for Django |version| and Python 3.5 or later. If the
Django version doesn't match, you can refer to the tutorial for your version
of Django by using the version switcher at the bottom right corner of this
page, or update Django to the newest version. If you are still using Python

View File

@ -192,7 +192,7 @@ Configurable attributes
.. attribute:: AppConfig.path
Filesystem path to the application directory, e.g.
``'/usr/lib/python3.4/dist-packages/django/contrib/admin'``.
``'/usr/lib/pythonX.Y/dist-packages/django/contrib/admin'``.
In most cases, Django can automatically detect and set this, but you can
also provide an explicit override as a class attribute on your

View File

@ -62,7 +62,6 @@ setup(
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Internet :: WWW/HTTP',

View File

@ -1,5 +1,3 @@
import unittest
from django.core.exceptions import ImproperlyConfigured
from django.core.handlers.wsgi import WSGIHandler, WSGIRequest, get_script_name
from django.core.signals import request_finished, request_started
@ -8,11 +6,6 @@ from django.test import (
RequestFactory, SimpleTestCase, TransactionTestCase, override_settings,
)
try:
from http import HTTPStatus
except ImportError: # Python < 3.5
HTTPStatus = None
class HandlerTests(SimpleTestCase):
@ -182,7 +175,6 @@ class HandlerRequestTests(SimpleTestCase):
environ = RequestFactory().get('/%E2%A8%87%87%A5%E2%A8%A0').environ
self.assertIsInstance(environ['PATH_INFO'], str)
@unittest.skipIf(HTTPStatus is None, 'HTTPStatus only exists on Python 3.5+')
def test_handle_accepts_httpstatus_enum_value(self):
def start_response(status, headers):
start_response.status = status

View File

@ -1,13 +1,10 @@
from http import HTTPStatus
from django.core.exceptions import SuspiciousOperation
from django.db import connection, transaction
from django.http import HttpResponse, StreamingHttpResponse
from django.views.decorators.csrf import csrf_exempt
try:
from http import HTTPStatus
except ImportError: # Python < 3.5
pass
def regular(request):
return HttpResponse(b"regular content")

View File

@ -61,10 +61,7 @@ class MailTests(HeadersCheckMixin, SimpleTestCase):
def iter_attachments():
for i in email_message.walk():
# Once support for Python<3.5 has been dropped, we can use
# i.get_content_disposition() here instead.
content_disposition = i.get('content-disposition', '').split(';')[0].lower()
if content_disposition == 'attachment':
if i.get_content_disposition() == 'attachment':
filename = i.get_filename()
content = i.get_payload(decode=True)
mimetype = i.get_content_type()
@ -1161,7 +1158,7 @@ class FakeSMTPServer(smtpd.SMTPServer, threading.Thread):
def __init__(self, *args, **kwargs):
threading.Thread.__init__(self)
# New kwarg added in Python 3.5; default switching to False in 3.6.
if sys.version_info >= (3, 5):
# Setting a value only silences a deprecation warning in Python 3.5.
kwargs['decode_data'] = True
smtpd.SMTPServer.__init__(self, *args, **kwargs)
self._sink = []

View File

@ -5,7 +5,7 @@ import errno
import os
import socket
import sys
from http.client import HTTPConnection
from http.client import HTTPConnection, RemoteDisconnected
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import urlopen
@ -14,11 +14,6 @@ from django.test import LiveServerTestCase, override_settings
from .models import Person
try:
from http.client import RemoteDisconnected
except ImportError: # Python 3.4
from http.client import BadStatusLine as RemoteDisconnected
TEST_ROOT = os.path.dirname(__file__)
TEST_SETTINGS = {
'MEDIA_URL': '/media/',

View File

@ -2,7 +2,6 @@ import base64
import os
import shutil
import string
import sys
import tempfile
import unittest
from datetime import timedelta
@ -733,10 +732,9 @@ class SessionMiddlewareTests(TestCase):
# A deleted cookie header looks like:
# Set-Cookie: sessionid=; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/
self.assertEqual(
'Set-Cookie: {}={}; expires=Thu, 01-Jan-1970 00:00:00 GMT; '
'Set-Cookie: {}=""; expires=Thu, 01-Jan-1970 00:00:00 GMT; '
'Max-Age=0; Path=/'.format(
settings.SESSION_COOKIE_NAME,
'""' if sys.version_info >= (3, 5) else '',
),
str(response.cookies[settings.SESSION_COOKIE_NAME])
)
@ -763,10 +761,9 @@ class SessionMiddlewareTests(TestCase):
# expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0;
# Path=/example/
self.assertEqual(
'Set-Cookie: {}={}; Domain=.example.local; expires=Thu, '
'Set-Cookie: {}=""; Domain=.example.local; expires=Thu, '
'01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/example/'.format(
settings.SESSION_COOKIE_NAME,
'""' if sys.version_info >= (3, 5) else '',
),
str(response.cookies[settings.SESSION_COOKIE_NAME])
)

View File

@ -1,4 +1,3 @@
import sys
import unittest
from io import StringIO
@ -94,18 +93,13 @@ class TestDebugSQL(unittest.TestCase):
]
verbose_expected_outputs = [
# Output format changed in Python 3.5+
x.format('' if sys.version_info < (3, 5) else 'TestDebugSQL.') for x in [
'runTest (test_runner.test_debug_sql.{}FailingTest) ... FAIL',
'runTest (test_runner.test_debug_sql.{}ErrorTest) ... ERROR',
'runTest (test_runner.test_debug_sql.{}PassingTest) ... ok',
'runTest (test_runner.test_debug_sql.{}PassingSubTest) ... ok',
'runTest (test_runner.test_debug_sql.TestDebugSQL.FailingTest) ... FAIL',
'runTest (test_runner.test_debug_sql.TestDebugSQL.ErrorTest) ... ERROR',
'runTest (test_runner.test_debug_sql.TestDebugSQL.PassingTest) ... ok',
# If there are errors/failures in subtests but not in test itself,
# the status is not written. That behavior comes from Python.
'runTest (test_runner.test_debug_sql.{}FailingSubTest) ...',
'runTest (test_runner.test_debug_sql.{}ErrorSubTest) ...',
]
] + [
'runTest (test_runner.test_debug_sql.TestDebugSQL.FailingSubTest) ...',
'runTest (test_runner.test_debug_sql.TestDebugSQL.ErrorSubTest) ...',
('''SELECT COUNT(*) AS "__count" '''
'''FROM "test_runner_person" WHERE '''
'''"test_runner_person"."first_name" = 'pass';'''),

View File

@ -1,5 +1,4 @@
import os
import sys
import unittest
from io import StringIO
from unittest import mock
@ -684,9 +683,6 @@ class HTMLEqualTests(SimpleTestCase):
error_msg = (
"First argument is not valid HTML:\n"
"('Unexpected end tag `div` (Line 1, Column 6)', (1, 6))"
) if sys.version_info >= (3, 5) else (
"First argument is not valid HTML:\n"
"Unexpected end tag `div` (Line 1, Column 6), at line 1, column 7"
)
with self.assertRaisesMessage(AssertionError, error_msg):
self.assertHTMLEqual('< div></ div>', '<div></div>')