The Target API defines how you interact with targets in your plugin. For example, you would use the Target API to read the `
source` / `
sources` field of a target to know which files to run on.
The Target API can also be used to add new target types—such as adding support for a new language. Additionally, the Target API can be used to extend existing target types.
## Targets and Fields - the core building blocks
### Definition of _target_
As described in [Targets and BUILD files](🔗), a _target_ is an _addressable_ set of metadata describing some of your code.
For example, this BUILD file defines a `
PythonTestTarget` target with `
### Definition of _field_
A _field_ is a single value of metadata belonging to a target, such as `
source` and `
timeout` above. (`
name` is a special thing used to create the `
Each field has a Python class that defines its BUILD file alias, data type, and optional settings like default values. For example:
### Target == alias + combination of fields
Alternatively, you can think of a target as simply an alias and a combination of fields:
A target's fields should make sense together. For example, it does not make sense for a `
python_source` target to have a `
Any unrecognized fields will cause an exception when used in a BUILD file.
### Fields may be reused
Because fields are stand-alone Python classes, the same field definition may be reused across multiple different target types.
For example, many target types have the `
This gives you reuse of code ([DRY](🔗)) and is important for your plugin to work with multiple different target types, as explained below.
## A Field-Driven API
Idiomatic Pants plugins do not care about specific target types; they only care that the target type has the right combination of field types that the plugin needs to operate.
For example, the Python formatter Black does not actually care whether you have a `
python_test`, or `
custom_target` target; all that it cares about is that your target type has the field `
Targets are only [used by the Rules API](🔗) to get access to the underlying fields through the methods `
.has_field()` and `
This means that when creating new target types, the fields you choose for your target will determine the functionality it has.
## Customizing fields through subclassing
Often, you may like how a field behaves, but want to make some tweaks. For example, you may want to give a default value to the `
To modify an existing field, simply subclass it.
Target` methods `
.has_field()` and `
.get()` understand this subclass relationship, as follows:
This subclass mechanism is key to how the Target API behaves:
You can use subclasses of fields—along with `
Target.has_field()`— to filter out irrelevant targets. For example, the Black formatter doesn't work with any plain `
SourcesField` field; it needs `
PythonSourceField`. The Python test runner is even more specific: it needs `
You can create custom fields and custom target types that still work with pre-existing functionality. For example, you can subclass `
PythonSourceField` to create `
DjangoSourceField`, and the Black formatter will still be able to operate on your target.