1.. _pyproject.toml config:
2
3-----------------------------------------------------
4Configuring setuptools using ``pyproject.toml`` files
5-----------------------------------------------------
6
7.. note:: New in 61.0.0 (**experimental**)
8
9.. warning::
10   Support for declaring :doc:`project metadata
11   <PyPUG:specifications/declaring-project-metadata>` or configuring
12   ``setuptools`` via ``pyproject.toml`` files is still experimental and might
13   change (or be removed) in future releases.
14
15.. important::
16   For the time being, ``pip`` still might require a ``setup.py`` file
17   to support :doc:`editable installs <pip:cli/pip_install>`.
18
19   A simple script will suffice, for example:
20
21   .. code-block:: python
22
23       from setuptools import setup
24
25       setup()
26
27Starting with :pep:`621`, the Python community selected ``pyproject.toml`` as
28a standard way of specifying *project metadata*.
29``Setuptools`` has adopted this standard and will use the information contained
30in this file as an input in the build process.
31
32The example below illustrates how to write a ``pyproject.toml`` file that can
33be used with ``setuptools``. It contains two TOML tables (identified by the
34``[table-header]`` syntax): ``build-system`` and ``project``.
35The ``build-system`` table is used to tell the build frontend (e.g.
36:pypi:`build` or :pypi:`pip`) to use ``setuptools`` and any other plugins (e.g.
37``setuptools-scm``) to build the package.
38The ``project`` table contains metadata fields as described by
39:doc:`PyPUG:specifications/declaring-project-metadata` guide.
40
41.. _example-pyproject-config:
42
43.. code-block:: toml
44
45   [build-system]
46   requires = ["setuptools", "setuptools-scm"]
47   build-backend = "setuptools.build_meta"
48
49   [project]
50   name = "my_package"
51   description = "My package description"
52   readme = "README.rst"
53   keywords = ["one", "two"]
54   license = {text = "BSD 3-Clause License"}
55   classifiers = [
56       "Framework :: Django",
57       "Programming Language :: Python :: 3",
58   ]
59   dependencies = [
60       "requests",
61       'importlib-metadata; python_version<"3.8"',
62   ]
63   dynamic = ["version"]
64
65   [project.optional-dependencies]
66   pdf = ["ReportLab>=1.2", "RXP"]
67   rest = ["docutils>=0.3", "pack ==1.1, ==1.3"]
68
69   [project.scripts]
70   my-script = "my_package.module:function"
71
72
73.. _setuptools-table:
74
75Setuptools-specific configuration
76=================================
77
78While the standard ``project`` table in the ``pyproject.toml`` file covers most
79of the metadata used during the packaging process, there are still some
80``setuptools``-specific configurations that can be set by users that require
81customization.
82These configurations are completely optional and probably can be skipped when
83creating simple packages.
84They are equivalent to the :doc:`/references/keywords` used by the ``setup.py``
85file, and can be set via the ``tool.setuptools`` table:
86
87========================= =========================== =========================
88Key                       Value Type (TOML)           Notes
89========================= =========================== =========================
90``platforms``             array
91``zip-safe``              boolean                     If not specified, ``setuptools`` will try to guess
92                                                      a reasonable default for the package
93``eager-resources``       array
94``py-modules``            array                       See tip below
95``packages``              array or ``find`` directive See tip below
96``package-dir``           table/inline-table          Used when explicitly listing ``packages``
97``namespace-packages``    array                       Not necessary if you use :pep:`420`
98``package-data``          table/inline-table          See :doc:`/userguide/datafiles`
99``include-package-data``  boolean                     ``True`` by default
100``exclude-package-data``  table/inline-table
101``license-files``         array of glob patterns      **Provisional** - likely to change with :pep:`639`
102                                                      (by default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``)
103``data-files``            table/inline-table          **Deprecated** - check :doc:`/userguide/datafiles`
104``script-files``          array                       **Deprecated** - equivalent to the ``script`` keyword in ``setup.py``
105                                                      (should be avoided in favour of ``project.scripts``)
106``provides``              array                       **Ignored by pip**
107``obsoletes``             array                       **Ignored by pip**
108========================= =========================== =========================
109
110.. note::
111   The `TOML value types`_ ``array`` and ``table/inline-table`` are roughly
112   equivalent to the Python's :obj:`dict` and :obj:`list` data types.
113
114Please note that some of these configurations are deprecated or at least
115discouraged, but they are made available to ensure portability.
116New packages should avoid relying on deprecated/discouraged fields, and
117existing packages should consider alternatives.
118
119.. tip::
120   When both ``py-modules`` and ``packages`` are left unspecified,
121   ``setuptools`` will attempt to perform :ref:`auto-discovery`, which should
122   cover most popular project directory organization techniques, such as the
123   :ref:`src-layout` and the :ref:`flat-layout`.
124
125   However if your project does not follow these conventional layouts
126   (e.g. you want to use a ``flat-layout`` but at the same time have custom
127   directories at the root of your project), you might need to use the ``find``
128   directive [#directives]_ as shown below:
129
130   .. code-block:: toml
131
132      [tool.setuptools.packages.find]
133      where = ["src"]  # list of folders that contain the packages (["."] by default)
134      include = ["my_package*"]  # package names should match these glob patterns (["*"] by default)
135      exclude = ["my_package.tests*"]  # exclude packages matching these glob patterns (empty by default)
136      namespaces = false  # to disable scanning PEP 420 namespaces (true by default)
137
138   Note that the glob patterns in the example above need to be matched
139   by the **entire** package name. This means that if you specify ``exclude = ["tests"]``,
140   modules like ``tests.my_package.test1`` will still be included in the distribution
141   (to remove them, add a wildcard to the end of the pattern: ``"tests*"``).
142
143   Alternatively, you can explicitly list the packages in modules:
144
145   .. code-block:: toml
146
147      [tool.setuptools]
148      packages = ["my_package"]
149
150
151.. _dynamic-pyproject-config:
152
153Dynamic Metadata
154================
155
156Note that in the first example of this page we use ``dynamic`` to identify
157which metadata fields are dynamically computed during the build by either
158``setuptools`` itself or the plugins installed via ``build-system.requires``
159(e.g. ``setuptools-scm`` is capable of deriving the current project version
160directly from the ``git`` :wiki:`version control` system).
161
162Currently the following fields can be listed as dynamic: ``version``,
163``classifiers``, ``description``, ``entry-points``, ``scripts``,
164``gui-scripts`` and ``readme``.
165When these fields are expected to be provided by ``setuptools`` a
166corresponding entry is required in the ``tool.setuptools.dynamic`` table
167[#entry-points]_. For example:
168
169.. code-block:: toml
170
171   # ...
172   [project]
173   name = "my_package"
174   dynamic = ["version", "readme"]
175   # ...
176   [tool.setuptools.dynamic]
177   version = {attr = "my_package.VERSION"}
178   readme = {file = ["README.rst", "USAGE.rst"]}
179
180In the ``dynamic`` table, the ``attr`` directive [#directives]_ will read an
181attribute from the given module [#attr]_, while ``file`` will read the contents
182of all given files and concatenate them in a single string.
183
184================= =================== =========================
185Key               Directive           Notes
186================= =================== =========================
187``version``       ``attr``, ``file``
188``readme``        ``file``
189``description``   ``file``            One-line text
190``classifiers``   ``file``            Multi-line text with one classifier per line
191``entry-points``  ``file``            INI format following :doc:`PyPUG:specifications/entry-points`
192                                      (``console_scripts`` and ``gui_scripts`` can be included)
193================= =================== =========================
194
195----
196
197.. rubric:: Notes
198
199.. [#entry-points] Dynamic ``scripts`` and ``gui-scripts`` are a special case.
200   When resolving these metadata keys, ``setuptools`` will look for
201   ``tool.setuptool.dynamic.entry-points``, and use the values of the
202   ``console_scripts`` and ``gui_scripts`` :doc:`entry-point groups
203   <PyPUG:specifications/entry-points>`.
204
205.. [#directives] In the context of this document, *directives* are special TOML
206   values that are interpreted differently by ``setuptools`` (usually triggering an
207   associated function). Most of the times they correspond to a special TOML table
208   (or inline-table) with a single top-level key.
209   For example, you can have the ``{find = {where = ["src"], exclude=["tests*"]}}``
210   directive for ``tool.setuptools.packages``, or ``{attr = "mymodule.attr"}``
211   directive for ``tool.setuptools.dynamic.version``.
212
213.. [#attr] ``attr`` is meant to be used when the module attribute is statically
214   specified (e.g. as a string, list or tuple). As a rule of thumb, the
215   attribute should be able to be parsed with :func:`ast.literal_eval`, and
216   should not be modified or re-assigned.
217
218.. _TOML value types: https://toml.io/en/v1.0.0
219