Common subsystem tasks
Common tasks for Subsystems
Skipping individual targets
Many subsystems allow skipping specific targets. For example, you might have Python files that you want to not typecheck with mypy. In Pants, this is achieved with a skip_*
field on the target. This is simple to implement.
- Create a field for skipping your tool
from pants.engine.target import BoolField
class SkipFortranLintField(BoolField):
alias = "skip_fortran_lint"
default = False
help = "If true, don't run fortran-lint on this target's code."
- Register this field on the appropriate targets.
def rules():
return [
FortranSourceTarget.register_plugin_field(SkipFortranLintField),
]
- Add this field as part of your subsystems
opt_out
method:
from dataclasses import dataclass
from pants.engine.target import FieldSet, Target
@dataclass
class FortranLintFieldSet(FieldSet):
required_fields = (FortranSourceField,)
source: FortranSourceField
@classmethod
def opt_out(cls, tgt: Target) -> bool:
return tgt.get(SkipFortranLintField).value
Making subsystems exportable with their default lockfile
Only some language backends support pants export
. These include the Python and JVM backends. Only tools which are themselves written to use a backend with this feature can be exported. For example, a Python-based tool which operates on a different language is exportable.
-
Make the subsystem a subclass of
ExportableTool
Language backends may have done this in their Tool base class.For example, the Python backend with
PythonToolRequirementsBase
and JVM withJvmToolBase
are already subclasses.from pants.backend.python.subsystems.python_tool_base import PythonToolBase
from pants.core.goals.resolves import ExportableTool
class FortranLint(PythonToolBase, ExportableTool):
... -
Register your class with a
UnionRule
withExportableTool
def rules():
return [
UnionRule(ExportableTool, FortranLint)
]
Loading config files
-
Add an option to toggle config discovery:
from pants.option.subsystem import Subsystem
from pants.option.option_types import BoolOption
from pants.util.strutil import softwrap
class FortranLint(Subsystem):
config_discovery = BoolOption(
default=True,
advanced=True,
help=lambda cls: softwrap(
f"""
If true, Pants will include all relevant config files during runs.
Use `[{cls.options_scope}].config` and `[{cls.options_scope}].custom_check_dir` instead if your config is in a non-standard location.
"""
),
) -
Add an option for the configuration file itself. Several options are useful depending on what types of config files you need:
FileOption
,FileListOption
,DirOption
,DirListOption
.from pants.option.subsystem import Subsystem
from pants.option.option_types import FileOption
from pants.util.strutil import softwrap
class FortranLint(Subsystem):
config = FileOption(
default=None,
advanced=True,
help=lambda cls: softwrap(
"""
Path to the fortran-lint config file.
Setting this option will disable config discovery for the config file. Use this option if the config is located in a non-standard location.
"""
),
) -
Add a helper function to generate the
ConfigFilesRequest
. Thecheck_existence
field is used for config discovery.specified
can also be a list for using one of the list options.from pants.core.util_rules.config_files import ConfigFilesRequest
from pants.option.subsystem import Subsystem
class FortranLint(Subsystem):
def config_request(self) -> ConfigFilesRequest:
return ConfigFilesRequest(
specified=self.config,
specified_option_name=f"[{self.options_scope}].config",
discovery=self.config_discovery,
check_existence=["fortran_lint.ini"],
) -
Make a request for the config files in a rule for running the tool. Use a
Get(ConfigFiles, ConfigFilesRequest)
to get the config files. This has a snapshot that contains the config files (or will be empty if none are found). You can merge these with the other digests to pass the files to yourProcess
. If a custom value was provided for the config file, you may need to pass that as an argument to theProcess
. You may also need to register rules frompants.core.util_rules.config_files
.from pants.core.goals.lint import LintResult
from pants.core.util_rules import config_files
from pants.core.util_rules.config_files import ConfigFiles, ConfigFilesRequest
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
from pants.engine.fs import Digest, MergeDigests
from pants.engine.rules import Get, MultiGet, collect_rules, rule
@rule
async def run_fortran_lint(request: FortranlintRequest.Batch, subsystem: FortranLint) -> LintResult:
sources, config_file = await MultiGet(
Get(SourceFiles, SourceFilesRequest(fs.sources for fs in request.elements)),
Get(ConfigFiles, ConfigFilesRequest, subsystem.config_request()),
)
input_digest = await Get(
Digest, MergeDigests((sources.snapshot.digest, config_file.snapshot.digest))
)
args = []
if subsystem.config_request:
args.append(f"--config-file={subsystem.config}")
# run your process with the digest and args
def rules():
return [
*collect_rules(),
*config_files.rules(),
]