PyProject
The future of Python builds, distribution, and packaging lies in pyproject.toml. The protocol has been incrementally built through PEPs 517 and 518 (a pair), PEP 621, PEP 631, and PEP 660.
Contents
Build System
This is the focus of PEP 517. A minimal example is:
[build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta"
Project
This is the focus of PEP 621. It is more-or-less a direct mapping of a boilerplate setup.py file into the TOML format.
[project] name = "my-project" description = "This is the short description" readme = "path/to/my/long/description" version = "1.0.2" authors = [ { name = "John Doe", email = "[email protected]" } ] urls = { homepage = "example.com/my-project" } license = { file = "path/to/my/license" } requires-python = ">=3.6" dependencies = [ "toml >= 0.10.1", ] [project.scripts] my-project-cli = "my-project:main"
Some notable changes from setup.py:
long_description
a long standing practice was to read a project's README file and provide this as a long_description
now, just provide a path to the README file
author, author_email, maintainer, maintainer_email
authors and maintainers have replaced these
- this design greatly simplifies the specification of multiple individuals
license
while a license may continue to be provided as a string, the syntax has changed (license = { text = "GPL" })
- the current recommendation is to provide a path to the license file
- future PEPs may build on this design
entry_points
project.scripts exists as a more-or-less perfect mapping
an analogous [projects.gui-scripts] exists for scripts that should only be called in a graphical setting
Dependencies
PEP 631 furthered the design of dependencies and introduced optional-dependencies. This is the pyproject.toml of docker-compose:
[project] dependencies = [ 'cached-property >= 1.2.0, < 2', 'distro >= 1.5.0, < 2', 'docker[ssh] >= 4.2.2, < 5', 'dockerpty >= 0.4.1, < 1', 'docopt >= 0.6.1, < 1', 'jsonschema >= 2.5.1, < 4', 'PyYAML >= 3.10, < 6', 'python-dotenv >= 0.13.0, < 1', 'requests >= 2.20.0, < 3', 'texttable >= 0.9.0, < 2', 'websocket-client >= 0.32.0, < 1', # Conditional 'backports.shutil_get_terminal_size == 1.0.0; python_version < "3.3"', 'backports.ssl_match_hostname >= 3.5, < 4; python_version < "3.5"', 'colorama >= 0.4, < 1; sys_platform == "win32"', 'enum34 >= 1.0.4, < 2; python_version < "3.4"', 'ipaddress >= 1.0.16, < 2; python_version < "3.3"', 'subprocess32 >= 3.5.4, < 4; python_version < "3.2"', ] [project.optional-dependencies] socks = [ 'PySocks >= 1.5.6, != 1.5.7, < 2' ] tests = [ 'ddt >= 1.2.2, < 2', 'pytest < 6', 'mock >= 1.0.1, < 4; python_version < "3.4"', ]
Metadata
Prior to the acceptance of PEP 621, some projects developed a hack to centralize their build information in pyproject.toml:
insert a [metadata] table
in setup.py, import a TOML parser and parse pyproject.toml
- set all metadata based on the parsed values
While this may still be encountered in the wild, it should not be replicated.