Skip to content

Commit c0c6354

Browse files
committed
replace setup.py
1 parent aa3e854 commit c0c6354

File tree

1 file changed

+105
-71
lines changed

1 file changed

+105
-71
lines changed

doc/source/2_programs_in_files.rst

Lines changed: 105 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ package is shown in :numref:`package-layout`.
443443
│ └── subpackage
444444
│ ├── __init__.py
445445
│ └── module_3.py
446-
└── setup.py
446+
└── pyproject.toml
447447
448448
If you haven't seen a diagram like this before, the names with lines
449449
descending from their first letter are folder names, and the
@@ -486,9 +486,9 @@ they make up the Python package.
486486
containing a file :file:`__init__.py`. It can also contain modules and
487487
further subpackages.
488488

489-
:file:`setup.py`
489+
:file:`pyproject.toml`
490490
This file is outside the package folder and is not
491-
actually a part of the package. The role of :file:`setup.py` will be
491+
actually a part of the package. The role of :file:`pyproject.toml` will be
492492
covered in :numref:`installable_packages`.
493493

494494
.. _importing_packages:
@@ -555,54 +555,66 @@ Making packages installable
555555

556556
In order for the :ref:`import statement <python:import>` to work, Python needs
557557
to know that the package being imported exists, and where to find it. This is
558-
achieved by installing the package. In order to make a package installable, we
559-
need to provide Python with a bit more information about it. This information
560-
can be provided in a Python script which must be called :file:`setup.py`. This
561-
file isn't part of the package and does not go in the package folder. Instead,
562-
it should be placed in the top-level folder of your git repository, so that the
563-
Python package installer will be able to find it.
558+
achieved by installing the package using Pip. In order to make a package
559+
installable, we need to provide Pip with a bit more information about it. The
560+
modern way to provide this information is using a configuration file which must
561+
be called :file:`pyproject.toml`. This file isn't part of the package and does
562+
not go in the package folder. Instead, it should be placed in the top-level
563+
folder of your git repository, so that the Python package installer will be
564+
able to find it.
564565

565-
.. _minimal-setup-py:
566+
.. _minimal-pyproject-toml:
566567

567568
.. code-block:: python3
568-
:caption: A minimal :file:`setup.py` which will make all the Python
569-
packages found in subfolders of the folder containing :file:`setup.py`
570-
installable.
571-
572-
from setuptools import setup, find_packages
573-
setup(
574-
name="my_package",
575-
version="0.1",
576-
packages=find_packages(),
577-
)
578-
579-
:numref:`minimal-setup-py` shows a very basic :file:`setup.py` which uses
580-
`setuptools` to make packages installable. `Setuptools
581-
<https://setuptools.readthedocs.io/en/latest/index.html>`__
582-
is a Python package which exists to help with the packaging and
583-
installation of Python packages. The :func:`~setuptools.setup`
584-
function records metadata such as the installation name to be given to
585-
your whole set of packages, and the version. It also needs to know
586-
about all of the packages in the current repository, but this can be
587-
automated with the :func:`~setuptools.find_packages` function, which
588-
will return a list of folders containing a file named :file:`__init__.py`.
569+
:caption: A minimal :file:`pyproject.toml` which will make all the Python
570+
packages found in subfolders of the folder containing
571+
:file:`pyproject.toml` installable.
572+
573+
[build-system]
574+
requires = ["hatchling"]
575+
build-backend = "hatchling.build"
576+
577+
[project]
578+
name = "my_package"
579+
version = "0.1"
580+
581+
:numref:`minimal-pyproject-toml` shows a very basic :file:`pyproject.toml`.
582+
This isn't a Python file, instead it's a configuration file written in a
583+
language called `TOML <https://toml.io/en/>`__. In our case, the TOML file
584+
comprises two sections, which TOML calls "tables".
585+
586+
The first table is called `build-system`, and enables us to choose which of the
587+
various Python project management packages we wish to use. For our very simple
588+
package we'll use `hatchling` which is part of the Python project management
589+
system `Hatch <https://hatch.pypa.io/>`. There are a number of other packages
590+
we could have used for this, but for our simple purposes it doesn't much matter
591+
which we use. Inside tables, TOML records configuration information as
592+
key-value pairs. There are two keys that we must set in the `build-system`
593+
table. `requires` is a list of packages that Pip should install in order to
594+
build this package. In this case, that is just `hatchling`. The second key we
595+
need is `build-backend`. This is the name of the Python module that will be
596+
used to build the package. For `hatchling` this is the `build` module in so we
597+
write `hatchling.build`.
598+
599+
The `project` table contains information about the Pip package we're creating.
600+
At a minimum, we need to give our Pip package a name and a version number.
589601

590602
.. only:: not book
591603

592-
This very simple :file:`setup.py` will suffice for packages that you only
593-
intend to use yourself. Should you wish to publish packages for use by other
594-
people, then you'll need to provide significantly more information in
595-
:file:`setup.py` and, potentially, in other places too. The canonical guide to
596-
this is the `Python Packaging User Guide
604+
This very simple :file:`pyproject.toml` will suffice for packages that you
605+
only intend to use yourself. Should you wish to publish packages for use by
606+
other people, then you'll need to provide significantly more information in
607+
:file:`pyproject.toml` and, potentially, in other places too. The canonical
608+
guide to this is the `Python Packaging User Guide
597609
<https://packaging.python.org/tutorials/packaging-projects/>`__.
598610

599611
.. only:: book
600612

601-
This very simple :file:`setup.py` will suffice for packages that you only
602-
intend to use yourself. Should you wish to publish packages for use by
613+
This very simple :file:`pyproject.toml` will suffice for packages that you
614+
only intend to use yourself. Should you wish to publish packages for use by
603615
other people, then you'll need to provide significantly more information in
604-
:file:`setup.py` and, potentially, in other places too. The canonical guide
605-
to this is the Python Packaging User Guide. [#packaging]_
616+
:file:`pyproject.toml` and, potentially, in other places too. The canonical
617+
guide to this is the Python Packaging User Guide. [#packaging]_
606618

607619
Installing a package from local code
608620
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -616,14 +628,24 @@ you would type:
616628
617629
(PoP_venv) $ python -m pip install -e folder/
618630
619-
replacing `folder` with the name of the top-level folder of your
620-
repository: the folder containing :file:`setup.py`. The option flag `-e`
621-
tells Pip to install the package in 'editable' mode. This means that
622-
instead of copying the package files to your venv's Python packages
623-
folder, symbolic links will be created. This means that any changes
624-
that you make to your package will show up the next time the package
625-
is imported in a new Python process, avoiding the need to reinstall
626-
the package every time you change it.
631+
replacing :file:`folder` with the name of the top-level folder of your
632+
repository: the folder containing :file:`pyproject.toml`. The option flag `-e` tells
633+
Pip to install the package in 'editable' mode. This means that instead of
634+
copying the package files to your venv's Python packages folder, symbolic links
635+
will be created. This means that any changes that you make to your package will
636+
show up the next time the package is imported in a new Python process, avoiding
637+
the need to reinstall the package every time you change it.
638+
639+
The name `folder` in the example above is an example of a relative path. This
640+
means that `folder` is located relative to the folder in which the command
641+
`python -m pip` is run. It often happens that a user wants to install the
642+
package whose :file:`pyproject.toml` is in the current folder. In this case
643+
it's helpful to know that the special relative path :file:`.` refers to the
644+
current folder. So to install the package defined in the current folder, type:
645+
646+
.. code-block:: console
647+
648+
(PoP_venv) $ python -m pip install -e .
627649
628650
.. warning::
629651

@@ -643,10 +665,10 @@ One frequent source of confusion in making packages installable and actually
643665
installing them is that Pip and Python have slightly different definitions of
644666
what constitutes a package. A Python package, as we have just learned, is a
645667
folder containing (at least) a file called :file:`__init__.py`. For Pip,
646-
however, a package is everything that :file:`setup.py` installs. In particular,
647-
this can include multiple Python packages. Indeed, :numref:`minimal-setup-py`
668+
however, a package is everything that :file:`pyproject.toml` installs. In particular,
669+
this can include multiple Python packages. Indeed, :numref:`minimal-pyproject-toml`
648670
is sufficient to install any number of Python packages contained in subfolders
649-
of the folder containing :file:`setup.py`.
671+
of the folder containing :file:`pyproject.toml`.
650672

651673
Package dependencies
652674
~~~~~~~~~~~~~~~~~~~~
@@ -657,30 +679,42 @@ package themselves import other packages, then a user will need those packages
657679
to be installed in their Python environment, or your package will not work. If
658680
your package depends on other packages that need to be installed from PyPI then
659681
steps need to be taken to ensure that your users will have the correct packages
660-
installed. The `install_requires` keyword argument to :func:`setuptools.setup`
661-
takes a list of Pip package names. Pip will install any of these packages that
662-
are not already available before installing the package itself.
663-
:numref:`dependency-setup-py` illustrates this by adding a dependency on
682+
installed. The `dependencies` key in the `project` table provides a list of
683+
packages on which the current package depends. Pip will install any of these
684+
packages that are not already available before installing the package itself.
685+
:numref:`dependency-pyproject-toml` illustrates this by adding a dependency on
664686
:mod:`numpy`.
665687

666-
.. _dependency-setup-py:
688+
.. _dependency-pyproject-toml:
667689

668690
.. code-block:: python3
669-
:caption: An extension to the :file:`setup.py` from
670-
:numref:`minimal-setup-py` to require that :mod:`numpy` is installed.
691+
:caption: An extension to the :file:`pyproject.toml` from
692+
:numref:`minimal-pyproject-toml` to require that :mod:`numpy` is installed.
693+
694+
[build-system]
695+
requires = ["hatchling"]
696+
build-backend = "hatchling.build"
697+
698+
[project]
699+
name = "my_package"
700+
version = "0.1"
701+
dependencies = ["numpy"]
702+
703+
.. note::
671704

672-
from setuptools import setup, find_packages
673-
setup(
674-
name="my_package",
675-
version="0.1",
676-
packages=find_packages(),
677-
install_requires=["numpy"]
678-
)
705+
It is important to understand the difference between the `requires` key in
706+
the `build-system` table and the `dependencies` key in the `project` table.
707+
The former is a list of packages needed to build the package, while the
708+
latter is a list of packages needed to use the current package. You will
709+
often need to specify `dependencies` but, unless you are doing something
710+
quite advanced such as writing Python packages in another language, you
711+
will not need to add to `requires`.
679712

680713
.. warning::
681714

682-
`install_requires` should not list packages from the Python Standard
683-
Library. These are always available, and listing them will cause Pip to error.
715+
Neither `dependencies` nor `requires`should list packages from the Python
716+
Standard Library. These are always available, and listing them will cause
717+
Pip to error.
684718

685719

686720
.. `install_requires` should only list packages that Pip can install from
@@ -769,7 +803,7 @@ are usually gathered in a separate tests folder. For example::
769803
│ └── fibonacci.py
770804
├── tests
771805
│ └── test_fibonacci.py
772-
└── setup.py
806+
└── pyproject.toml
773807

774808
We can then invoke the tests from the shell:
775809

@@ -1043,9 +1077,9 @@ already familiar with Git and GitHub then you will also need to work through
10431077

10441078
.. proof:exercise::
10451079
1046-
Following :numref:`installable_packages`, create a :file:`setup.py` file in
1047-
your exercise repository, so that the :mod:`math_utils` :term:`package` is
1048-
installable.
1080+
Following :numref:`installable_packages`, create a :file:`pyproject.toml`
1081+
file in your exercise repository, so that the :mod:`math_utils`
1082+
:term:`package` is installable.
10491083

10501084
Pytest can't easily test installability for you, so once you have managed to
10511085
install your package yourself, commit and push to GitHub to check that the

0 commit comments

Comments
 (0)