# 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.

[PEP 621]: https://www.python.org/dev/peps/pep-0621/
[PEP 631]: https://www.python.org/dev/peps/pep-0631/
[PEP 639]: https://www.python.org/dev/peps/pep-0639/

_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](https://packaging.python.org/specifications/core-metadata/#summary).
Indentation will be removed as well when escaping new lines:

```toml
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](https://toml.io/en/v1.0.0#string).

## Package version

=== "Static"

    ```toml
    [project]
    version = "1.0.0"
    ```

=== "Dynamic"

    ```toml
    [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 project version](https://backend.pdm-project.org/metadata/#dynamic-project-version) from the `pdm-backend` documentation.

## Python version

The required version of Python is specified as the string `requires-python`:

```toml
requires-python = ">=3.9"
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    ...
]
```

Note: As per [PEP 621](https://peps.python.org/pep-0621/#allow-tools-to-add-extend-data),
PDM is not permitted to dynamically update the `classifiers` section like some other non-compliant tools.
Thus, you should also include the appropriate [trove classifiers](https://pypi.org/classifiers/) as shown above if you plan on publishing your package on [PyPI](https://pypi.org/).

## License

<!-- TODO: update following paragraphs if PEP 639 is accepted,
see https://peps.python.org/pep-0639/#deprecate-license-classifiers -->

The license is specified as the string `license`:

```toml
license = {text = "BSD-2-Clause"}
classifiers = [
    "License :: OSI Approved :: BSD License",
    ...
]
```

Note: As per [PEP 621](https://peps.python.org/pep-0621/#allow-tools-to-add-extend-data),
PDM is not permitted to dynamically update the `classifiers` section like some other non-compliant tools.
Thus, you should also include the appropriate [trove classifiers](https://pypi.org/classifiers/) as shown above if you plan on publishing your package on [PyPI](https://pypi.org/).

## Dependency specification

The `project.dependencies` is an array of dependency specification strings following the [PEP 440](https://www.python.org/dev/peps/pep-0440/) and [PEP 508](https://www.python.org/dev/peps/pep-0508/).

Examples:

```toml
[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.

```toml
[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:

```bash
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

=== "pdm-backend"

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

=== "hatchling"

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

    Find more usages [here](https://hatch.pypa.io/dev/config/context/#environment-variables)

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-backend` and `hatchling`.

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

=== "pdm-backend"

    ```toml
    [project]
    dependencies = ["my-package @ file:///${PROJECT_ROOT}/my-package"]
    ```

=== "hatchling"

    ```toml
    [project]
    dependencies = ["my-package @ {root:uri}/my-package"]
    ```

    By default, hatchling doesn't support [direct references](https://hatch.pypa.io/dev/config/dependency/#direct-references)
    in the dependency string, you need to turn it on in `pyproject.toml`:

    ```toml
    [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:

```toml
[project.scripts]
mycli = "mycli.__main__:main"
```

will be translated to `setuptools` style:

```python
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]`:

```toml
[project.entry-points.pytest11]
myplugin = "mypackage.plugin:pytest_plugin"
```

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

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