Write a plugin#
PDM is aiming at being a community driven package manager. It is shipped with a full-featured plug-in system, with which you can:
- Develop a new command for PDM
- Add additional options to existing PDM commands
- Change PDM's behavior by reading additional config items
- Control the process of dependency resolution or installation
What should a plugin do#
The core PDM project focuses on dependency management and package publishing. Other functionalities you wish to integrate with PDM are preferred to lie in their own plugins and released as standalone PyPI projects. In case the plugin is considered a good supplement of the core project it may have a chance to be absorbed into PDM.
Write your own plugin#
In the following sections, I will show an example of adding a new command
hello which reads the
Write the command#
The PDM's CLI module is designed in a way that user can easily "inherit and modify". To write a new command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
First, let's create a new
HelloCommand class inherting from
pdm.cli.commands.base.BaseCommand. It has two major functions:
add_arguments()to manipulate the argument parser passed as the only argument, where you can add additional command line arguments to it
handle()to do something when the subcommand is matched, you can do nothing by writing a single
passstatement. It accepts two arguments: an
pdm.project.Projectobject as the first one and the parsed
argparse.Namespaceobject as the second.
The document string will serve as the command help text, which will be shown in
Besides, PDM's subcommand has two default options:
-v/--verbose to change the verbosity level and
-g/--global to enable global project.
If you don't want these default options, override the
arguments class attribute to a list of
pdm.cli.options.Option objects, or
assign it to an empty list to have no default options:
1 2 3
The default options are loaded first, then
add_arguments() is called.
Register the command to the core object#
Write a function somewhere in your plugin project. There is no limit on what the name of the function is, but the function should take only one argument -- the PDM core object:
core.register_command() to register the command. The second argument as the name of the subcommand is optional.
PDM will look for the
name attribute if the name is not passed.
Add a new config item#
Let's recall the first code snippet,
hello.name config key is consulted for the name if not passed via the command line.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Till now, if you query the config value by
pdm config get hello.name, an error will pop up saying it is not a valid config key.
You need to register the config item, too:
1 2 3 4 5
ConfigItem class takes 4 parameters, in the following order:
description: a description of the config item
default: default value of the config item
global_only: whether the config is allowed to set in home config only
env_var: the name of environment variable which will be read as the config value
Other plugin points#
Besides of commands and configurations, PDM provides some other plugin abilities which are not covered in the above example:
core.project_class: change the class of project object
core.repository_class: change the class of repository object, which controls how to look for candidates and metadata of a package
core.resolver_class: change the resolver class, to control the resolution process
core.synchronizer_class: change the synchronizer_class, to control the installation process
core.parser: add arguments to the root argument parser
Tips about developing a PDM plugin.#
When developing a plugin, one hopes to activate and plugin in development and get updated when the code changes. This is usually done
pip install -e . or
python setup.py develop in the traditional Python packaging world which leverages
setup.py to do so. However,
as there is no such
setup.py in a PDM project, how can we do that?
Fortunately, it becomes even easier with PDM and PEP 582. First, you should enable PEP 582 globally following the
corresponding part of this doc. Then you just need to install all dependencies into the
__pypackages__ directory by:
After that, all the dependencies are available with a compatible Python interpreter, including the plugin itself, in editable mode. That means any change
to the codebase will take effect immediately without re-installation. The
pdm executable also uses a Python interpreter under the hood,
so if you run
pdm from inside the plugin project, the plugin in development will be activated automatically, and you can do some testing to see how it works.
That is how PEP 582 benefits our development workflow.
Publish your plugin#
Now you have defined your plugin already, let's distribute it to PyPI. PDM's plugins are discovered by entry point types.
pdm.plugin entry point and point to your plugin callable (yeah, it doesn't need to be a function, any callable object can work):
1 2 3 4
pdm.plugin is wrapped with double quotes to be regarded as a single key.
1 2 3 4 5 6 7
Activate the plugin#
As plugins are loaded via entry points, they can be activated with no more steps than just installing the plugin.
Assume your plugin is published as
pdm-hello, and if you installed
Or if you installed
Otherwise, if you installed
pdm with normal
The core idea is you must install the plugin in the same site-package directory as
Now type in
pdm --help in the terminal, you will see the new added
hello command and use it: