1
0
mirror of https://github.com/django/django.git synced 2025-03-12 10:22:37 +00:00

Fixed #36005 -- Dropped support for Python 3.10 and 3.11.

This commit is contained in:
Mariusz Felisiak 2025-01-17 22:09:56 +01:00 committed by Sarah Boyce
parent 61dae11df5
commit f5772de696
30 changed files with 79 additions and 266 deletions

View File

@ -16,8 +16,6 @@ jobs:
strategy:
matrix:
python-version:
- '3.10'
- '3.11'
- '3.12'
- '3.13'
- '3.14-dev'
@ -64,64 +62,6 @@ jobs:
- name: Run tests
run: python -Wall tests/runtests.py --verbosity=2
pypy-sqlite:
runs-on: ubuntu-latest
name: Ubuntu, SQLite, PyPy3.10
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: pypy-3.10-nightly
cache: 'pip'
cache-dependency-path: 'tests/requirements/py3.txt'
- name: Install libmemcached-dev for pylibmc
run: sudo apt-get install libmemcached-dev
- name: Install and upgrade packaging tools
run: python -m pip install --upgrade pip setuptools wheel
- run: python -m pip install -r tests/requirements/py3.txt -e .
- name: Run tests
run: python -Wall tests/runtests.py --verbosity=2
pypy-postgresql:
runs-on: ubuntu-latest
name: Ubuntu, PostgreSQL, PyPy3.10
continue-on-error: true
services:
postgres:
image: postgres:14-alpine
env:
POSTGRES_DB: django
POSTGRES_USER: user
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: pypy-3.10-nightly
cache: 'pip'
cache-dependency-path: 'tests/requirements/py3.txt'
- name: Install libmemcached-dev for pylibmc
run: sudo apt-get install libmemcached-dev
- name: Install and upgrade packaging tools
run: python -m pip install --upgrade pip setuptools wheel
- run: python -m pip install -r tests/requirements/py3.txt -r tests/requirements/postgres.txt -e .
- name: Create PostgreSQL settings file
run: mv ./.github/workflows/data/test_postgres.py.tpl ./tests/test_postgres.py
- name: Run tests
run: python -Wall tests/runtests.py --settings=test_postgres --verbosity=2
javascript-tests:
runs-on: ubuntu-latest
name: JavaScript tests

View File

@ -24,7 +24,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: '3.13'
cache: 'pip'
cache-dependency-path: 'tests/requirements/py3.txt'
- name: Install and upgrade packaging tools

View File

@ -1,6 +1,6 @@
Thanks for downloading Django.
To install it, make sure you have Python 3.10 or greater installed. Then run
To install it, make sure you have Python 3.12 or greater installed. Then run
this command from the command prompt:
python -m pip install .

View File

@ -16,7 +16,7 @@ from django.db import models
from django.db.migrations.operations.base import Operation
from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
from django.utils.functional import LazyObject, Promise
from django.utils.version import PY311, get_docs_version
from django.utils.version import get_docs_version
FUNCTION_TYPES = (types.FunctionType, types.BuiltinFunctionType, types.MethodType)
@ -140,11 +140,7 @@ class EnumSerializer(BaseSerializer):
enum_class = self.value.__class__
module = enum_class.__module__
if issubclass(enum_class, enum.Flag):
if PY311:
members = list(self.value)
else:
members, _ = enum._decompose(enum_class, self.value)
members = reversed(members)
else:
members = (self.value,)
return (

View File

@ -1,25 +1,8 @@
import enum
from enum import EnumType, IntEnum, StrEnum
from enum import property as enum_property
from django.utils.functional import Promise
from django.utils.version import PY311, PY312
if PY311:
from enum import EnumType, IntEnum, StrEnum
from enum import property as enum_property
else:
from enum import EnumMeta as EnumType
from types import DynamicClassAttribute as enum_property
class ReprEnum(enum.Enum):
def __str__(self):
return str(self.value)
class IntEnum(int, ReprEnum):
pass
class StrEnum(str, ReprEnum):
pass
__all__ = ["Choices", "IntegerChoices", "TextChoices"]
@ -49,14 +32,6 @@ class ChoicesType(EnumType):
member._label_ = label
return enum.unique(cls)
if not PY312:
def __contains__(cls, member):
if not isinstance(member, enum.Enum):
# Allow non-enums to match against member values.
return any(x.value == member for x in cls)
return super().__contains__(member)
@property
def names(cls):
empty = ["__empty__"] if hasattr(cls, "__empty__") else []
@ -79,13 +54,7 @@ class ChoicesType(EnumType):
class Choices(enum.Enum, metaclass=ChoicesType):
"""Class for creating enumerated choices."""
if PY311:
do_not_call_in_templates = enum.nonmember(True)
else:
@property
def do_not_call_in_templates(self):
return True
@enum_property
def label(self):

View File

@ -14,7 +14,6 @@ from django.db.models.fields import Field
from django.db.models.query_utils import DeferredAttribute
from django.db.models.utils import AltersData
from django.utils.translation import gettext_lazy as _
from django.utils.version import PY311
class FieldFile(File, AltersData):
@ -329,7 +328,7 @@ class FileField(Field):
f"File for {self.name} must have "
"the name attribute specified to be saved."
)
if PY311 and isinstance(file._file, ContentFile):
if isinstance(file._file, ContentFile):
exc.add_note("Pass a 'name' argument to ContentFile.")
raise exc

View File

@ -28,7 +28,7 @@ from django.test.utils import setup_test_environment
from django.test.utils import teardown_databases as _teardown_databases
from django.test.utils import teardown_test_environment
from django.utils.datastructures import OrderedSet
from django.utils.version import PY312, PY313
from django.utils.version import PY313
try:
import ipdb as pdb
@ -829,7 +829,6 @@ class DiscoverRunner:
"unittest -k option."
),
)
if PY312:
parser.add_argument(
"--durations",
dest="durations",
@ -1005,9 +1004,8 @@ class DiscoverRunner:
"resultclass": self.get_resultclass(),
"verbosity": self.verbosity,
"buffer": self.buffer,
"durations": self.durations,
}
if PY312:
kwargs["durations"] = self.durations
return kwargs
def run_checks(self, databases):

View File

@ -54,7 +54,6 @@ from django.test.utils import (
override_settings,
)
from django.utils.functional import classproperty
from django.utils.version import PY311
from django.views.static import serve
logger = logging.getLogger("django.test")
@ -71,24 +70,6 @@ __all__ = (
__unittest = True
if not PY311:
# Backport of unittest.case._enter_context() from Python 3.11.
def _enter_context(cm, addcleanup):
# Look up the special methods on the type to match the with statement.
cls = type(cm)
try:
enter = cls.__enter__
exit = cls.__exit__
except AttributeError:
raise TypeError(
f"'{cls.__module__}.{cls.__qualname__}' object does not support the "
f"context manager protocol"
) from None
result = enter(cm)
addcleanup(exit, cm, None, None, None)
return result
def to_list(value):
"""Put value into a list if it's not already one."""
if not isinstance(value, list):
@ -398,12 +379,6 @@ class SimpleTestCase(unittest.TestCase):
"""Perform post-test things."""
pass
if not PY311:
# Backport of unittest.TestCase.enterClassContext() from Python 3.11.
@classmethod
def enterClassContext(cls, cm):
return _enter_context(cm, cls.addClassCleanup)
def settings(self, **kwargs):
"""
A context manager that temporarily sets a setting and reverts to the

View File

@ -17,7 +17,7 @@ from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_str
from django.utils.module_loading import import_string
from django.utils.regex_helper import _lazy_re_compile
from django.utils.version import PY311, get_docs_version
from django.utils.version import get_docs_version
from django.views.decorators.debug import coroutine_functions_to_sensitive_variables
# Minimal Django templates engine to render the error templates
@ -567,7 +567,6 @@ class ExceptionReporter:
post_context = []
colno = tb_area_colno = ""
if PY311:
_, _, start_column, end_column = next(
itertools.islice(
tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None
@ -578,9 +577,7 @@ class ExceptionReporter:
spaces = " " * (start_column + len(str(lineno + 1)) + 2)
colno = f"\n{spaces}{underline}"
tb_area_spaces = " " * (
4
+ start_column
- (len(context_line) - len(context_line.lstrip()))
4 + start_column - (len(context_line) - len(context_line.lstrip()))
)
tb_area_colno = f"\n{tb_area_spaces}{underline}"
yield {

View File

@ -92,14 +92,14 @@ 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.10
<running-unit-tests-settings>`. For example, to run the tests on Python 3.12
using PostgreSQL:
.. console::
$ tox -e py310-postgres -- --settings=my_postgres_settings
$ tox -e py312-postgres -- --settings=my_postgres_settings
This command sets up a Python 3.10 virtual environment, installs Django's
This command sets up a Python 3.12 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``).
@ -114,14 +114,14 @@ above:
.. code-block:: console
$ DJANGO_SETTINGS_MODULE=my_postgres_settings tox -e py310-postgres
$ DJANGO_SETTINGS_MODULE=my_postgres_settings tox -e py312-postgres
Windows users should use:
.. code-block:: doscon
...\> set DJANGO_SETTINGS_MODULE=my_postgres_settings
...\> tox -e py310-postgres
...\> tox -e py312-postgres
Running the JavaScript tests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -220,7 +220,7 @@ this. For a small app like polls, this process isn't too difficult.
]
description = "A Django app to conduct web-based polls."
readme = "README.rst"
requires-python = ">= 3.10"
requires-python = ">= 3.12"
authors = [
{name = "Your Name", email = "yourname@example.com"},
]
@ -234,8 +234,6 @@ this. For a small app like polls, this process isn't too difficult.
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Internet :: WWW/HTTP",

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|, which supports Python 3.10 and
This tutorial is written for Django |version|, which supports Python 3.12 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

View File

@ -1583,10 +1583,6 @@ Outputs timings, including database setup and total run time.
Shows the N slowest test cases (N=0 for all).
.. admonition:: Python 3.12 and later
This feature is only available for Python 3.12 and later.
``testserver``
--------------

View File

@ -21,6 +21,8 @@ Python compatibility
Django 6.0 supports Python 3.12 and 3.13. We **highly recommend** and only
officially support the latest release of each series.
The Django 5.2.x series is the last to support Python 3.10 and 3.11.
Third-party library support for older version of Django
=======================================================

View File

@ -419,7 +419,8 @@ performance gains, typically for heavyweight applications.
A key aim of the PyPy project is `compatibility
<https://www.pypy.org/compat.html>`_ with existing Python APIs and libraries.
Django is compatible, but you will need to check the compatibility of other
Django is compatible with versions of PyPy corresponding to the supported
Python versions, but you will need to check the compatibility of other
libraries you rely on.
C implementations of Python libraries

View File

@ -608,7 +608,6 @@ and tear down the test suite.
``durations`` will show a list of the N slowest test cases. Setting this
option to ``0`` will result in the duration for all tests being shown.
Requires Python 3.12+.
Django may, from time to time, extend the capabilities of the test runner
by adding new arguments. The ``**kwargs`` declaration allows for this

View File

@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "Django"
dynamic = ["version"]
requires-python = ">= 3.10"
requires-python = ">= 3.12"
dependencies = [
"asgiref>=3.8.1",
"sqlparse>=0.3.1",
@ -27,8 +27,6 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Internet :: WWW/HTTP",
@ -54,7 +52,7 @@ Source = "https://github.com/django/django"
Tracker = "https://code.djangoproject.com/"
[tool.black]
target-version = ["py310"]
target-version = ["py312"]
force-exclude = "tests/test_runner_apps/tagged/tests_syntax_error.py"
[tool.isort]

View File

@ -184,7 +184,7 @@ class MailTests(MailTestsMixin, SimpleTestCase):
"""Line length check should encode the payload supporting `surrogateescape`.
Following https://github.com/python/cpython/issues/76511, newer
versions of Python (3.11.9, 3.12.3 and 3.13) ensure that a message's
versions of Python (3.12.3 and 3.13) ensure that a message's
payload is encoded with the provided charset and `surrogateescape` is
used as the error handling strategy.

View File

@ -8,7 +8,6 @@ from django.template import Context, Template
from django.test import SimpleTestCase
from django.utils.functional import Promise
from django.utils.translation import gettext_lazy as _
from django.utils.version import PY311
class Suit(models.IntegerChoices):
@ -200,13 +199,7 @@ class ChoicesTests(SimpleTestCase):
def test_do_not_call_in_templates_nonmember(self):
self.assertNotIn("do_not_call_in_templates", Suit.__members__)
if PY311:
self.assertIs(Suit.do_not_call_in_templates, True)
else:
# Using @property on an enum does not behave as expected.
self.assertTrue(Suit.do_not_call_in_templates)
self.assertIsNot(Suit.do_not_call_in_templates, True)
self.assertIsInstance(Suit.do_not_call_in_templates, property)
class Separator(bytes, models.Choices):

View File

@ -12,7 +12,6 @@ from django.core.files.uploadedfile import TemporaryUploadedFile
from django.db import IntegrityError, models
from django.test import TestCase, override_settings
from django.test.utils import isolate_apps
from django.utils.version import PY311
from .models import Document
@ -80,7 +79,6 @@ class FileFieldTests(TestCase):
with self.assertRaisesMessage(FieldError, msg) as cm:
d.save()
if PY311:
self.assertEqual(
cm.exception.__notes__, ["Pass a 'name' argument to ContentFile."]
)

View File

@ -34,7 +34,7 @@ else:
)
from django.utils.functional import classproperty
from django.utils.log import DEFAULT_LOGGING
from django.utils.version import PY312, PYPY
from django.utils.version import PYPY
try:
@ -691,7 +691,6 @@ if __name__ == "__main__":
"Same as unittest -k option. Can be used multiple times."
),
)
if PY312:
parser.add_argument(
"--durations",
dest="durations",

View File

@ -4,7 +4,6 @@ from io import StringIO
from django.db import connection
from django.test import TestCase
from django.test.runner import DiscoverRunner
from django.utils.version import PY311
from .models import Person
@ -114,17 +113,15 @@ class TestDebugSQL(unittest.TestCase):
),
]
# Python 3.11 uses fully qualified test name in the output.
method_name = ".runTest" if PY311 else ""
test_class_path = "test_runner.test_debug_sql.TestDebugSQL"
verbose_expected_outputs = [
f"runTest ({test_class_path}.FailingTest{method_name}) ... FAIL",
f"runTest ({test_class_path}.ErrorTest{method_name}) ... ERROR",
f"runTest ({test_class_path}.PassingTest{method_name}) ... ok",
f"runTest ({test_class_path}.FailingTest.runTest) ... FAIL",
f"runTest ({test_class_path}.ErrorTest.runTest) ... ERROR",
f"runTest ({test_class_path}.PassingTest.runTest) ... ok",
# If there are errors/failures in subtests but not in test itself,
# the status is not written. That behavior comes from Python.
f"runTest ({test_class_path}.FailingSubTest{method_name}) ...",
f"runTest ({test_class_path}.ErrorSubTest{method_name}) ...",
f"runTest ({test_class_path}.FailingSubTest.runTest) ...",
f"runTest ({test_class_path}.ErrorSubTest.runTest) ...",
(
"""SELECT COUNT(*) AS "__count"\n"""
"""FROM "test_runner_person"\nWHERE """

View File

@ -16,7 +16,6 @@ from django.test.utils import (
captured_stderr,
captured_stdout,
)
from django.utils.version import PY312
@contextmanager
@ -768,7 +767,6 @@ class DiscoverRunnerTests(SimpleTestCase):
failures = runner.suite_result(suite, result)
self.assertEqual(failures, expected_failures)
@unittest.skipUnless(PY312, "unittest --durations option requires Python 3.12")
def test_durations(self):
with captured_stderr() as stderr, captured_stdout():
runner = DiscoverRunner(durations=10)
@ -776,7 +774,6 @@ class DiscoverRunnerTests(SimpleTestCase):
runner.run_suite(suite)
self.assertIn("Slowest test durations", stderr.getvalue())
@unittest.skipUnless(PY312, "unittest --durations option requires Python 3.12")
def test_durations_debug_sql(self):
with captured_stderr() as stderr, captured_stdout():
runner = DiscoverRunner(durations=10, debug_sql=True)

View File

@ -7,7 +7,6 @@ from unittest.suite import TestSuite, _ErrorHolder
from django.test import SimpleTestCase
from django.test.runner import ParallelTestSuite, RemoteTestResult
from django.utils.version import PY311, PY312
try:
import tblib.pickling_support
@ -193,27 +192,21 @@ class RemoteTestResultTest(SimpleTestCase):
subtest_test.run(result=result)
events = result.events
# addDurations added in Python 3.12.
if PY312:
self.assertEqual(len(events), 5)
else:
self.assertEqual(len(events), 4)
self.assertIs(result.wasSuccessful(), False)
event = events[1]
self.assertEqual(event[0], "addSubTest")
self.assertEqual(
str(event[2]),
"dummy_test (test_runner.test_parallel.SampleFailingSubtest%s) (index=0)"
# Python 3.11 uses fully qualified test name in the output.
% (".dummy_test" if PY311 else ""),
"dummy_test (test_runner.test_parallel.SampleFailingSubtest.dummy_test) "
"(index=0)",
)
self.assertEqual(repr(event[3][1]), "AssertionError('0 != 1')")
event = events[2]
self.assertEqual(repr(event[3][1]), "AssertionError('2 != 1')")
@unittest.skipUnless(PY312, "unittest --durations option requires Python 3.12")
def test_add_duration(self):
result = RemoteTestResult()
result.addDuration(None, 2.3)

View File

@ -15,7 +15,7 @@ from django import db
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.management import call_command
from django.core.management.base import CommandError, SystemCheckError
from django.core.management.base import SystemCheckError
from django.test import SimpleTestCase, TransactionTestCase, skipUnlessDBFeature
from django.test.runner import (
DiscoverRunner,
@ -32,7 +32,6 @@ from django.test.utils import (
get_unique_databases_and_mirrors,
iter_test_cases,
)
from django.utils.version import PY312
from .models import B, Person, Through
@ -479,7 +478,6 @@ class ManageCommandTests(unittest.TestCase):
)
self.assertIn("Total run took", stderr.getvalue())
@unittest.skipUnless(PY312, "unittest --durations option requires Python 3.12")
def test_durations(self):
with captured_stderr() as stderr:
call_command(
@ -490,17 +488,6 @@ class ManageCommandTests(unittest.TestCase):
)
self.assertIn("durations=10", stderr.getvalue())
@unittest.skipIf(PY312, "unittest --durations option requires Python 3.12")
def test_durations_lt_py312(self):
msg = "Error: unrecognized arguments: --durations=10"
with self.assertRaises(CommandError, msg=msg):
call_command(
"test",
"--durations=10",
"sites",
testrunner="test_runner.tests.MockTestRunner",
)
# Isolate from the real environment.
@mock.patch.dict(os.environ, {}, clear=True)

View File

@ -49,7 +49,6 @@ from django.test.utils import (
)
from django.urls import NoReverseMatch, path, reverse, reverse_lazy
from django.utils.html import VOID_ELEMENTS
from django.utils.version import PY311
from .models import Car, Person, PossessedCar
from .views import empty_response
@ -103,11 +102,9 @@ class SkippingTestCase(SimpleTestCase):
SkipTestCase("test_foo").test_foo,
ValueError,
"skipUnlessDBFeature cannot be used on test_foo (test_utils.tests."
"SkippingTestCase.test_skip_unless_db_feature.<locals>.SkipTestCase%s) "
"as SkippingTestCase.test_skip_unless_db_feature.<locals>.SkipTestCase "
"doesn't allow queries against the 'default' database."
# Python 3.11 uses fully qualified test name in the output.
% (".test_foo" if PY311 else ""),
"SkippingTestCase.test_skip_unless_db_feature.<locals>.SkipTestCase."
"test_foo) as SkippingTestCase.test_skip_unless_db_feature.<locals>."
"SkipTestCase doesn't allow queries against the 'default' database.",
)
def test_skip_if_db_feature(self):
@ -150,11 +147,9 @@ class SkippingTestCase(SimpleTestCase):
SkipTestCase("test_foo").test_foo,
ValueError,
"skipIfDBFeature cannot be used on test_foo (test_utils.tests."
"SkippingTestCase.test_skip_if_db_feature.<locals>.SkipTestCase%s) "
"SkippingTestCase.test_skip_if_db_feature.<locals>.SkipTestCase.test_foo) "
"as SkippingTestCase.test_skip_if_db_feature.<locals>.SkipTestCase "
"doesn't allow queries against the 'default' database."
# Python 3.11 uses fully qualified test name in the output.
% (".test_foo" if PY311 else ""),
"doesn't allow queries against the 'default' database.",
)

View File

@ -8,7 +8,6 @@ from django.utils.dateparse import (
parse_time,
)
from django.utils.timezone import get_fixed_timezone
from django.utils.version import PY311
class DateParseTests(unittest.TestCase):
@ -16,7 +15,6 @@ class DateParseTests(unittest.TestCase):
# Valid inputs
self.assertEqual(parse_date("2012-04-23"), date(2012, 4, 23))
self.assertEqual(parse_date("2012-4-9"), date(2012, 4, 9))
if PY311:
self.assertEqual(parse_date("20120423"), date(2012, 4, 23))
# Invalid inputs
self.assertIsNone(parse_date("2012423"))
@ -26,7 +24,6 @@ class DateParseTests(unittest.TestCase):
def test_parse_time(self):
# Valid inputs
self.assertEqual(parse_time("09:15:00"), time(9, 15))
if PY311:
self.assertEqual(parse_time("091500"), time(9, 15))
self.assertEqual(parse_time("10:10"), time(10, 10))
self.assertEqual(parse_time("10:20:30.400"), time(10, 20, 30, 400000))

View File

@ -1,6 +1,5 @@
from django.test import SimpleTestCase
from django.utils.functional import cached_property, classproperty, lazy
from django.utils.version import PY312
class FunctionalTests(SimpleTestCase):
@ -133,14 +132,10 @@ class FunctionalTests(SimpleTestCase):
"Cannot assign the same cached_property to two different names ('a' and "
"'b')."
)
if PY312:
error_type = TypeError
msg = type_msg
else:
error_type = RuntimeError
msg = "Error calling __set_name__"
with self.assertRaisesMessage(error_type, msg) as ctx:
with self.assertRaisesMessage(error_type, msg):
class ReusedCachedProperty:
@cached_property
@ -149,9 +144,6 @@ class FunctionalTests(SimpleTestCase):
b = a
if not PY312:
self.assertEqual(str(ctx.exception.__context__), str(TypeError(type_msg)))
def test_cached_property_reuse_same_name(self):
"""
Reusing a cached_property on different classes under the same name is

View File

@ -7,7 +7,7 @@ import tempfile
import threading
from io import StringIO
from pathlib import Path
from unittest import mock, skipIf, skipUnless
from unittest import mock, skipIf
from asgiref.sync import async_to_sync, iscoroutinefunction
@ -24,7 +24,6 @@ from django.urls.converters import IntConverter
from django.utils.functional import SimpleLazyObject
from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import mark_safe
from django.utils.version import PY311
from django.views.debug import (
CallableSettingWrapper,
ExceptionCycleWarning,
@ -695,7 +694,6 @@ class ExceptionReporterTests(SimpleTestCase):
text,
)
@skipUnless(PY311, "Exception notes were added in Python 3.11.")
def test_exception_with_notes(self):
request = self.rf.get("/test_view/")
try:
@ -806,7 +804,6 @@ class ExceptionReporterTests(SimpleTestCase):
or os.environ.get("PYTHONNODEBUGRANGES", False),
"Fine-grained error locations are disabled.",
)
@skipUnless(PY311, "Fine-grained error locations were added in Python 3.11.")
def test_highlight_error_position(self):
request = self.rf.get("/test_view/")
try:

View File

@ -26,7 +26,7 @@ setenv =
PYTHONDONTWRITEBYTECODE=1
deps =
-e .
py{3,310,311,312,313,py3}: -rtests/requirements/py3.txt
py{3,312,313}: -rtests/requirements/py3.txt
postgres: -rtests/requirements/postgres.txt
mysql: -rtests/requirements/mysql.txt
oracle: -rtests/requirements/oracle.txt