diff --git a/.flake8 b/.flake8
new file mode 100644
index 00000000000..c4094af4621
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,9 @@
+[flake8]
+exclude = build,.git,.tox,./tests/.env
+extend-ignore = E203
+max-line-length = 88
+per-file-ignores =
+ django/core/cache/backends/filebased.py:W601
+ django/core/cache/backends/base.py:W601
+ django/core/cache/backends/redis.py:W601
+ tests/cache/tests.py:W601
diff --git a/MANIFEST.in b/MANIFEST.in
index 3eacc186044..63c16094314 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -13,5 +13,4 @@ graft extras
graft js_tests
graft scripts
graft tests
-global-exclude __pycache__
global-exclude *.py[co]
diff --git a/docs/internals/contributing/writing-code/coding-style.txt b/docs/internals/contributing/writing-code/coding-style.txt
index 7f825da90ab..73c120f71e6 100644
--- a/docs/internals/contributing/writing-code/coding-style.txt
+++ b/docs/internals/contributing/writing-code/coding-style.txt
@@ -46,7 +46,7 @@ Python style
* Unless otherwise specified, follow :pep:`8`.
Use :pypi:`flake8` to check for problems in this area. Note that our
- ``setup.cfg`` file contains some excluded files (deprecated modules we don't
+ ``.flake8`` file contains some excluded files (deprecated modules we don't
care about cleaning up and some third-party code that Django vendors) as well
as some excluded errors that we don't consider as gross violations. Remember
that :pep:`8` is only a guide, so respect the style of the surrounding code
diff --git a/docs/internals/howto-release-django.txt b/docs/internals/howto-release-django.txt
index 4fb0df4c732..c0a8ab8ab14 100644
--- a/docs/internals/howto-release-django.txt
+++ b/docs/internals/howto-release-django.txt
@@ -83,7 +83,7 @@ permissions.
.. code-block:: shell
- $ python -m pip install wheel twine
+ $ python -m pip install build twine
* Access to `Django's project on PyPI `_ to
upload binaries, ideally with extra permissions to `yank a release
@@ -345,10 +345,11 @@ issuing **multiple releases**, repeat these steps for each release.
<2719a7f8c161233f45d34b624a9df9392c86cc1b>`).
#. If this is a pre-release package also update the "Development Status"
- trove classifier in ``setup.cfg`` to reflect this. An ``rc`` pre-release
- should not change the trove classifier (:commit:`example commit for alpha
- release `, :commit:`example
- commit for beta release <25fec8940b24107e21314ab6616e18ce8dec1c1c>`).
+ trove classifier in ``pyproject.toml`` to reflect this. An ``rc``
+ pre-release should not change the trove classifier (:commit:`example
+ commit for alpha release `,
+ :commit:`example commit for beta release
+ <25fec8940b24107e21314ab6616e18ce8dec1c1c>`).
#. Otherwise, make sure the classifier is set to
``Development Status :: 5 - Production/Stable``.
@@ -370,8 +371,8 @@ issuing **multiple releases**, repeat these steps for each release.
#. Make sure you have an absolutely clean tree by running ``git clean -dfx``.
-#. Run ``make -f extras/Makefile`` to generate the release packages. This will
- create the release packages in a ``dist/`` directory.
+#. Run ``python -m build`` to generate the release packages. This will create
+ the release packages in a ``dist/`` directory.
#. Generate the hashes of the release packages:
diff --git a/docs/topics/auth/passwords.txt b/docs/topics/auth/passwords.txt
index 4d5f845a57f..68f5453d54f 100644
--- a/docs/topics/auth/passwords.txt
+++ b/docs/topics/auth/passwords.txt
@@ -97,7 +97,7 @@ To use Argon2id as your default storage algorithm, do the following:
#. Install the :pypi:`argon2-cffi` package. This can be done by running
``python -m pip install django[argon2]``, which is equivalent to
``python -m pip install argon2-cffi`` (along with any version requirement
- from Django's ``setup.cfg``).
+ from Django's ``pyproject.toml``).
#. Modify :setting:`PASSWORD_HASHERS` to list ``Argon2PasswordHasher`` first.
That is, in your settings file, you'd put::
@@ -128,7 +128,7 @@ To use Bcrypt as your default storage algorithm, do the following:
#. Install the :pypi:`bcrypt` package. This can be done by running
``python -m pip install django[bcrypt]``, which is equivalent to
``python -m pip install bcrypt`` (along with any version requirement from
- Django's ``setup.cfg``).
+ Django's ``pyproject.toml``).
#. Modify :setting:`PASSWORD_HASHERS` to list ``BCryptSHA256PasswordHasher``
first. That is, in your settings file, you'd put::
diff --git a/extras/Makefile b/extras/Makefile
deleted file mode 100644
index 66efd0d4519..00000000000
--- a/extras/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-all: sdist bdist_wheel
-
-sdist:
- python setup.py sdist
-
-bdist_wheel:
- python setup.py bdist_wheel
-
-.PHONY : sdist bdist_wheel
diff --git a/pyproject.toml b/pyproject.toml
index f8632ac3cea..51c4a311731 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,12 +1,68 @@
[build-system]
-requires = ['setuptools>=40.8.0']
-build-backend = 'setuptools.build_meta'
+requires = ["setuptools>=61.0.0,<69.3.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "Django"
+dynamic = ["version"]
+requires-python = ">= 3.10"
+dependencies = [
+ "asgiref>=3.7.0",
+ "sqlparse>=0.3.1",
+ "tzdata; sys_platform == 'win32'",
+]
+authors = [
+ {name = "Django Software Foundation", email = "foundation@djangoproject.com"},
+]
+description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
+readme = "README.rst"
+license = {text = "BSD-3-Clause"}
+classifiers = [
+ "Development Status :: 2 - Pre-Alpha",
+ "Environment :: Web Environment",
+ "Framework :: Django",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Operating System :: OS Independent",
+ "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",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+ "Topic :: Internet :: WWW/HTTP :: WSGI",
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+
+[project.optional-dependencies]
+argon2 = ["argon2-cffi>=19.1.0"]
+bcrypt = ["bcrypt"]
+
+[project.scripts]
+django-admin = "django.core.management:execute_from_command_line"
+
+[project.urls]
+Homepage = "https://www.djangoproject.com/"
+Documentation = "https://docs.djangoproject.com/"
+"Release notes" = "https://docs.djangoproject.com/en/stable/releases/"
+Funding = "https://www.djangoproject.com/fundraising/"
+Source = "https://github.com/django/django"
+Tracker = "https://code.djangoproject.com/"
[tool.black]
-target-version = ['py310']
-force-exclude = 'tests/test_runner_apps/tagged/tests_syntax_error.py'
+target-version = ["py310"]
+force-exclude = "tests/test_runner_apps/tagged/tests_syntax_error.py"
[tool.isort]
-profile = 'black'
-default_section = 'THIRDPARTY'
-known_first_party = 'django'
+profile = "black"
+default_section = "THIRDPARTY"
+known_first_party = "django"
+
+[tool.setuptools.dynamic]
+version = {attr = "django.__version__"}
+
+[tool.setuptools.packages.find]
+include = ["django*"]
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 29814e54e6b..00000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,61 +0,0 @@
-[metadata]
-name = Django
-version = attr: django.__version__
-url = https://www.djangoproject.com/
-author = Django Software Foundation
-author_email = foundation@djangoproject.com
-description = A high-level Python web framework that encourages rapid development and clean, pragmatic design.
-long_description = file: README.rst
-license = BSD-3-Clause
-classifiers =
- Development Status :: 2 - Pre-Alpha
- Environment :: Web Environment
- Framework :: Django
- Intended Audience :: Developers
- License :: OSI Approved :: BSD License
- Operating System :: OS Independent
- 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
- Topic :: Internet :: WWW/HTTP
- Topic :: Internet :: WWW/HTTP :: Dynamic Content
- Topic :: Internet :: WWW/HTTP :: WSGI
- Topic :: Software Development :: Libraries :: Application Frameworks
- Topic :: Software Development :: Libraries :: Python Modules
-project_urls =
- Documentation = https://docs.djangoproject.com/
- Release notes = https://docs.djangoproject.com/en/stable/releases/
- Funding = https://www.djangoproject.com/fundraising/
- Source = https://github.com/django/django
- Tracker = https://code.djangoproject.com/
-
-[options]
-python_requires = >=3.10
-packages = find:
-include_package_data = true
-zip_safe = false
-install_requires =
- asgiref >= 3.7.0
- sqlparse >= 0.3.1
- tzdata; sys_platform == 'win32'
-
-[options.entry_points]
-console_scripts =
- django-admin = django.core.management:execute_from_command_line
-
-[options.extras_require]
-argon2 = argon2-cffi >= 19.1.0
-bcrypt = bcrypt
-
-[flake8]
-exclude = build,.git,.tox,./tests/.env
-extend-ignore = E203
-max-line-length = 88
-per-file-ignores =
- django/core/cache/backends/filebased.py:W601
- django/core/cache/backends/base.py:W601
- django/core/cache/backends/redis.py:W601
- tests/cache/tests.py:W601
diff --git a/setup.py b/setup.py
deleted file mode 100644
index ef91130d473..00000000000
--- a/setup.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import os
-import site
-import sys
-from distutils.sysconfig import get_python_lib
-
-from setuptools import setup
-
-# Allow editable install into user site directory.
-# See https://github.com/pypa/pip/issues/7953.
-site.ENABLE_USER_SITE = "--user" in sys.argv[1:]
-
-# Warn if we are installing over top of an existing installation. This can
-# cause issues where files that were deleted from a more recent Django are
-# still present in site-packages. See #18115.
-overlay_warning = False
-if "install" in sys.argv:
- lib_paths = [get_python_lib()]
- if lib_paths[0].startswith("/usr/lib/"):
- # We have to try also with an explicit prefix of /usr/local in order to
- # catch Debian's custom user site-packages directory.
- lib_paths.append(get_python_lib(prefix="/usr/local"))
- for lib_path in lib_paths:
- existing_path = os.path.abspath(os.path.join(lib_path, "django"))
- if os.path.exists(existing_path):
- # We note the need for the warning here, but present it after the
- # command is run, so it's more likely to be seen.
- overlay_warning = True
- break
-
-
-setup()
-
-
-if overlay_warning:
- sys.stderr.write(
- """
-
-========
-WARNING!
-========
-
-You have just installed Django over top of an existing
-installation, without removing it first. Because of this,
-your install may now include extraneous files from a
-previous version that have since been removed from
-Django. This is known to cause a variety of problems. You
-should manually remove the
-
-%(existing_path)s
-
-directory and re-install Django.
-
-"""
- % {"existing_path": existing_path}
- )