mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Fixed #30116 -- Dropped support for Python 3.5.
This commit is contained in:
parent
5a5c77d55d
commit
7e6b214ed3
2
INSTALL
2
INSTALL
@ -1,6 +1,6 @@
|
||||
Thanks for downloading Django.
|
||||
|
||||
To install it, make sure you have Python 3.5 or greater installed. Then run
|
||||
To install it, make sure you have Python 3.6 or greater installed. Then run
|
||||
this command from the command prompt:
|
||||
|
||||
python setup.py install
|
||||
|
@ -77,10 +77,8 @@ def _check_lazy_references(apps, ignore=None):
|
||||
"""
|
||||
operation, args, keywords = obj, [], {}
|
||||
while hasattr(operation, 'func'):
|
||||
# The or clauses are redundant but work around a bug (#25945) in
|
||||
# functools.partial in Python <= 3.5.1.
|
||||
args.extend(getattr(operation, 'args', []) or [])
|
||||
keywords.update(getattr(operation, 'keywords', {}) or {})
|
||||
args.extend(getattr(operation, 'args', []))
|
||||
keywords.update(getattr(operation, 'keywords', {}))
|
||||
operation = operation.func
|
||||
return operation, args, keywords
|
||||
|
||||
|
@ -160,10 +160,6 @@ class BaseDatabaseFeatures:
|
||||
# Support for the DISTINCT ON clause
|
||||
can_distinct_on_fields = False
|
||||
|
||||
# Does the backend decide to commit before SAVEPOINT statements
|
||||
# when autocommit is disabled? https://bugs.python.org/issue8145#msg109965
|
||||
autocommits_when_autocommit_is_off = False
|
||||
|
||||
# Does the backend prevent running SQL queries in broken transactions?
|
||||
atomic_transactions = True
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import sys
|
||||
|
||||
from django.db.backends.base.features import BaseDatabaseFeatures
|
||||
|
||||
from .base import Database
|
||||
@ -15,7 +13,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
supports_timezones = False
|
||||
max_query_params = 999
|
||||
supports_mixed_date_datetime_comparisons = False
|
||||
autocommits_when_autocommit_is_off = sys.version_info < (3, 6)
|
||||
can_introspect_autofield = True
|
||||
can_introspect_decimal_field = False
|
||||
can_introspect_duration_field = False
|
||||
|
@ -173,14 +173,6 @@ class Atomic(ContextDecorator):
|
||||
connection.commit_on_exit = True
|
||||
connection.needs_rollback = False
|
||||
if not connection.get_autocommit():
|
||||
# sqlite3 in Python < 3.6 doesn't handle transactions and
|
||||
# savepoints properly when autocommit is off.
|
||||
# Turning autocommit back on isn't an option; it would trigger
|
||||
# a premature commit. Give up if that happens.
|
||||
if connection.features.autocommits_when_autocommit_is_off:
|
||||
raise TransactionManagementError(
|
||||
"Your database backend doesn't behave properly when "
|
||||
"autocommit is off. Turn it on before using 'atomic'.")
|
||||
# Pretend we're already in an atomic block to bypass the code
|
||||
# that disables autocommit to enter a transaction, and make a
|
||||
# note to deal with this case in __exit__.
|
||||
|
@ -3,8 +3,6 @@ import itertools
|
||||
import operator
|
||||
from functools import total_ordering, wraps
|
||||
|
||||
from django.utils.version import PY36, get_docs_version
|
||||
|
||||
|
||||
# You can't trivially replace this with `functools.partial` because this binds
|
||||
# to classes and returns bound instances, whereas functools.partial (on
|
||||
@ -22,8 +20,8 @@ class cached_property:
|
||||
|
||||
A cached property can be made out of an existing method:
|
||||
(e.g. ``url = cached_property(get_absolute_url)``).
|
||||
On Python < 3.6, the optional ``name`` argument must be provided, e.g.
|
||||
``url = cached_property(get_absolute_url, name='url')``.
|
||||
The optional ``name`` argument is obsolete as of Python 3.6 and will be
|
||||
deprecated in Django 4.0 (#30127).
|
||||
"""
|
||||
name = None
|
||||
|
||||
@ -34,29 +32,8 @@ class cached_property:
|
||||
'__set_name__() on it.'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_mangled(name):
|
||||
return name.startswith('__') and not name.endswith('__')
|
||||
|
||||
def __init__(self, func, name=None):
|
||||
if PY36:
|
||||
self.real_func = func
|
||||
else:
|
||||
func_name = func.__name__
|
||||
name = name or func_name
|
||||
if not (isinstance(name, str) and name.isidentifier()):
|
||||
raise ValueError(
|
||||
"%r can't be used as the name of a cached_property." % name,
|
||||
)
|
||||
if self._is_mangled(name):
|
||||
raise ValueError(
|
||||
'cached_property does not work with mangled methods on '
|
||||
'Python < 3.6 without the appropriate `name` argument. See '
|
||||
'https://docs.djangoproject.com/en/%s/ref/utils/'
|
||||
'#cached-property-mangled-name' % get_docs_version(),
|
||||
)
|
||||
self.name = name
|
||||
self.func = func
|
||||
self.real_func = func
|
||||
self.__doc__ = getattr(func, '__doc__')
|
||||
|
||||
def __set_name__(self, owner, name):
|
||||
|
@ -72,11 +72,10 @@ def module_has_submodule(package, module_name):
|
||||
full_module_name = package_name + '.' + module_name
|
||||
try:
|
||||
return importlib_find(full_module_name, package_path) is not None
|
||||
except (ImportError, AttributeError):
|
||||
# When module_name is an invalid dotted path, Python raises ImportError
|
||||
# (or ModuleNotFoundError in Python 3.6+). AttributeError may be raised
|
||||
except (ModuleNotFoundError, AttributeError):
|
||||
# When module_name is an invalid dotted path, Python raises
|
||||
# ModuleNotFoundError. AttributeError is raised on PY36 (fixed in PY37)
|
||||
# if the penultimate part of the path is not a package.
|
||||
# (https://bugs.python.org/issue30436)
|
||||
return False
|
||||
|
||||
|
||||
@ -87,7 +86,7 @@ def module_dir(module):
|
||||
Raise ValueError otherwise, e.g. for namespace packages that are split
|
||||
over several directories.
|
||||
"""
|
||||
# Convert to list because _NamespacePath does not support indexing on 3.3.
|
||||
# Convert to list because _NamespacePath does not support indexing.
|
||||
paths = list(getattr(module, '__path__', []))
|
||||
if len(paths) == 1:
|
||||
return paths[0]
|
||||
|
@ -2,7 +2,7 @@
|
||||
How to install Django on Windows
|
||||
================================
|
||||
|
||||
This document will guide you through installing Python 3.5 and Django on
|
||||
This document will guide you through installing Python 3.7 and Django on
|
||||
Windows. It also provides instructions for installing `virtualenv`_ and
|
||||
`virtualenvwrapper`_, which make it easier to work on Python projects. This is
|
||||
meant as a beginner's guide for users working on Django projects and does not
|
||||
@ -17,12 +17,12 @@ Install Python
|
||||
==============
|
||||
|
||||
Django is a Python web framework, thus requiring Python to be installed on your
|
||||
machine. At the time of writing, Python 3.5 is the latest version.
|
||||
machine. At the time of writing, Python 3.7 is the latest version.
|
||||
|
||||
To install Python on your machine go to https://python.org/downloads/. The
|
||||
website should offer you a download button for the latest Python version.
|
||||
Download the executable installer and run it. Check the box next to ``Add
|
||||
Python 3.5 to PATH`` and then click ``Install Now``.
|
||||
Python 3.7 to PATH`` and then click ``Install Now``.
|
||||
|
||||
After installation, open the command prompt and check that the Python version
|
||||
matches the version you installed by executing::
|
||||
|
@ -90,12 +90,12 @@ In addition to the default environments, ``tox`` supports running unit tests
|
||||
for other versions of Python and other database backends. Since Django's test
|
||||
suite doesn't bundle a settings file for database backends other than SQLite,
|
||||
however, you must :ref:`create and provide your own test settings
|
||||
<running-unit-tests-settings>`. For example, to run the tests on Python 3.5
|
||||
<running-unit-tests-settings>`. For example, to run the tests on Python 3.7
|
||||
using PostgreSQL::
|
||||
|
||||
$ tox -e py35-postgres -- --settings=my_postgres_settings
|
||||
$ tox -e py37-postgres -- --settings=my_postgres_settings
|
||||
|
||||
This command sets up a Python 3.5 virtual environment, installs Django's
|
||||
This command sets up a Python 3.7 virtual environment, installs Django's
|
||||
test suite dependencies (including those for PostgreSQL), and calls
|
||||
``runtests.py`` with the supplied arguments (in this case,
|
||||
``--settings=my_postgres_settings``).
|
||||
|
@ -219,8 +219,8 @@ this. For a small app like polls, this process isn't too difficult.
|
||||
'License :: OSI Approved :: BSD License', # example license
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||
],
|
||||
|
@ -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|, which supports Python 3.5 and
|
||||
This tutorial is written for Django |version|, which supports Python 3.6 and
|
||||
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're using an older
|
||||
|
@ -514,18 +514,6 @@ https://web.archive.org/web/20110718035220/http://diveintomark.org/archives/2004
|
||||
z = person.friends # does not call
|
||||
x is z # is True
|
||||
|
||||
.. warning::
|
||||
|
||||
.. _cached-property-mangled-name:
|
||||
|
||||
On Python < 3.6, ``cached_property`` doesn't work properly with a
|
||||
mangled__ name unless it's passed a ``name`` of the form
|
||||
``_Class__attribute``::
|
||||
|
||||
__friends = cached_property(get_friends, name='_Person__friends')
|
||||
|
||||
__ https://docs.python.org/faq/programming.html#i-try-to-use-spam-and-i-get-an-error-about-someclassname-spam
|
||||
|
||||
.. function:: keep_lazy(func, *resultclasses)
|
||||
|
||||
Django offers many utility functions (particularly in ``django.utils``)
|
||||
|
@ -1329,23 +1329,6 @@ The decorator can also be applied to test case classes::
|
||||
decorator. For a given class, :func:`~django.test.modify_settings` is
|
||||
always applied after :func:`~django.test.override_settings`.
|
||||
|
||||
.. admonition:: Considerations with Python 3.5
|
||||
|
||||
If using Python 3.5 (or older, if using an older version of Django), avoid
|
||||
mixing ``remove`` with ``append`` and ``prepend`` in
|
||||
:func:`~django.test.modify_settings`. In some cases it matters whether a
|
||||
value is first added and then removed or vice versa, and dictionary key
|
||||
order isn't preserved until Python 3.6. Instead, apply the decorator twice
|
||||
to guarantee the order of operations. For example, to ensure that
|
||||
``SessionMiddleware`` appears first in ``MIDDLEWARE``::
|
||||
|
||||
@modify_settings(MIDDLEWARE={
|
||||
'remove': ['django.contrib.sessions.middleware.SessionMiddleware'],
|
||||
)
|
||||
@modify_settings(MIDDLEWARE={
|
||||
'prepend': ['django.contrib.sessions.middleware.SessionMiddleware'],
|
||||
})
|
||||
|
||||
.. warning::
|
||||
|
||||
The settings file contains some settings that are only consulted during
|
||||
|
3
setup.py
3
setup.py
@ -5,7 +5,7 @@ from distutils.sysconfig import get_python_lib
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
CURRENT_PYTHON = sys.version_info[:2]
|
||||
REQUIRED_PYTHON = (3, 5)
|
||||
REQUIRED_PYTHON = (3, 6)
|
||||
|
||||
# This check and everything above must remain compatible with Python 2.7.
|
||||
if CURRENT_PYTHON < REQUIRED_PYTHON:
|
||||
@ -98,7 +98,6 @@ setup(
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3 :: Only',
|
||||
|
@ -32,7 +32,6 @@ from django.db.migrations.recorder import MigrationRecorder
|
||||
from django.test import (
|
||||
LiveServerTestCase, SimpleTestCase, TestCase, override_settings,
|
||||
)
|
||||
from django.utils.version import PY36
|
||||
|
||||
custom_templates_dir = os.path.join(os.path.dirname(__file__), 'custom_templates')
|
||||
|
||||
@ -1145,7 +1144,7 @@ class ManageCheck(AdminScriptTestCase):
|
||||
args = ['check']
|
||||
out, err = self.run_manage(args)
|
||||
self.assertNoOutput(out)
|
||||
self.assertOutput(err, 'ModuleNotFoundError' if PY36 else 'ImportError')
|
||||
self.assertOutput(err, 'ModuleNotFoundError')
|
||||
self.assertOutput(err, 'No module named')
|
||||
self.assertOutput(err, 'admin_scriptz')
|
||||
|
||||
|
@ -1211,10 +1211,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.
|
||||
# Setting a value only silences a deprecation warning in Python 3.5.
|
||||
kwargs['decode_data'] = True
|
||||
smtpd.SMTPServer.__init__(self, *args, **kwargs)
|
||||
smtpd.SMTPServer.__init__(self, *args, decode_data=True, **kwargs)
|
||||
self._sink = []
|
||||
self.active = False
|
||||
self.active_lock = threading.Lock()
|
||||
|
@ -22,7 +22,6 @@ from django.utils.deconstruct import deconstructible
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from django.utils.timezone import get_default_timezone, get_fixed_timezone, utc
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.version import PY36
|
||||
|
||||
from .models import FoodManager, FoodQuerySet
|
||||
|
||||
@ -413,10 +412,7 @@ class WriterTests(SimpleTestCase):
|
||||
# Test a string regex with flag
|
||||
validator = RegexValidator(r'^[0-9]+$', flags=re.S)
|
||||
string = MigrationWriter.serialize(validator)[0]
|
||||
if PY36:
|
||||
self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=re.RegexFlag(16))")
|
||||
else:
|
||||
self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=16)")
|
||||
self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=re.RegexFlag(16))")
|
||||
self.serialize_round_trip(validator)
|
||||
|
||||
# Test message and code
|
||||
|
@ -1,11 +1,9 @@
|
||||
import unittest
|
||||
from operator import attrgetter
|
||||
|
||||
from django.core.exceptions import FieldError, ValidationError
|
||||
from django.db import connection, models
|
||||
from django.test import SimpleTestCase, TestCase
|
||||
from django.test.utils import CaptureQueriesContext, isolate_apps
|
||||
from django.utils.version import PY36
|
||||
|
||||
from .models import (
|
||||
Base, Chef, CommonInfo, GrandChild, GrandParent, ItalianRestaurant,
|
||||
@ -176,7 +174,6 @@ class ModelInheritanceTests(TestCase):
|
||||
|
||||
self.assertIs(C._meta.parents[A], C._meta.get_field('a'))
|
||||
|
||||
@unittest.skipUnless(PY36, 'init_subclass is new in Python 3.6')
|
||||
@isolate_apps('model_inheritance')
|
||||
def test_init_subclass(self):
|
||||
saved_kwargs = {}
|
||||
@ -193,7 +190,6 @@ class ModelInheritanceTests(TestCase):
|
||||
|
||||
self.assertEqual(saved_kwargs, kwargs)
|
||||
|
||||
@unittest.skipUnless(PY36, '__set_name__ is new in Python 3.6')
|
||||
@isolate_apps('model_inheritance')
|
||||
def test_set_name(self):
|
||||
class ClassAttr:
|
||||
|
@ -234,7 +234,7 @@ def teardown(state):
|
||||
# Discard the multiprocessing.util finalizer that tries to remove a
|
||||
# temporary directory that's already removed by this script's
|
||||
# atexit.register(shutil.rmtree, TMPDIR) handler. Prevents
|
||||
# FileNotFoundError at the end of a test run on Python 3.6+ (#27890).
|
||||
# FileNotFoundError at the end of a test run (#27890).
|
||||
from multiprocessing.util import _finalizer_registry
|
||||
_finalizer_registry.pop((-100, 0), None)
|
||||
|
||||
|
@ -228,7 +228,6 @@ class AtomicInsideTransactionTests(AtomicTests):
|
||||
self.atomic.__exit__(*sys.exc_info())
|
||||
|
||||
|
||||
@skipIfDBFeature('autocommits_when_autocommit_is_off')
|
||||
class AtomicWithoutAutocommitTests(AtomicTests):
|
||||
"""All basic tests for atomic should also pass when autocommit is turned off."""
|
||||
|
||||
@ -480,7 +479,6 @@ class AtomicMiscTests(TransactionTestCase):
|
||||
Reporter.objects.create()
|
||||
|
||||
|
||||
@skipIfDBFeature('autocommits_when_autocommit_is_off')
|
||||
class NonAutocommitTests(TransactionTestCase):
|
||||
|
||||
available_apps = []
|
||||
|
@ -1,8 +1,5 @@
|
||||
import unittest
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from django.utils.functional import cached_property, lazy
|
||||
from django.utils.version import PY36
|
||||
|
||||
|
||||
class FunctionalTests(SimpleTestCase):
|
||||
@ -104,7 +101,6 @@ class FunctionalTests(SimpleTestCase):
|
||||
for attr in attrs:
|
||||
self.assertCachedPropertyWorks(attr, Class)
|
||||
|
||||
@unittest.skipUnless(PY36, '__set_name__ is new in Python 3.6')
|
||||
def test_cached_property_auto_name(self):
|
||||
"""
|
||||
cached_property caches its value and behaves like a property
|
||||
@ -132,7 +128,6 @@ class FunctionalTests(SimpleTestCase):
|
||||
obj.other2
|
||||
self.assertFalse(hasattr(obj, 'different_name'))
|
||||
|
||||
@unittest.skipUnless(PY36, '__set_name__ is new in Python 3.6')
|
||||
def test_cached_property_reuse_different_names(self):
|
||||
"""Disallow this case because the decorated function wouldn't be cached."""
|
||||
with self.assertRaises(RuntimeError) as ctx:
|
||||
@ -151,7 +146,6 @@ class FunctionalTests(SimpleTestCase):
|
||||
))
|
||||
)
|
||||
|
||||
@unittest.skipUnless(PY36, '__set_name__ is new in Python 3.6')
|
||||
def test_cached_property_reuse_same_name(self):
|
||||
"""
|
||||
Reusing a cached_property on different classes under the same name is
|
||||
@ -177,7 +171,6 @@ class FunctionalTests(SimpleTestCase):
|
||||
self.assertEqual(b.cp, 2)
|
||||
self.assertEqual(a.cp, 1)
|
||||
|
||||
@unittest.skipUnless(PY36, '__set_name__ is new in Python 3.6')
|
||||
def test_cached_property_set_name_not_called(self):
|
||||
cp = cached_property(lambda s: None)
|
||||
|
||||
@ -189,29 +182,6 @@ class FunctionalTests(SimpleTestCase):
|
||||
with self.assertRaisesMessage(TypeError, msg):
|
||||
Foo().cp
|
||||
|
||||
@unittest.skipIf(PY36, '__set_name__ is new in Python 3.6')
|
||||
def test_cached_property_mangled_error(self):
|
||||
msg = (
|
||||
'cached_property does not work with mangled methods on '
|
||||
'Python < 3.6 without the appropriate `name` argument.'
|
||||
)
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
@cached_property
|
||||
def __value(self):
|
||||
pass
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
def func(self):
|
||||
pass
|
||||
cached_property(func, name='__value')
|
||||
|
||||
@unittest.skipIf(PY36, '__set_name__ is new in Python 3.6')
|
||||
def test_cached_property_name_validation(self):
|
||||
msg = "%s can't be used as the name of a cached_property."
|
||||
with self.assertRaisesMessage(ValueError, msg % "'<lambda>'"):
|
||||
cached_property(lambda x: None)
|
||||
with self.assertRaisesMessage(ValueError, msg % 42):
|
||||
cached_property(str, name=42)
|
||||
|
||||
def test_lazy_equality(self):
|
||||
"""
|
||||
== and != work correctly for Promises.
|
||||
|
@ -17,7 +17,6 @@ from django.test.utils import LoggingCaptureMixin
|
||||
from django.urls import path, reverse
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.version import PY36
|
||||
from django.views.debug import (
|
||||
CLEANSED_SUBSTITUTE, CallableSettingWrapper, ExceptionReporter,
|
||||
cleanse_setting, technical_500_response,
|
||||
@ -515,7 +514,7 @@ class ExceptionReporterTests(SimpleTestCase):
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
reporter = ExceptionReporter(request, exc_type, exc_value, tb)
|
||||
html = reporter.get_traceback_html()
|
||||
self.assertInHTML('<h1>%sError at /test_view/</h1>' % ('ModuleNotFound' if PY36 else 'Import'), html)
|
||||
self.assertInHTML('<h1>ModuleNotFoundError at /test_view/</h1>', html)
|
||||
|
||||
def test_ignore_traceback_evaluation_exceptions(self):
|
||||
"""
|
||||
|
2
tox.ini
2
tox.ini
@ -21,7 +21,7 @@ passenv = DJANGO_SETTINGS_MODULE PYTHONPATH HOME DISPLAY
|
||||
setenv =
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
deps =
|
||||
py{3,35,36,37}: -rtests/requirements/py3.txt
|
||||
py{3,36,37}: -rtests/requirements/py3.txt
|
||||
postgres: -rtests/requirements/postgres.txt
|
||||
mysql: -rtests/requirements/mysql.txt
|
||||
oracle: -rtests/requirements/oracle.txt
|
||||
|
Loading…
Reference in New Issue
Block a user