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:
parent
61dae11df5
commit
f5772de696
60
.github/workflows/schedule_tests.yml
vendored
60
.github/workflows/schedule_tests.yml
vendored
@ -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
|
||||
|
2
.github/workflows/screenshots.yml
vendored
2
.github/workflows/screenshots.yml
vendored
@ -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
|
||||
|
2
INSTALL
2
INSTALL
@ -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 .
|
||||
|
@ -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)
|
||||
members = list(self.value)
|
||||
else:
|
||||
members = (self.value,)
|
||||
return (
|
||||
|
@ -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
|
||||
do_not_call_in_templates = enum.nonmember(True)
|
||||
|
||||
@enum_property
|
||||
def label(self):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,15 +829,14 @@ class DiscoverRunner:
|
||||
"unittest -k option."
|
||||
),
|
||||
)
|
||||
if PY312:
|
||||
parser.add_argument(
|
||||
"--durations",
|
||||
dest="durations",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="N",
|
||||
help="Show the N slowest test cases (N=0 for all).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--durations",
|
||||
dest="durations",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="N",
|
||||
help="Show the N slowest test cases (N=0 for all).",
|
||||
)
|
||||
|
||||
@property
|
||||
def shuffle_seed(self):
|
||||
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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,22 +567,19 @@ 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
|
||||
)
|
||||
_, _, start_column, end_column = next(
|
||||
itertools.islice(
|
||||
tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None
|
||||
)
|
||||
if start_column and end_column:
|
||||
underline = "^" * (end_column - start_column)
|
||||
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()))
|
||||
)
|
||||
tb_area_colno = f"\n{tb_area_spaces}{underline}"
|
||||
)
|
||||
if start_column and end_column:
|
||||
underline = "^" * (end_column - start_column)
|
||||
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()))
|
||||
)
|
||||
tb_area_colno = f"\n{tb_area_spaces}{underline}"
|
||||
yield {
|
||||
"exc_cause": exc_cause,
|
||||
"exc_cause_explicit": exc_cause_explicit,
|
||||
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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``
|
||||
--------------
|
||||
|
||||
|
@ -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
|
||||
=======================================================
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
self.assertIs(Suit.do_not_call_in_templates, True)
|
||||
|
||||
|
||||
class Separator(bytes, models.Choices):
|
||||
|
@ -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,10 +79,9 @@ 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."]
|
||||
)
|
||||
self.assertEqual(
|
||||
cm.exception.__notes__, ["Pass a 'name' argument to ContentFile."]
|
||||
)
|
||||
|
||||
def test_delete_content_file(self):
|
||||
file = ContentFile(b"", name="foo")
|
||||
|
@ -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,15 +691,14 @@ if __name__ == "__main__":
|
||||
"Same as unittest -k option. Can be used multiple times."
|
||||
),
|
||||
)
|
||||
if PY312:
|
||||
parser.add_argument(
|
||||
"--durations",
|
||||
dest="durations",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="N",
|
||||
help="Show the N slowest test cases (N=0 for all).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--durations",
|
||||
dest="durations",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="N",
|
||||
help="Show the N slowest test cases (N=0 for all).",
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
|
@ -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 """
|
||||
|
@ -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)
|
||||
|
@ -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.assertEqual(len(events), 5)
|
||||
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)
|
||||
|
@ -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)
|
||||
|
@ -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.",
|
||||
)
|
||||
|
||||
|
||||
|
@ -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,8 +15,7 @@ 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))
|
||||
self.assertEqual(parse_date("20120423"), date(2012, 4, 23))
|
||||
# Invalid inputs
|
||||
self.assertIsNone(parse_date("2012423"))
|
||||
with self.assertRaises(ValueError):
|
||||
@ -26,8 +24,7 @@ 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("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))
|
||||
self.assertEqual(parse_time("10:20:30,400"), time(10, 20, 30, 400000))
|
||||
|
@ -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__"
|
||||
error_type = TypeError
|
||||
msg = type_msg
|
||||
|
||||
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
|
||||
|
@ -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:
|
||||
|
2
tox.ini
2
tox.ini
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user