Skip to content

Commit f5772de

Browse files
felixxmsarahboyce
authored andcommitted
Fixed #36005 -- Dropped support for Python 3.10 and 3.11.
1 parent 61dae11 commit f5772de

File tree

30 files changed

+79
-266
lines changed

30 files changed

+79
-266
lines changed

.github/workflows/schedule_tests.yml

-60
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ jobs:
1616
strategy:
1717
matrix:
1818
python-version:
19-
- '3.10'
20-
- '3.11'
2119
- '3.12'
2220
- '3.13'
2321
- '3.14-dev'
@@ -64,64 +62,6 @@ jobs:
6462
- name: Run tests
6563
run: python -Wall tests/runtests.py --verbosity=2
6664

67-
pypy-sqlite:
68-
runs-on: ubuntu-latest
69-
name: Ubuntu, SQLite, PyPy3.10
70-
continue-on-error: true
71-
steps:
72-
- name: Checkout
73-
uses: actions/checkout@v4
74-
- name: Set up Python
75-
uses: actions/setup-python@v5
76-
with:
77-
python-version: pypy-3.10-nightly
78-
cache: 'pip'
79-
cache-dependency-path: 'tests/requirements/py3.txt'
80-
- name: Install libmemcached-dev for pylibmc
81-
run: sudo apt-get install libmemcached-dev
82-
- name: Install and upgrade packaging tools
83-
run: python -m pip install --upgrade pip setuptools wheel
84-
- run: python -m pip install -r tests/requirements/py3.txt -e .
85-
- name: Run tests
86-
run: python -Wall tests/runtests.py --verbosity=2
87-
88-
pypy-postgresql:
89-
runs-on: ubuntu-latest
90-
name: Ubuntu, PostgreSQL, PyPy3.10
91-
continue-on-error: true
92-
services:
93-
postgres:
94-
image: postgres:14-alpine
95-
env:
96-
POSTGRES_DB: django
97-
POSTGRES_USER: user
98-
POSTGRES_PASSWORD: postgres
99-
ports:
100-
- 5432:5432
101-
options: >-
102-
--health-cmd pg_isready
103-
--health-interval 10s
104-
--health-timeout 5s
105-
--health-retries 5
106-
steps:
107-
- name: Checkout
108-
uses: actions/checkout@v4
109-
- name: Set up Python
110-
uses: actions/setup-python@v5
111-
with:
112-
python-version: pypy-3.10-nightly
113-
cache: 'pip'
114-
cache-dependency-path: 'tests/requirements/py3.txt'
115-
- name: Install libmemcached-dev for pylibmc
116-
run: sudo apt-get install libmemcached-dev
117-
- name: Install and upgrade packaging tools
118-
run: python -m pip install --upgrade pip setuptools wheel
119-
- run: python -m pip install -r tests/requirements/py3.txt -r tests/requirements/postgres.txt -e .
120-
- name: Create PostgreSQL settings file
121-
run: mv ./.github/workflows/data/test_postgres.py.tpl ./tests/test_postgres.py
122-
- name: Run tests
123-
run: python -Wall tests/runtests.py --settings=test_postgres --verbosity=2
124-
12565
javascript-tests:
12666
runs-on: ubuntu-latest
12767
name: JavaScript tests

.github/workflows/screenshots.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Set up Python
2525
uses: actions/setup-python@v5
2626
with:
27-
python-version: '3.11'
27+
python-version: '3.13'
2828
cache: 'pip'
2929
cache-dependency-path: 'tests/requirements/py3.txt'
3030
- name: Install and upgrade packaging tools

INSTALL

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Thanks for downloading Django.
22

3-
To install it, make sure you have Python 3.10 or greater installed. Then run
3+
To install it, make sure you have Python 3.12 or greater installed. Then run
44
this command from the command prompt:
55

66
python -m pip install .

django/db/migrations/serializer.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from django.db.migrations.operations.base import Operation
1717
from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
1818
from django.utils.functional import LazyObject, Promise
19-
from django.utils.version import PY311, get_docs_version
19+
from django.utils.version import get_docs_version
2020

2121
FUNCTION_TYPES = (types.FunctionType, types.BuiltinFunctionType, types.MethodType)
2222

@@ -140,11 +140,7 @@ def serialize(self):
140140
enum_class = self.value.__class__
141141
module = enum_class.__module__
142142
if issubclass(enum_class, enum.Flag):
143-
if PY311:
144-
members = list(self.value)
145-
else:
146-
members, _ = enum._decompose(enum_class, self.value)
147-
members = reversed(members)
143+
members = list(self.value)
148144
else:
149145
members = (self.value,)
150146
return (

django/db/models/enums.py

+3-34
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,8 @@
11
import enum
2+
from enum import EnumType, IntEnum, StrEnum
3+
from enum import property as enum_property
24

35
from django.utils.functional import Promise
4-
from django.utils.version import PY311, PY312
5-
6-
if PY311:
7-
from enum import EnumType, IntEnum, StrEnum
8-
from enum import property as enum_property
9-
else:
10-
from enum import EnumMeta as EnumType
11-
from types import DynamicClassAttribute as enum_property
12-
13-
class ReprEnum(enum.Enum):
14-
def __str__(self):
15-
return str(self.value)
16-
17-
class IntEnum(int, ReprEnum):
18-
pass
19-
20-
class StrEnum(str, ReprEnum):
21-
pass
22-
236

247
__all__ = ["Choices", "IntegerChoices", "TextChoices"]
258

@@ -49,14 +32,6 @@ def __new__(metacls, classname, bases, classdict, **kwds):
4932
member._label_ = label
5033
return enum.unique(cls)
5134

52-
if not PY312:
53-
54-
def __contains__(cls, member):
55-
if not isinstance(member, enum.Enum):
56-
# Allow non-enums to match against member values.
57-
return any(x.value == member for x in cls)
58-
return super().__contains__(member)
59-
6035
@property
6136
def names(cls):
6237
empty = ["__empty__"] if hasattr(cls, "__empty__") else []
@@ -79,13 +54,7 @@ def values(cls):
7954
class Choices(enum.Enum, metaclass=ChoicesType):
8055
"""Class for creating enumerated choices."""
8156

82-
if PY311:
83-
do_not_call_in_templates = enum.nonmember(True)
84-
else:
85-
86-
@property
87-
def do_not_call_in_templates(self):
88-
return True
57+
do_not_call_in_templates = enum.nonmember(True)
8958

9059
@enum_property
9160
def label(self):

django/db/models/fields/files.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from django.db.models.query_utils import DeferredAttribute
1515
from django.db.models.utils import AltersData
1616
from django.utils.translation import gettext_lazy as _
17-
from django.utils.version import PY311
1817

1918

2019
class FieldFile(File, AltersData):
@@ -329,7 +328,7 @@ def pre_save(self, model_instance, add):
329328
f"File for {self.name} must have "
330329
"the name attribute specified to be saved."
331330
)
332-
if PY311 and isinstance(file._file, ContentFile):
331+
if isinstance(file._file, ContentFile):
333332
exc.add_note("Pass a 'name' argument to ContentFile.")
334333
raise exc
335334

django/test/runner.py

+10-12
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from django.test.utils import teardown_databases as _teardown_databases
2929
from django.test.utils import teardown_test_environment
3030
from django.utils.datastructures import OrderedSet
31-
from django.utils.version import PY312, PY313
31+
from django.utils.version import PY313
3232

3333
try:
3434
import ipdb as pdb
@@ -829,15 +829,14 @@ def add_arguments(cls, parser):
829829
"unittest -k option."
830830
),
831831
)
832-
if PY312:
833-
parser.add_argument(
834-
"--durations",
835-
dest="durations",
836-
type=int,
837-
default=None,
838-
metavar="N",
839-
help="Show the N slowest test cases (N=0 for all).",
840-
)
832+
parser.add_argument(
833+
"--durations",
834+
dest="durations",
835+
type=int,
836+
default=None,
837+
metavar="N",
838+
help="Show the N slowest test cases (N=0 for all).",
839+
)
841840

842841
@property
843842
def shuffle_seed(self):
@@ -1005,9 +1004,8 @@ def get_test_runner_kwargs(self):
10051004
"resultclass": self.get_resultclass(),
10061005
"verbosity": self.verbosity,
10071006
"buffer": self.buffer,
1007+
"durations": self.durations,
10081008
}
1009-
if PY312:
1010-
kwargs["durations"] = self.durations
10111009
return kwargs
10121010

10131011
def run_checks(self, databases):

django/test/testcases.py

-25
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
override_settings,
5555
)
5656
from django.utils.functional import classproperty
57-
from django.utils.version import PY311
5857
from django.views.static import serve
5958

6059
logger = logging.getLogger("django.test")
@@ -71,24 +70,6 @@
7170
__unittest = True
7271

7372

74-
if not PY311:
75-
# Backport of unittest.case._enter_context() from Python 3.11.
76-
def _enter_context(cm, addcleanup):
77-
# Look up the special methods on the type to match the with statement.
78-
cls = type(cm)
79-
try:
80-
enter = cls.__enter__
81-
exit = cls.__exit__
82-
except AttributeError:
83-
raise TypeError(
84-
f"'{cls.__module__}.{cls.__qualname__}' object does not support the "
85-
f"context manager protocol"
86-
) from None
87-
result = enter(cm)
88-
addcleanup(exit, cm, None, None, None)
89-
return result
90-
91-
9273
def to_list(value):
9374
"""Put value into a list if it's not already one."""
9475
if not isinstance(value, list):
@@ -398,12 +379,6 @@ def _post_teardown(self):
398379
"""Perform post-test things."""
399380
pass
400381

401-
if not PY311:
402-
# Backport of unittest.TestCase.enterClassContext() from Python 3.11.
403-
@classmethod
404-
def enterClassContext(cls, cm):
405-
return _enter_context(cm, cls.addClassCleanup)
406-
407382
def settings(self, **kwargs):
408383
"""
409384
A context manager that temporarily sets a setting and reverts to the

django/views/debug.py

+13-16
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from django.utils.encoding import force_str
1818
from django.utils.module_loading import import_string
1919
from django.utils.regex_helper import _lazy_re_compile
20-
from django.utils.version import PY311, get_docs_version
20+
from django.utils.version import get_docs_version
2121
from django.views.decorators.debug import coroutine_functions_to_sensitive_variables
2222

2323
# Minimal Django templates engine to render the error templates
@@ -567,22 +567,19 @@ def get_exception_traceback_frames(self, exc_value, tb):
567567
post_context = []
568568

569569
colno = tb_area_colno = ""
570-
if PY311:
571-
_, _, start_column, end_column = next(
572-
itertools.islice(
573-
tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None
574-
)
570+
_, _, start_column, end_column = next(
571+
itertools.islice(
572+
tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None
575573
)
576-
if start_column and end_column:
577-
underline = "^" * (end_column - start_column)
578-
spaces = " " * (start_column + len(str(lineno + 1)) + 2)
579-
colno = f"\n{spaces}{underline}"
580-
tb_area_spaces = " " * (
581-
4
582-
+ start_column
583-
- (len(context_line) - len(context_line.lstrip()))
584-
)
585-
tb_area_colno = f"\n{tb_area_spaces}{underline}"
574+
)
575+
if start_column and end_column:
576+
underline = "^" * (end_column - start_column)
577+
spaces = " " * (start_column + len(str(lineno + 1)) + 2)
578+
colno = f"\n{spaces}{underline}"
579+
tb_area_spaces = " " * (
580+
4 + start_column - (len(context_line) - len(context_line.lstrip()))
581+
)
582+
tb_area_colno = f"\n{tb_area_spaces}{underline}"
586583
yield {
587584
"exc_cause": exc_cause,
588585
"exc_cause_explicit": exc_cause_explicit,

docs/internals/contributing/writing-code/unit-tests.txt

+5-5
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,14 @@ In addition to the default environments, ``tox`` supports running unit tests
9292
for other versions of Python and other database backends. Since Django's test
9393
suite doesn't bundle a settings file for database backends other than SQLite,
9494
however, you must :ref:`create and provide your own test settings
95-
<running-unit-tests-settings>`. For example, to run the tests on Python 3.10
95+
<running-unit-tests-settings>`. For example, to run the tests on Python 3.12
9696
using PostgreSQL:
9797

9898
.. console::
9999

100-
$ tox -e py310-postgres -- --settings=my_postgres_settings
100+
$ tox -e py312-postgres -- --settings=my_postgres_settings
101101

102-
This command sets up a Python 3.10 virtual environment, installs Django's
102+
This command sets up a Python 3.12 virtual environment, installs Django's
103103
test suite dependencies (including those for PostgreSQL), and calls
104104
``runtests.py`` with the supplied arguments (in this case,
105105
``--settings=my_postgres_settings``).
@@ -114,14 +114,14 @@ above:
114114

115115
.. code-block:: console
116116

117-
$ DJANGO_SETTINGS_MODULE=my_postgres_settings tox -e py310-postgres
117+
$ DJANGO_SETTINGS_MODULE=my_postgres_settings tox -e py312-postgres
118118

119119
Windows users should use:
120120

121121
.. code-block:: doscon
122122

123123
...\> set DJANGO_SETTINGS_MODULE=my_postgres_settings
124-
...\> tox -e py310-postgres
124+
...\> tox -e py312-postgres
125125

126126
Running the JavaScript tests
127127
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

docs/intro/reusable-apps.txt

+1-3
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ this. For a small app like polls, this process isn't too difficult.
220220
]
221221
description = "A Django app to conduct web-based polls."
222222
readme = "README.rst"
223-
requires-python = ">= 3.10"
223+
requires-python = ">= 3.12"
224224
authors = [
225225
{name = "Your Name", email = "[email protected]"},
226226
]
@@ -234,8 +234,6 @@ this. For a small app like polls, this process isn't too difficult.
234234
"Programming Language :: Python",
235235
"Programming Language :: Python :: 3",
236236
"Programming Language :: Python :: 3 :: Only",
237-
"Programming Language :: Python :: 3.10",
238-
"Programming Language :: Python :: 3.11",
239237
"Programming Language :: Python :: 3.12",
240238
"Programming Language :: Python :: 3.13",
241239
"Topic :: Internet :: WWW/HTTP",

docs/intro/tutorial01.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ in a shell prompt (indicated by the $ prefix):
2323
If Django is installed, you should see the version of your installation. If it
2424
isn't, you'll get an error telling "No module named django".
2525

26-
This tutorial is written for Django |version|, which supports Python 3.10 and
26+
This tutorial is written for Django |version|, which supports Python 3.12 and
2727
later. If the Django version doesn't match, you can refer to the tutorial for
2828
your version of Django by using the version switcher at the bottom right corner
2929
of this page, or update Django to the newest version. If you're using an older

docs/ref/django-admin.txt

-4
Original file line numberDiff line numberDiff line change
@@ -1583,10 +1583,6 @@ Outputs timings, including database setup and total run time.
15831583

15841584
Shows the N slowest test cases (N=0 for all).
15851585

1586-
.. admonition:: Python 3.12 and later
1587-
1588-
This feature is only available for Python 3.12 and later.
1589-
15901586
``testserver``
15911587
--------------
15921588

0 commit comments

Comments
 (0)