Skip to main content
Version: 2.3 (deprecated)

Overview

An intro to the Pants engine's core concepts.


Pants is designed for extensibility: you can extend Pants by writing custom plugins, using a standard Plugin API. In fact, all of Pants's built-in functionality uses the same API!

Some of the ways you can extend Pants:

  • Add support for new languages.
  • Add new goals, like a publish goal or docker goal.
  • Add new linters, formatters, and type-checkers.
  • Add new codegen implementations.
  • Define new target types that still work with core Pants.
  • Add new forms of dependency inference
  • Define macros to reduce boilerplate in BUILD files.

Thanks to Pants's execution engine, your plugins will automatically bring you the same benefits you get from using core Pants, including:

  • Fine-grained caching.
  • Concurrent execution.
  • Remote execution.
The Plugin API is not yet stable

While we'll try our best to limit changes, the Plugin API does not yet follow the Deprecation Policy. Components of the API may change between minor versions—e.g. 2.1.0 to 2.2.0—without a deprecation.

We will document any changes in the release notes with a guide on how to navigate the change.

Core concepts

The plugin API is split into two main interfaces:

  1. The Target API: a declarative interface for creating new target types and extending pre-existing targets.
  2. The Rules API: where you define your logic and model each step of your build.

Plugins are written in typed Python 3 code. You write your logic in Python, and then Pants will run your plugin in the Rust engine.

Locating Plugin code

Plugins can be consumed in either of two ways:

  • From a published package in a repository such as PyPI.
  • directly from in-repo sources.

It's often convenient to use in-repo plugins, when the plugin is only relevant to a single repo, and you want to iterate on it rapidly. In other cases, you may want to publish the plugin, so it can be reused across multiple repos.

Published plugins

You consume published plugins by adding them to the plugins option:

pants.toml
[GLOBAL]
plugins = [
"my.plugin==2.3.4",
]

In-repo plugins

Conventionally, in-repo plugins live in a folder called pants-plugins, although they may be placed anywhere.

You must specify the path to your plugin's top-level folder using the pythonpath option:

pants.toml
[GLOBAL]
pythonpath = ["%(buildroot)s/pants-plugins"]
In-repo dependencies

In-repo plugin code should not depend on other in-repo code outside of the pants-plugins folder. The pants-plugins folder helps isolate plugins from regular code, which is necessary due to how Pants's startup sequence works.

You can depend on third-party dependencies in your in-repo plugin by adding them to the plugins option:

pants.toml
[GLOBAL]
plugins = [
"ansicolors==1.18.0",
]

Enabling Plugins with register.py

Recall that a Pants backend is a Python package that implements some required functionality, and uses hooks to register itself with Pants.

A plugin will contain one or more backends, with the hooks for each one defined in a file called register.py. To enable a custom plugin you add its backends to your backend_packages configuration:

[GLOBAL]
pythonpath = ["%(buildroot)s/pants-plugins"]
backend_packages.add = [
# This will activate `pants-plugins/plugin1/register.py`.
"plugin1",
# This will activate `pants-plugins/subdir/plugin2/register.py`.
"subdir.plugin2",
]

Building in-repo plugins with Pants

Because plugin code is written in Python, you can use Pants's Python backend to build your plugin code. For example, you can use Pants to lint, format, and test your plugin code.

To do so, set up Pants for Python support, and add your pants-plugins directory as a source root:

pants.toml
[source]
root_patterns = [
..,
"pants-plugins",
]

Your plugin code uses the Plugin API, so it depends on Pants's code as a 3rdparty dependency. It's important to depend on the exact Pants version that you're running in the repo.

One way to express this is to add pantsbuild.pants==<version> to your requirements.txt, but then you have to be careful to keep the versions in sync.

A better way is to use the pants_requirement() macro, which uses the pants_version from your pants.toml:

pants-plugins/BUILD
pants_requirement(name="pants", dist="pantsbuild.pants")

Pants's dependency inference understands imports of the pants module and will automatically add dependencies on the pants_requirement where relevant. You can also explicitly add it to the dependencies field:

pants-plugins/bash/BUILD
python_library(
# This will be inferred, but can also be explicitly specified.
dependencies=["pants-plugins:pants"],
)
pants_requirement cannot update a constraints file

If you are using a lockfile—which we strongly recommend for both correctness and performance—then you will need to remember to regenerate your constraints.txt file when upgrading Pants versions.

See https://www.pantsbuild.org/v2.0/docs/python-third-party-dependencies.