Options and subsystems
How to add options to your plugin.
Defining options
As explained in Options, options are partitioned into named scopes, like [test]
and [isort]
. Each of these scopes corresponds to a subsystem.
To add new options:
- Define a subclass of
Subsystem
frompants.subsystem.subsystem
.- Set the class property
options_scope
with the name of the subsystem.- This value will be prepended to all options in the subsystem, e.g.
--skip
will become--shellcheck-skip
.
- This value will be prepended to all options in the subsystem, e.g.
- Set the class property
help
, which is used by./pants help
.
- Set the class property
- Add new options through
pants.options.option_types
class attributes. - Register the
Subsystem
withSubsystemRule
andregister.py
.- You don't need
SubsystemRule
if theSubsystem
is used in an@rule
becausecollect_rules()
will recognize it. It doesn't hurt to keep this around, though.
- You don't need
- pants-plugins/example/shellcheck.py
- pants-plugins/example/register.py
from pants.engine.rules import SubsystemRule
from pants.option.option_types import BoolOption
from pants.option.subsystem import Subsystem
class ShellcheckSubsystem(Subsystem):
options_scope = "shellcheck"
help = "The Shellcheck linter."
config_discovery = BoolOption(
"--config-discovery",
default=True,
advanced=True,
help="Whether Pants should...",
)
def rules():
return [SubsystemRule(ShellcheckSubsystem)]
from example import shellcheck
def rules():
return [*shellcheck.rules()]
The subsystem should now show up when you run ./pants help shellcheck
.
GoalSubsystem
As explained in Goal rules, goals use a subclass of Subsystem
: GoalSubsystem
from pants.engine.goal
.
GoalSubsystem
behaves the same way as a normal subsystem, except that you set the class property name
rather than options_scope
. The name
will auto-populate the options_scope
.
Option types
These classes correspond to the option types at Options.
Every option type requires that you set the flag name (e.g. -l
or --level
) and the keyword argument help
. Most types require that you set default
. You can optionally set advanced=True
with every option for it to only show up with help-advanced
.
Class name | Notes |
---|---|
StrOption | Must set default to a str or None . |
BoolOption | Must set default to a bool or None . TODO Reminder when choosing a flag name: Pants will recognize the command line argument --no-my-flag-name as equivalent to --my-flag-name=false . |
IntOption | Must set default to an int or None . |
FloatOption | Must set default to a float or None . |
EnumOption | This is like StrOption , but with the valid choices constrained to your enum. To use, define an enum.Enum . The values of your enum will be what users can type, e.g. 'kale' and 'spinach' below: python<br/>class LeafyGreens(Enum):<br/> KALE = "kale"<br/> SPINACH = "spinach"<br/> You must either set default to a value from your enum or None . If you set default=None , you must set enum_type . |
List options: - StrListOption - BoolListOption - IntListOption - FloatListOption - EnumListOption | Default is [] if default is not set. For EnumListOption , you must set the keyword argument enum_type . |
DictOption | Default is {} if default is not set. Currently, Pants does not offer any validation of the dictionary entries, e.g. dict[str, str] vs dict[str, list[str]] . (Although per TOML specs, the key should always be str .) You may want to add eager validation that users are inputting options the correct way. |
ArgsListOption | Adds an --args option, e.g. --isort-args . This type is extra useful because it uses a special shell_str that lets users type the arguments as a single string with spaces, which Pants will shlex for them. That is, --args='arg1 arg2' gets converted to ['arg1', 'arg2'] . You must set the keyword argument example , e.g. '--arg1 arg2' . You must also set tool_name: str , e.g. 'Black' . You can optionally set passthrough=True if the user should be able to use the style ./pants my-goal :: -- --arg1 , i.e. arguments after -- . |
Using options in rules
To use a Subsystem
or GoalSubsystem
in your rule, request it as a parameter. Then, use the class attributes to access the option value.
from pants.engine.rules import rule
...
@rule
async def demo(shellcheck: Shellcheck) -> LintResults:
if shellcheck.skip:
return LintResults()
config_discovery = shellcheck.config_discovery
...