Skip to content

Commit 9e32e17

Browse files
authored
Merge pull request pypa#1371 from sinoroc/add-modernize-setup-py-project
Add page "How to modernize a setup.py project?"
2 parents 54d4260 + ae74041 commit 9e32e17

File tree

4 files changed

+254
-8
lines changed

4 files changed

+254
-8
lines changed

source/discussions/setup-py-deprecated.rst

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,6 @@ Although the usage of :file:`setup.py` as an executable script is deprecated,
105105
its usage as a configuration file for setuptools is absolutely fine.
106106
There is likely no modification needed in :file:`setup.py`.
107107

108-
.. todo::
109-
110-
Link to a "How to modernize a setup.py based project?"
111-
112108

113109
Is ``pyproject.toml`` mandatory?
114110
================================
@@ -124,10 +120,7 @@ at the root of its source tree with a content like this:
124120
build-backend = "setuptools.build_meta"
125121
126122
127-
.. todo::
128-
129-
Link to "How to modernize a setup.py based project?"
130-
123+
The guide :ref:`modernize-setup-py-project` has more details about this.
131124

132125
The standard fallback behavior for a :term:`build frontend <Build Frontend>`
133126
in the absence of a :file:`pyproject.toml` file and its ``[build-system]`` table
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
.. _modernize-setup-py-project:
2+
3+
4+
==============================================
5+
How to modernize a ``setup.py`` based project?
6+
==============================================
7+
8+
9+
Should ``pyproject.toml`` be added?
10+
===================================
11+
12+
A :term:`pyproject.toml` file is strongly recommended.
13+
The presence of a :file:`pyproject.toml` file itself does not bring much. [#]_
14+
What is actually strongly recommended is the ``[build-system]`` table in :file:`pyproject.toml`.
15+
16+
.. [#] Note that it has influence on the build isolation feature of *pip*,
17+
see below.
18+
19+
20+
Should ``setup.py`` be deleted?
21+
===============================
22+
23+
No, :file:`setup.py` can exist in a modern :ref:`setuptools` based project.
24+
The :term:`setup.py` file is a valid configuration file for setuptools
25+
that happens to be written in Python.
26+
However, the following commands are deprecated and **MUST NOT** be run anymore,
27+
and their recommended replacement commands can be used instead:
28+
29+
+---------------------------------+----------------------------------------+
30+
| Deprecated | Current recommendation |
31+
+=================================+========================================+
32+
| ``python setup.py install`` | ``python -m pip install .`` |
33+
+---------------------------------+----------------------------------------+
34+
| ``python setup.py develop`` | ``python -m pip install --editable .`` |
35+
+---------------------------------+----------------------------------------+
36+
| ``python setup.py sdist`` | ``python -m build`` |
37+
+---------------------------------+ |
38+
| ``python setup.py bdist_wheel`` | |
39+
+---------------------------------+----------------------------------------+
40+
41+
42+
For more details:
43+
44+
* :ref:`setup-py-deprecated`
45+
46+
47+
Where to start?
48+
===============
49+
50+
The :term:`project` must contain a :file:`pyproject.toml` file at the root of its source tree
51+
that contains a ``[build-system]`` table like so:
52+
53+
.. code:: toml
54+
55+
[build-system]
56+
requires = ["setuptools"]
57+
build-backend = "setuptools.build_meta"
58+
59+
60+
This is the standardized method of letting :term:`build frontends <Build Frontend>` know
61+
that :ref:`setuptools` is the :term:`build backend <Build Backend>` for this project.
62+
63+
Note that the presence of a :file:`pyproject.toml` file (even if empty)
64+
triggers :ref:`pip` to change its default behavior to use *build isolation*.
65+
66+
For more details:
67+
68+
* :ref:`distributing-packages`
69+
* :ref:`declaring-build-dependencies`
70+
* :doc:`pip:reference/build-system/pyproject-toml`
71+
72+
73+
How to handle additional build-time dependencies?
74+
=================================================
75+
76+
On top of setuptools itself,
77+
if :file:`setup.py` depends on other third-party libraries (outside of Python's standard library),
78+
those must be listed in the ``requires`` list of the ``[build-system]`` table,
79+
so that the build frontend knows to install them
80+
when building the :term:`distributions <Distribution Package>`.
81+
82+
For example, a :file:`setup.py` file such as this:
83+
84+
.. code:: python
85+
86+
import setuptools
87+
import some_build_toolkit # comes from the `some-build-toolkit` library
88+
89+
def get_version():
90+
version = some_build_toolkit.compute_version()
91+
return version
92+
93+
setuptools.setup(
94+
name="my-project",
95+
version=get_version(),
96+
)
97+
98+
99+
requires a :file:`pyproject.toml` file like this (:file:`setup.py` stays unchanged):
100+
101+
.. code:: toml
102+
103+
[build-system]
104+
requires = [
105+
"setuptools",
106+
"some-build-toolkit",
107+
]
108+
build-backend = "setuptools.build_meta"
109+
110+
111+
For more details:
112+
113+
* :ref:`declaring-build-dependencies`
114+
115+
116+
What is the build isolation feature?
117+
====================================
118+
119+
Build frontends typically create an ephemeral virtual environment
120+
where they install only the build dependencies (and their dependencies)
121+
that are listed under ``build-sytem.requires``
122+
and trigger the build in that environment.
123+
124+
For some projects this isolation is unwanted and it can be deactivated as follows:
125+
126+
* ``python -m build --no-isolation``
127+
* ``python -m install --no-build-isolation``
128+
129+
For more details:
130+
131+
* :doc:`pip:reference/build-system/pyproject-toml`
132+
133+
134+
How to handle packaging metadata?
135+
=================================
136+
137+
All static metadata can optionally be moved to a ``[project]`` table in :file:`pyproject.toml`.
138+
139+
For example, a :file:`setup.py` file such as this:
140+
141+
.. code:: python
142+
143+
import setuptools
144+
145+
setuptools.setup(
146+
name="my-project",
147+
version="1.2.3",
148+
)
149+
150+
151+
can be entirely replaced by a :file:`pyproject.toml` file like this:
152+
153+
.. code:: toml
154+
155+
[build-system]
156+
requires = ["setuptools"]
157+
build-backend = "setuptools.build_meta"
158+
159+
[project]
160+
name = "my-project"
161+
version = "1.2.3"
162+
163+
164+
Read :ref:`declaring-project-metadata` for the full specification
165+
of the content allowed in the ``[project]`` table.
166+
167+
168+
How to handle dynamic metadata?
169+
===============================
170+
171+
If some packaging metadata fields are not static
172+
they need to be listed as ``dynamic`` in this ``[project]`` table.
173+
174+
For example, a :file:`setup.py` file such as this:
175+
176+
.. code:: python
177+
178+
import setuptools
179+
import some_build_toolkit
180+
181+
def get_version():
182+
version = some_build_toolkit.compute_version()
183+
return version
184+
185+
setuptools.setup(
186+
name="my-project",
187+
version=get_version(),
188+
)
189+
190+
191+
can be modernized as follows:
192+
193+
.. code:: toml
194+
195+
[build-system]
196+
requires = [
197+
"setuptools",
198+
"some-build-toolkit",
199+
]
200+
build-backend = "setuptools.build_meta"
201+
202+
[project]
203+
name = "my-project"
204+
dynamic = ["version"]
205+
206+
207+
.. code:: python
208+
209+
import setuptools
210+
import some_build_toolkit
211+
212+
def get_version():
213+
version = some_build_toolkit.compute_version()
214+
return version
215+
216+
setuptools.setup(
217+
version=get_version(),
218+
)
219+
220+
221+
For more details:
222+
223+
* :ref:`declaring-project-metadata-dynamic`
224+
225+
226+
What if something that can not be changed expects a ``setup.py`` file?
227+
======================================================================
228+
229+
For example, a process exists that can not be changed easily
230+
and it needs to execute a command such as ``python setup.py --name``.
231+
232+
It is perfectly fine to leave a :file:`setup.py` file in the project source tree
233+
even after all its content has been moved to :file:`pyproject.toml`.
234+
This file can be as minimalistic as this:
235+
236+
.. code:: python
237+
238+
import setuptools
239+
240+
setuptools.setup()
241+
242+
243+
Where to read more about this?
244+
==============================
245+
246+
* :ref:`declaring-build-dependencies`
247+
* :ref:`declaring-project-metadata`
248+
* :doc:`pip:reference/build-system/pyproject-toml`
249+
* :doc:`setuptools:build_meta`

source/guides/section-build-and-publish.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ Building and Publishing
1515
using-testpypi
1616
making-a-pypi-friendly-readme
1717
publishing-package-distribution-releases-using-github-actions-ci-cd-workflows
18+
modernize-setup-py-project

source/specifications/declaring-project-metadata.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ metadata.
379379
"click",
380380
]
381381
382+
383+
.. _declaring-project-metadata-dynamic:
384+
382385
``dynamic``
383386
-----------
384387

0 commit comments

Comments
 (0)