Skip to content

PEP 621 Metadata#

The project metadata are stored in the pyproject.toml. The specifications are defined by PEP 621, PEP 631 and PEP 639. Read the detailed specifications in the PEPs.

In the following part of this document, metadata should be written under [project] table if not given explicitly.

Multiline description#

You can split a long description onto multiple lines, thanks to TOML support for multiline strings. Just remember to escape new lines, so the final description appears on one line only in your package metadata. Indentation will be removed as well when escaping new lines:

1
2
3
4
5
6
description = """\
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
    Ut enim ad minim veniam, quis nostrud exercitation ullamco \
    laboris nisi ut aliquip ex ea commodo consequat.\
"""

See TOML's specification on strings.

Package version#

1
2
[project]
version = "1.0.0"
1
2
3
4
5
6
[project]
...
dynamic = ["version"]

[tool.pdm]
version = { source = "file", path = "mypackage/__version__.py" }

The version will be read from the mypackage/__version__.py file searching for the pattern: __version__ = "{version}".

Read more information about other configurations in dynamic versioning.

Dependency specification#

The project.dependencies is an array of dependency specification strings following the PEP 440 and PEP 508.

Examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[project]
...
dependencies = [
    # Named requirement
    "requests",
    # Named requirement with version specifier
    "flask >= 1.1.0",
    # Requirement with environment marker
    "pywin32; sys_platform == 'win32'",
    # URL requirement
    "pip @ git+https://github.com/pypa/pip.git@20.3.1"
]

Optional dependencies#

You can have some requirements optional, which is similar to setuptools' extras_require parameter.

1
2
3
4
5
6
7
[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"',
]

To install a group of optional dependencies:

1
pdm install -G socks

-G option can be given multiple times to include more than one group.

Context variables expansion#

Depending on which build backend you are using, PDM will expand some variables in the dependency strings.

Environment variables#

1
2
[project]
dependencies = ["flask @ https://${USERNAME}:${PASSWORD}/artifacts.io/Flask-1.1.2.tar.gz"]
1
2
[project]
dependencies = ["flask @ https://{env:USERNAME}:{env:PASSWORD}/artifacts.io/Flask-1.1.2.tar.gz"]

Find more usages here

Don't worry about credential leakage, the environment variables will be expanded when needed and kept untouched in the lock file.

Relative paths#

When you add a package from a relative path, PDM will automatically save it as a relative path for pdm-pep517 and hatchling.

For example, if you run pdm add ./my-package, it will result in the following line in pyproject.toml.

1
2
[project]
dependencies = ["my-package @ file:///${PROJECT_ROOT}/my-package"]
1
2
[project]
dependencies = ["my-package @ {root:uri}/my-package"]

By default, hatchling doesn't support direct references in the dependency string, you need to turn it on in pyproject.toml:

1
2
[tool.hatch.metadata]
allow-direct-references = true

The relative path will be expanded based on the project root when installing or locking.

Console scripts#

The following content:

1
2
[project.scripts]
mycli = "mycli.__main__:main"

will be translated to setuptools style:

1
2
3
4
5
entry_points = {
    'console_scripts': [
        'mycli=mycli.__main__:main'
    ]
}

Also, [project.gui-scripts] will be translated to gui_scripts entry points group in setuptools style.

Entry points#

Other types of entry points are given by [project.entry-points.<type>] section, with the same format of [project.scripts]:

1
2
[project.entry-points.pytest11]
myplugin = "mypackage.plugin:pytest_plugin"

If the entry point name contains dots or other special characters, wrap it in quotes:

1
2
[project.entry-points."flake8.extension"]
myplugin = "mypackage.plugin:flake8_plugin"