Skip to content

Project file syntax#

Project metadata#

PDM reads the project's metadata following the standardized format of PEP 621. View the PEP for the detailed specification.

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

Determine the package version dynamically#

You can specify a file source for version field like: version = {from = "pdm/__init__.py"}, in this form, the version will be read from the __version__ variable in that file.

PDM can also read version from SCM tags. If you are using git or hg as the version control system, define the version as follows:

1
version = {use_scm = true}

In either case, you MUST also include version in dynamic field, or the backend will raise an error:

1
dynamic = ["version"]

Include and exclude package files#

The way of specifying include and exclude files are simple, they are given as a list of glob patterns:

1
2
3
4
5
6
7
includes = [
    "**/*.json",
    "mypackage/",
]
excludes = [
    "mypackage/_temp/*"
]

If neither includes or excludes is given, PDM is also smart enough to include top level packages and all data files in them. Packages can also lie in src directory that PDM can find it.

Select another package directory to look for packages#

Similar to setuptools' package_dir setting, one can specify another package directory, such as src, in pyproject.toml easily:

1
package-dir = "src"

If no package directory is given, PDM can also recognize src as the package-dir implicitly if:

  1. src/__init__.py doesn't exist, meaning it is not a valid Python package, and
  2. There exist some packages under src/*.

Implicit namespace packages#

As specified in PEP 420, a directory will be recognized as a namespace package if:

  1. <package>/__init__.py doesn't exist, and
  2. There exist normal packages and/or other namespace packages under <package>/*, and
  3. <package> is not specified as package-dir

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
dependencies = [
    # Named requirement
    "requests",
    # Named requirement with version specifier
    "flask >= 1.1.0",
    # Requirement with environment marker
    "pywin32; sys_platform == 'win32'",
    # URL requirement
    "pip @ https://github.com/pypa/[email protected]"
]

Editable requirement#

Beside of the normal dependency specifications, one can also have some packages installed in editable mode. The editable specification string format is the same as Pip's editable install mode.

Examples:

1
2
3
4
5
dependencies = [
    ...,
    "-e path/to/SomeProject",
    "-e git+http://repo/my_project.git#egg=SomeProject"
]

About editable installation

One can have editable installation and normal installation for the same package. The one that comes at last wins. However, editable dependencies WON'T be included in the metadata of the built artifacts since they are not valid PEP 508 strings. They only exist for development purpose.

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 -s socks

-s option can be given multiple times to include more than one groups.

Development dependencies#

You can have some development only dependencies, which is the same as package.json's dev-dependencies field:

1
2
3
4
5
6
7
[project]

dev-dependencies = [
    "pytest",
    "flake8",
    "black"
]

To install all of them:

1
$ pdm install -d

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"

Build C extensions#

Currently, building C extensions still relies on setuptools. You should write a python script which contains a function named build and accepts the parameter dictionary of setup() as the only argument. Update the dictionary with your ext_modules settings in the function.

Here is an example taken from MarkupSafe:

1
2
3
4
5
6
7
# build.py
from setuptools import Extension

ext_modules = [Extension("markupsafe._speedups", ["src/markupsafe/_speedups.c"])]

def build(setup_kwargs):
    setup_kwargs.update(ext_modules=ext_modules)

Now, specify the build script path via build in the pyproject.toml:

1
2
3
# pyproject.toml
[project]
build = "build.py"