diff --git a/django/apps/config.py b/django/apps/config.py index 76b3014d9d..98070e62c8 100644 --- a/django/apps/config.py +++ b/django/apps/config.py @@ -55,7 +55,7 @@ class AppConfig(object): """Attempt to determine app's filesystem path from its module.""" # See #21874 for extended discussion of the behavior of this method in # various cases. - # Convert paths to list because Python 3.3 _NamespacePath does not + # Convert paths to list because Python 3's _NamespacePath does not # support indexing. paths = list(getattr(module, '__path__', [])) if len(paths) != 1: diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 70a7fda1d4..e8d209c9da 100644 --- a/django/core/mail/message.py +++ b/django/core/mail/message.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import mimetypes import os import random -import sys import time from email import ( charset as Charset, encoders as Encoders, generator, message_from_string, @@ -170,13 +169,7 @@ class SafeMIMEText(MIMEMixin, MIMEText): # We do it manually and trigger re-encoding of the payload. MIMEText.__init__(self, _text, _subtype, None) del self['Content-Transfer-Encoding'] - # Workaround for versions without http://bugs.python.org/issue19063 - if (3, 2) < sys.version_info < (3, 3, 4): - payload = _text.encode(utf8_charset.output_charset) - self._payload = payload.decode('ascii', 'surrogateescape') - self.set_charset(utf8_charset) - else: - self.set_payload(_text, utf8_charset) + self.set_payload(_text, utf8_charset) self.replace_header('Content-Type', 'text/%s; charset="%s"' % (_subtype, _charset)) elif _charset is None: # the default value of '_charset' is 'us-ascii' on Python 2 diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py index ee86469177..5697b8b7eb 100644 --- a/django/db/backends/sqlite3/features.py +++ b/django/db/backends/sqlite3/features.py @@ -1,9 +1,8 @@ from __future__ import unicode_literals -import sys - from django.db import utils from django.db.backends.base.features import BaseDatabaseFeatures +from django.utils import six from django.utils.functional import cached_property from .base import Database @@ -50,7 +49,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def can_share_in_memory_db(self): return ( - sys.version_info[:2] >= (3, 4) and + six.PY3 and Database.__name__ == 'sqlite3.dbapi2' and Database.sqlite_version_info >= (3, 7, 13) ) diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index b2d774bda6..73213afe94 100644 --- a/django/dispatch/dispatcher.py +++ b/django/dispatch/dispatcher.py @@ -3,11 +3,12 @@ import threading import warnings import weakref +from django.utils import six from django.utils.deprecation import RemovedInDjango21Warning from django.utils.inspect import func_accepts_kwargs from django.utils.six.moves import range -if sys.version_info < (3, 4): +if six.PY2: from .weakref_backports import WeakMethod else: from weakref import WeakMethod @@ -108,7 +109,7 @@ class Signal(object): if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'): ref = WeakMethod receiver_object = receiver.__self__ - if sys.version_info >= (3, 4): + if six.PY3: receiver = ref(receiver) weakref.finalize(receiver_object, self._remove_receiver) else: diff --git a/django/utils/functional.py b/django/utils/functional.py index bade67de46..c0d19093fc 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -251,7 +251,7 @@ class LazyObject(object): self._setup() return self._wrapped.__dict__ - # Python 3.3 will call __reduce__ when pickling; this method is needed + # Python 3 will call __reduce__ when pickling; this method is needed # to serialize and deserialize correctly. @classmethod def __newobj__(cls, *args): diff --git a/django/utils/glob.py b/django/utils/glob.py index a3138b09dc..d3f9e08138 100644 --- a/django/utils/glob.py +++ b/django/utils/glob.py @@ -7,30 +7,15 @@ from django.utils import six # backport of Python 3.4's glob.escape -try: +if six.PY3: from glob import escape as glob_escape -except ImportError: +else: _magic_check = re.compile('([*?[])') - if six.PY3: - _magic_check_bytes = re.compile(b'([*?[])') - - def glob_escape(pathname): - """ - Escape all special characters. - """ - drive, pathname = os.path.splitdrive(pathname) - if isinstance(pathname, bytes): - pathname = _magic_check_bytes.sub(br'[\1]', pathname) - else: - pathname = _magic_check.sub(r'[\1]', pathname) - return drive + pathname - - else: - def glob_escape(pathname): - """ - Escape all special characters. - """ - drive, pathname = os.path.splitdrive(pathname) - pathname = _magic_check.sub(r'[\1]', pathname) - return drive + pathname + def glob_escape(pathname): + """ + Escape all special characters. + """ + drive, pathname = os.path.splitdrive(pathname) + pathname = _magic_check.sub(r'[\1]', pathname) + return drive + pathname diff --git a/django/utils/html_parser.py b/django/utils/html_parser.py index e7f7c11571..a69f914921 100644 --- a/django/utils/html_parser.py +++ b/django/utils/html_parser.py @@ -1,6 +1,7 @@ import re import sys +from django.utils import six from django.utils.six.moves import html_parser as _html_parser current_version = sys.version_info @@ -15,7 +16,7 @@ except AttributeError: pass if not use_workaround: - if current_version >= (3, 4): + if six.PY3: class HTMLParser(_html_parser.HTMLParser): """Explicitly set convert_charrefs to be False. diff --git a/django/utils/module_loading.py b/django/utils/module_loading.py index c42d208178..4a2c500321 100644 --- a/django/utils/module_loading.py +++ b/django/utils/module_loading.py @@ -63,11 +63,8 @@ def autodiscover_modules(*args, **kwargs): raise -if sys.version_info[:2] >= (3, 3): - if sys.version_info[:2] >= (3, 4): - from importlib.util import find_spec as importlib_find - else: - from importlib import find_loader as importlib_find +if six.PY3: + from importlib.util import find_spec as importlib_find def module_has_submodule(package, module_name): """See if 'module' is in 'package'.""" diff --git a/docs/faq/install.txt b/docs/faq/install.txt index 05906b2a18..38b01a4d6b 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -16,9 +16,9 @@ How do I get started? What are Django's prerequisites? -------------------------------- -Django requires Python, specifically Python 2.7 or 3.3 and above. Other Python -libraries may be required for some uses, but you'll receive an error about it -as they're needed. +Django requires Python. See the table in the next question for the versions of +Python that work with each version of Django. Other Python libraries may be +required for some uses, but you'll receive an error about it as they're needed. For a development environment -- if you just want to experiment with Django -- you don't need to have a separate Web server installed; Django comes with its @@ -47,13 +47,19 @@ Django version Python versions ============== =============== 1.4 2.5, 2.6, 2.7 **1.7, 1.8** **2.7** and **3.2, 3.3, 3.4** -1.9 2.7, 3.3, 3.4, 3.5 +1.9 2.7, 3.4, 3.5 ============== =============== For each version of Python, only the latest micro release (A.B.C) is officially supported. You can find the latest micro version for each series on the `Python download page `_. +Typically, we will support a Python version up to and including the first +Django LTS release that will receive security updates until after security +support for that version of Python ends. For example, Python 3.3 security +support ends September 2017 and Django 1.8 LTS security support ends April +2018. Therefore Django 1.8 is the last version to support Python 3.3. + What Python version should I use with Django? --------------------------------------------- diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt index 694bec7845..e1797c8e3b 100644 --- a/docs/internals/contributing/writing-code/unit-tests.txt +++ b/docs/internals/contributing/writing-code/unit-tests.txt @@ -21,8 +21,8 @@ Running the unit tests Quickstart ~~~~~~~~~~ -If you are on Python < 3.3, you'll first need to install a backport of the -``unittest.mock`` module that's available in Python 3.3+. See +If you are on Python 2, you'll first need to install a backport of the +``unittest.mock`` module that's available in Python 3. See :ref:`running-unit-tests-dependencies` for details on installing `mock`_ and the other optional test dependencies. diff --git a/docs/intro/install.txt b/docs/intro/install.txt index ffdad3c4c2..b4f250afc9 100644 --- a/docs/intro/install.txt +++ b/docs/intro/install.txt @@ -9,9 +9,9 @@ that'll work while you walk through the introduction. Install Python -------------- -Being a Python Web framework, Django requires Python. It works with Python 2.7 -and Python 3.3+. All these versions of Python include a lightweight database called -SQLite_ so you won't need to set up a database just yet. +Being a Python Web framework, Django requires Python. See +:ref:`faq-python-version-support` for details. Python includes a lightweight +database called SQLite_ so you won't need to set up a database just yet. .. _sqlite: http://sqlite.org/ diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index 6db12d118b..ca96ae54df 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -22,7 +22,7 @@ tell Django is installed and which version by running the following command: 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.3 or later. If the +This tutorial is written for Django |version| and Python 3.4 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 diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 2cd2ad018a..4b7ef5184a 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -20,7 +20,7 @@ Python compatibility Like Django 1.8, Django 1.9 requires Python 2.7 or above, though we **highly recommend** the latest minor release. We've dropped support for -Python 3.2 and added support for Python 3.5. +Python 3.2 and 3.3, and added support for Python 3.5. What's new in Django 1.9 ======================== diff --git a/tests/apps/tests.py b/tests/apps/tests.py index 2837e78aff..c199fa156d 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals import os -import sys import warnings from unittest import skipUnless @@ -367,9 +366,7 @@ class AppConfigTests(SimpleTestCase): AppConfig('label', Stub(__path__=['a', 'b'])) -@skipUnless( - sys.version_info > (3, 3, 0), - "Namespace packages sans __init__.py were added in Python 3.3") +@skipUnless(six.PY3, "Namespace packages sans __init__.py were added in Python 3.3") class NamespacePackageAppTests(SimpleTestCase): # We need nsapp to be top-level so our multiple-paths tests can add another # location for it (if its inside a normal package with an __init__.py that diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py index 993010b2f4..58fbf2a0c7 100644 --- a/tests/auth_tests/test_hashers.py +++ b/tests/auth_tests/test_hashers.py @@ -353,7 +353,7 @@ class TestUtilsHashPass(SimpleTestCase): def test_load_library_importerror(self): PlainHasher = type(str('PlainHasher'), (BasePasswordHasher,), {'algorithm': 'plain', 'library': 'plain'}) - # Python 3.3 adds quotes around module name + # Python 3 adds quotes around module name with six.assertRaisesRegex(self, ValueError, "Couldn't load 'PlainHasher' algorithm library: No module named '?plain'?"): PlainHasher()._load_library() diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index 3077dd9e61..58344e50ce 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -843,7 +843,7 @@ class MakeMigrationsTests(MigrationTestBase): content = cmd("0001", migration_name_0001) self.assertIn("dependencies=[\n]", content) - # Python 3.3+ importlib caches os.listdir() on some platforms like + # Python 3 importlib caches os.listdir() on some platforms like # Mac OS X (#23850). if hasattr(importlib, 'invalidate_caches'): importlib.invalidate_caches() diff --git a/tests/project_template/test_settings.py b/tests/project_template/test_settings.py index 8998a844c7..ac115f7dc2 100644 --- a/tests/project_template/test_settings.py +++ b/tests/project_template/test_settings.py @@ -1,11 +1,11 @@ -import sys import unittest from django.test import TestCase +from django.utils import six -@unittest.skipIf(sys.version_info < (3, 3), - 'Python < 3.3 cannot import the project template because ' +@unittest.skipIf(six.PY2, + 'Python 2 cannot import the project template because ' 'django/conf/project_template doesn\'t have an __init__.py file.') class TestStartProjectSettings(TestCase):