jaymd96-pants-baseline 0.2.1__tar.gz → 0.2.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/PKG-INFO +1 -1
  2. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/__about__.py +1 -1
  3. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/register.py +15 -17
  4. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/rules/audit_rules.py +16 -17
  5. jaymd96_pants_baseline-0.2.13/src/pants_baseline/rules/fmt_rules.py +108 -0
  6. jaymd96_pants_baseline-0.2.13/src/pants_baseline/rules/lint_rules.py +124 -0
  7. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/rules/test_rules.py +14 -11
  8. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/rules/typecheck_rules.py +18 -17
  9. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/subsystems/baseline.py +6 -0
  10. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/subsystems/ruff.py +24 -6
  11. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/subsystems/ty.py +11 -0
  12. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/subsystems/uv.py +11 -0
  13. jaymd96_pants_baseline-0.2.1/src/pants_baseline/rules/fmt_rules.py +0 -138
  14. jaymd96_pants_baseline-0.2.1/src/pants_baseline/rules/lint_rules.py +0 -138
  15. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/.gitignore +0 -0
  16. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/LICENSE +0 -0
  17. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/README.md +0 -0
  18. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/pyproject.toml +0 -0
  19. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/__init__.py +0 -0
  20. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/bundled_claude_plugins.py +0 -0
  21. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/goals/__init__.py +0 -0
  22. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/goals/audit.py +0 -0
  23. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/goals/fmt.py +0 -0
  24. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/goals/lint.py +0 -0
  25. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/goals/test.py +0 -0
  26. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/goals/typecheck.py +0 -0
  27. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/rules/__init__.py +0 -0
  28. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/subsystems/__init__.py +0 -0
  29. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/src/pants_baseline/targets.py +0 -0
  30. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/tests/__init__.py +0 -0
  31. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/tests/conftest.py +0 -0
  32. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/tests/integration/__init__.py +0 -0
  33. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/tests/integration/test_goals.py +0 -0
  34. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/tests/unit/__init__.py +0 -0
  35. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/tests/unit/test_subsystems.py +0 -0
  36. {jaymd96_pants_baseline-0.2.1 → jaymd96_pants_baseline-0.2.13}/tests/unit/test_targets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaymd96-pants-baseline
3
- Version: 0.2.1
3
+ Version: 0.2.13
4
4
  Summary: Opinionated Python code quality baseline plugin for Pants build system
5
5
  Project-URL: Homepage, https://github.com/jaymd96/pants-baseline
6
6
  Project-URL: Repository, https://github.com/jaymd96/pants-baseline.git
@@ -1,4 +1,4 @@
1
1
  """Version information for jaymd96-pants-baseline."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.13"
4
4
  __author__ = "James"
@@ -2,22 +2,19 @@
2
2
 
3
3
  This module is the entry point for the Pants plugin system.
4
4
  It registers all rules, targets, and subsystems provided by this plugin.
5
+
6
+ This plugin integrates with Pants' built-in lint, fmt, and check goals
7
+ rather than providing custom goals.
5
8
  """
6
9
 
7
- from typing import Iterable
10
+ from typing import Iterable, Type
8
11
 
9
12
  from pants.engine.rules import Rule
13
+ from pants.option.subsystem import Subsystem
10
14
 
11
- from pants_baseline.goals import audit as audit_goal
12
- from pants_baseline.goals import fmt as fmt_goal
13
- from pants_baseline.goals import lint as lint_goal
14
- from pants_baseline.goals import test as test_goal
15
- from pants_baseline.goals import typecheck as typecheck_goal
16
- from pants_baseline.rules import audit_rules, fmt_rules, lint_rules, test_rules, typecheck_rules
15
+ from pants_baseline.rules import fmt_rules, lint_rules
17
16
  from pants_baseline.subsystems.baseline import BaselineSubsystem
18
17
  from pants_baseline.subsystems.ruff import RuffSubsystem
19
- from pants_baseline.subsystems.ty import TySubsystem
20
- from pants_baseline.subsystems.uv import UvSubsystem
21
18
  from pants_baseline.targets import BaselinePythonProject
22
19
 
23
20
 
@@ -29,19 +26,20 @@ def rules() -> Iterable[Rule]:
29
26
  rather than using collect_rules() which only collects @rule functions.
30
27
  """
31
28
  return [
29
+ # Tool rules (integrate with Pants built-in lint/fmt goals)
32
30
  *lint_rules.rules(),
33
31
  *fmt_rules.rules(),
34
- *typecheck_rules.rules(),
35
- *test_rules.rules(),
36
- *audit_rules.rules(),
37
- *lint_goal.rules(),
38
- *fmt_goal.rules(),
39
- *typecheck_goal.rules(),
40
- *test_goal.rules(),
41
- *audit_goal.rules(),
42
32
  ]
43
33
 
44
34
 
45
35
  def target_types() -> Iterable[type]:
46
36
  """Return all custom target types provided by this plugin."""
47
37
  return [BaselinePythonProject]
38
+
39
+
40
+ def subsystems() -> Iterable[Type[Subsystem]]:
41
+ """Return all subsystems provided by this plugin."""
42
+ return [
43
+ BaselineSubsystem,
44
+ RuffSubsystem,
45
+ ]
@@ -3,11 +3,13 @@
3
3
  from dataclasses import dataclass
4
4
  from typing import Iterable
5
5
 
6
- from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
7
- from pants.engine.fs import Digest, MergeDigests, PathGlobs, Snapshot
6
+ from pants.core.util_rules.external_tool import download_external_tool
7
+ from pants.engine.fs import MergeDigests, PathGlobs
8
+ from pants.engine.internals.selectors import concurrently
9
+ from pants.engine.intrinsics import merge_digests, execute_process, path_globs_to_digest
8
10
  from pants.engine.platform import Platform
9
- from pants.engine.process import FallibleProcessResult, Process
10
- from pants.engine.rules import Get, collect_rules, rule
11
+ from pants.engine.process import Process
12
+ from pants.engine.rules import collect_rules, implicitly, rule
11
13
  from pants.util.logging import LogLevel
12
14
 
13
15
  from pants_baseline.subsystems.uv import UvSubsystem
@@ -39,23 +41,20 @@ async def run_uv_audit(
39
41
  platform: Platform,
40
42
  ) -> AuditResult:
41
43
  """Run uv audit on dependencies."""
42
- # Download uv
43
- downloaded_uv = await Get(
44
- DownloadedExternalTool,
45
- ExternalToolRequest,
46
- uv_subsystem.get_request(platform),
44
+ # Download uv and get lock files in parallel using new intrinsics
45
+ downloaded_uv_get = download_external_tool(uv_subsystem.get_request(platform))
46
+ lock_file_digest_get = path_globs_to_digest(
47
+ PathGlobs([request.lock_file, "pyproject.toml", "requirements.txt"])
47
48
  )
48
49
 
49
- # Get the lock file if it exists
50
- lock_file_snapshot = await Get(
51
- Snapshot,
52
- PathGlobs([request.lock_file, "pyproject.toml", "requirements.txt"]),
50
+ downloaded_uv, lock_file_digest = await concurrently(
51
+ downloaded_uv_get,
52
+ lock_file_digest_get,
53
53
  )
54
54
 
55
55
  # Merge the uv binary with lock files
56
- input_digest = await Get(
57
- Digest,
58
- MergeDigests([downloaded_uv.digest, lock_file_snapshot.digest]),
56
+ input_digest = await merge_digests(
57
+ MergeDigests([downloaded_uv.digest, lock_file_digest]),
59
58
  )
60
59
 
61
60
  # Build ignore args
@@ -78,7 +77,7 @@ async def run_uv_audit(
78
77
  level=LogLevel.DEBUG,
79
78
  )
80
79
 
81
- result = await Get(FallibleProcessResult, Process, process)
80
+ result = await execute_process(process, **implicitly())
82
81
 
83
82
  stdout = result.stdout.decode()
84
83
  stderr = result.stderr.decode()
@@ -0,0 +1,108 @@
1
+ """Rules for Ruff formatting."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Iterable
5
+
6
+ from pants.core.goals.fmt import FmtResult, FmtTargetsRequest
7
+ from pants.core.util_rules.external_tool import DownloadedExternalTool, download_external_tool
8
+ from pants.core.util_rules.partitions import PartitionerType
9
+ from pants.engine.fs import Digest, MergeDigests
10
+ from pants.engine.intrinsics import merge_digests
11
+ from pants.engine.platform import Platform
12
+ from pants.engine.process import FallibleProcessResult, Process, execute_process_or_raise
13
+ from pants.engine.rules import collect_rules, implicitly, rule
14
+ from pants.engine.target import FieldSet
15
+ from pants.util.logging import LogLevel
16
+ from pants.util.meta import classproperty
17
+
18
+ from pants.backend.python.target_types import PythonSourceField
19
+
20
+ from pants_baseline.subsystems.baseline import BaselineSubsystem
21
+ from pants_baseline.subsystems.ruff import RuffSubsystem
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class RuffFmtFieldSet(FieldSet):
26
+ """Field set for Ruff formatting."""
27
+
28
+ required_fields = (PythonSourceField,)
29
+
30
+ sources: PythonSourceField
31
+
32
+
33
+ class RuffFmtRequest(FmtTargetsRequest):
34
+ """Request to run Ruff formatting."""
35
+
36
+ field_set_type = RuffFmtFieldSet
37
+ tool_subsystem = RuffSubsystem
38
+ partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
39
+
40
+ @classproperty
41
+ def tool_name(cls) -> str:
42
+ return "baseline-ruff-fmt"
43
+
44
+ @classproperty
45
+ def tool_id(cls) -> str:
46
+ return "baseline-ruff-fmt"
47
+
48
+
49
+ @rule(desc="Format with Ruff", level=LogLevel.DEBUG)
50
+ async def run_ruff_fmt(
51
+ request: RuffFmtRequest.Batch[RuffFmtFieldSet, Any],
52
+ ruff_subsystem: RuffSubsystem,
53
+ baseline_subsystem: BaselineSubsystem,
54
+ platform: Platform,
55
+ ) -> FmtResult:
56
+ """Run Ruff formatter on Python files."""
57
+ if ruff_subsystem.skip:
58
+ return FmtResult.skip(request, formatter_name="baseline-ruff-fmt")
59
+
60
+ if not baseline_subsystem.enabled:
61
+ return FmtResult.skip(request, formatter_name="baseline-ruff-fmt")
62
+
63
+ snapshot = request.snapshot
64
+ if not snapshot.files:
65
+ return FmtResult.skip(request, formatter_name="baseline-ruff-fmt")
66
+
67
+ # Download ruff
68
+ downloaded_ruff: DownloadedExternalTool = await download_external_tool(
69
+ ruff_subsystem.get_request(platform)
70
+ )
71
+
72
+ # Merge the ruff binary with the source files
73
+ input_digest: Digest = await merge_digests(
74
+ MergeDigests([downloaded_ruff.digest, snapshot.digest]),
75
+ )
76
+
77
+ # Build Ruff format command
78
+ # Note: quote-style and indent-style are config file options only in ruff 0.9+
79
+ argv = [
80
+ downloaded_ruff.exe,
81
+ "format",
82
+ f"--target-version=py{baseline_subsystem.python_version.replace('.', '')}",
83
+ f"--line-length={baseline_subsystem.line_length}",
84
+ *snapshot.files,
85
+ ]
86
+
87
+ result: FallibleProcessResult = await execute_process_or_raise(
88
+ **implicitly(
89
+ Process(
90
+ argv=argv,
91
+ input_digest=input_digest,
92
+ output_files=snapshot.files,
93
+ description=f"Run Ruff format on {len(snapshot.files)} files",
94
+ level=LogLevel.DEBUG,
95
+ )
96
+ )
97
+ )
98
+
99
+ # FmtResult.create() is async and takes 2 args in Pants 2.30+
100
+ return await FmtResult.create(request, result)
101
+
102
+
103
+ def rules() -> Iterable:
104
+ """Return all format rules."""
105
+ return [
106
+ *collect_rules(),
107
+ *RuffFmtRequest.rules(),
108
+ ]
@@ -0,0 +1,124 @@
1
+ """Rules for Ruff linting."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Iterable
5
+
6
+ from pants.core.goals.lint import LintResult, LintTargetsRequest
7
+ from pants.core.util_rules.external_tool import DownloadedExternalTool, download_external_tool
8
+ from pants.core.util_rules.partitions import PartitionerType
9
+ from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest, determine_source_files
10
+ from pants.engine.fs import Digest, MergeDigests
11
+ from pants.engine.intrinsics import execute_process, merge_digests
12
+ from pants.engine.platform import Platform
13
+ from pants.engine.process import Process
14
+ from pants.engine.rules import collect_rules, implicitly, rule
15
+ from pants.engine.target import FieldSet
16
+ from pants.util.logging import LogLevel
17
+ from pants.util.meta import classproperty
18
+
19
+ from pants.backend.python.target_types import PythonSourceField
20
+
21
+ from pants_baseline.subsystems.baseline import BaselineSubsystem
22
+ from pants_baseline.subsystems.ruff import RuffSubsystem
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class RuffLintFieldSet(FieldSet):
27
+ """Field set for Ruff linting."""
28
+
29
+ required_fields = (PythonSourceField,)
30
+
31
+ sources: PythonSourceField
32
+
33
+
34
+ class RuffLintRequest(LintTargetsRequest):
35
+ """Request to run Ruff linting."""
36
+
37
+ field_set_type = RuffLintFieldSet
38
+ tool_subsystem = RuffSubsystem
39
+ partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
40
+
41
+ @classproperty
42
+ def tool_name(cls) -> str:
43
+ return "baseline-ruff"
44
+
45
+ @classproperty
46
+ def tool_id(cls) -> str:
47
+ return "baseline-ruff"
48
+
49
+
50
+ @rule(desc="Lint with Ruff", level=LogLevel.DEBUG)
51
+ async def run_ruff_lint(
52
+ request: RuffLintRequest.Batch[RuffLintFieldSet, Any],
53
+ ruff_subsystem: RuffSubsystem,
54
+ baseline_subsystem: BaselineSubsystem,
55
+ platform: Platform,
56
+ ) -> LintResult:
57
+ """Run Ruff linter on Python files."""
58
+ if ruff_subsystem.skip:
59
+ return LintResult.create(request, exit_code=0, stdout="", stderr="", strip_chroot_path=True)
60
+
61
+ if not baseline_subsystem.enabled:
62
+ return LintResult.create(request, exit_code=0, stdout="", stderr="", strip_chroot_path=True)
63
+
64
+ field_sets = list(request.elements)
65
+
66
+ if not field_sets:
67
+ return LintResult.create(request, exit_code=0, stdout="No targets to lint", stderr="", strip_chroot_path=True)
68
+
69
+ # Download ruff and get source files
70
+ downloaded_ruff: DownloadedExternalTool = await download_external_tool(
71
+ ruff_subsystem.get_request(platform)
72
+ )
73
+ sources: SourceFiles = await determine_source_files(
74
+ SourceFilesRequest(
75
+ sources_fields=[fs.sources for fs in field_sets],
76
+ for_sources_types=(PythonSourceField,),
77
+ )
78
+ )
79
+
80
+ if not sources.files:
81
+ return LintResult.create(request, exit_code=0, stdout="No files to lint", stderr="", strip_chroot_path=True)
82
+
83
+ # Merge the ruff binary with the source files
84
+ input_digest: Digest = await merge_digests(
85
+ MergeDigests([downloaded_ruff.digest, sources.snapshot.digest]),
86
+ )
87
+
88
+ # Build Ruff command
89
+ select_args = [f"--select={','.join(ruff_subsystem.select)}"] if ruff_subsystem.select else []
90
+ ignore_args = [f"--ignore={','.join(ruff_subsystem.ignore)}"] if ruff_subsystem.ignore else []
91
+
92
+ # Build Ruff check command
93
+ # Note: line-length is a config file option only in ruff 0.9+
94
+ argv = [
95
+ downloaded_ruff.exe,
96
+ "check",
97
+ f"--target-version=py{baseline_subsystem.python_version.replace('.', '')}",
98
+ *select_args,
99
+ *ignore_args,
100
+ "--output-format=concise",
101
+ *sources.files,
102
+ ]
103
+
104
+ # execute_process returns FallibleProcessResult which LintResult.create accepts directly
105
+ process_result = await execute_process(
106
+ Process(
107
+ argv=argv,
108
+ input_digest=input_digest,
109
+ description=f"Run Ruff lint on {len(sources.files)} files",
110
+ level=LogLevel.DEBUG,
111
+ ),
112
+ **implicitly(),
113
+ )
114
+
115
+ # LintResult.create() takes 2 args in Pants 2.30+
116
+ return LintResult.create(request, process_result)
117
+
118
+
119
+ def rules() -> Iterable:
120
+ """Return all lint rules."""
121
+ return [
122
+ *collect_rules(),
123
+ *RuffLintRequest.rules(),
124
+ ]
@@ -5,8 +5,10 @@ from typing import Iterable
5
5
 
6
6
  from pants.core.goals.test import TestRequest, TestResult
7
7
  from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
8
- from pants.engine.process import FallibleProcessResult, Process
9
- from pants.engine.rules import Get, collect_rules, rule
8
+ from pants.engine.internals.selectors import concurrently
9
+ from pants.engine.intrinsics import execute_process
10
+ from pants.engine.process import Process
11
+ from pants.engine.rules import collect_rules, implicitly, rule
10
12
  from pants.engine.target import FieldSet
11
13
  from pants.engine.unions import UnionRule
12
14
  from pants.util.logging import LogLevel
@@ -70,19 +72,20 @@ async def run_pytest(
70
72
  output_setting=None,
71
73
  )
72
74
 
73
- # Get test source files
74
- test_source_files_request: SourceFilesRequest = SourceFilesRequest(
75
+ # Get test and source files in parallel using new intrinsics
76
+ test_source_files_request = SourceFilesRequest(
75
77
  sources_fields=[fs.test_sources for fs in field_sets],
76
78
  for_sources_types=(BaselineTestSourcesField,),
77
79
  )
78
- test_sources = await Get(SourceFiles, SourceFilesRequest, test_source_files_request)
79
-
80
- # Get source files for coverage
81
- source_files_request: SourceFilesRequest = SourceFilesRequest(
80
+ source_files_request = SourceFilesRequest(
82
81
  sources_fields=[fs.sources for fs in field_sets],
83
82
  for_sources_types=(BaselineSourcesField,),
84
83
  )
85
- sources = await Get(SourceFiles, SourceFilesRequest, source_files_request)
84
+
85
+ test_sources, sources = await concurrently(
86
+ implicitly(test_source_files_request, SourceFiles),
87
+ implicitly(source_files_request, SourceFiles),
88
+ )
86
89
 
87
90
  if not test_sources.files:
88
91
  return TestResult(
@@ -119,14 +122,14 @@ async def run_pytest(
119
122
  *test_sources.files,
120
123
  ]
121
124
 
122
- process: Process = Process(
125
+ process = Process(
123
126
  argv=argv,
124
127
  input_digest=test_sources.snapshot.digest,
125
128
  description=f"Run pytest on {len(test_sources.files)} test files",
126
129
  level=LogLevel.DEBUG,
127
130
  )
128
131
 
129
- result = await Get(FallibleProcessResult, Process, process)
132
+ result = await execute_process(process, **implicitly())
130
133
 
131
134
  return TestResult(
132
135
  exit_code=result.exit_code,
@@ -4,12 +4,14 @@ from dataclasses import dataclass
4
4
  from typing import Iterable
5
5
 
6
6
  from pants.core.goals.check import CheckRequest, CheckResult, CheckResults
7
- from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
7
+ from pants.core.util_rules.external_tool import download_external_tool
8
8
  from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
9
- from pants.engine.fs import Digest, MergeDigests
9
+ from pants.engine.fs import MergeDigests
10
+ from pants.engine.internals.selectors import concurrently
11
+ from pants.engine.intrinsics import merge_digests, execute_process
10
12
  from pants.engine.platform import Platform
11
- from pants.engine.process import FallibleProcessResult, Process
12
- from pants.engine.rules import Get, MultiGet, collect_rules, rule
13
+ from pants.engine.process import Process
14
+ from pants.engine.rules import collect_rules, implicitly, rule
13
15
  from pants.engine.target import FieldSet, Target
14
16
  from pants.engine.unions import UnionRule
15
17
  from pants.util.logging import LogLevel
@@ -78,16 +80,16 @@ async def run_ty_check(
78
80
  checker_name="ty",
79
81
  )
80
82
 
81
- # Download ty and get source files in parallel
82
- downloaded_ty, sources = await MultiGet(
83
- Get(DownloadedExternalTool, ExternalToolRequest, ty_subsystem.get_request(platform)),
84
- Get(
85
- SourceFiles,
86
- SourceFilesRequest(
87
- sources_fields=[fs.sources for fs in field_sets],
88
- for_sources_types=(BaselineSourcesField,),
89
- ),
90
- ),
83
+ # Download ty and get source files in parallel using new intrinsics
84
+ downloaded_ty_get = download_external_tool(ty_subsystem.get_request(platform))
85
+ sources_get = SourceFilesRequest(
86
+ sources_fields=[fs.sources for fs in field_sets],
87
+ for_sources_types=(BaselineSourcesField,),
88
+ )
89
+
90
+ downloaded_ty, sources = await concurrently(
91
+ downloaded_ty_get,
92
+ implicitly(sources_get, SourceFiles),
91
93
  )
92
94
 
93
95
  if not sources.files:
@@ -104,8 +106,7 @@ async def run_ty_check(
104
106
  )
105
107
 
106
108
  # Merge the ty binary with the source files
107
- input_digest = await Get(
108
- Digest,
109
+ input_digest = await merge_digests(
109
110
  MergeDigests([downloaded_ty.digest, sources.snapshot.digest]),
110
111
  )
111
112
 
@@ -129,7 +130,7 @@ async def run_ty_check(
129
130
  level=LogLevel.DEBUG,
130
131
  )
131
132
 
132
- result = await Get(FallibleProcessResult, Process, process)
133
+ result = await execute_process(process, **implicitly())
133
134
 
134
135
  return CheckResults(
135
136
  results=[
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from pants.engine.rules import collect_rules
5
6
  from pants.option.option_types import BoolOption, IntOption, StrListOption, StrOption
6
7
  from pants.option.subsystem import Subsystem
7
8
 
@@ -72,3 +73,8 @@ class BaselineSubsystem(Subsystem):
72
73
  """Return Python version in format suitable for tools (e.g., 'py311')."""
73
74
  version = self.python_version.replace(".", "")
74
75
  return f"py{version}"
76
+
77
+
78
+ def rules():
79
+ """Return rules for the baseline subsystem."""
80
+ return collect_rules()
@@ -4,7 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  from pants.core.util_rules.external_tool import ExternalTool
6
6
  from pants.engine.platform import Platform
7
+ from pants.engine.rules import collect_rules
8
+ from pants.engine.unions import UnionRule
7
9
  from pants.option.option_types import BoolOption, SkipOption, StrListOption, StrOption
10
+ from pants.core.goals.generate_lockfiles import ExportableTool
8
11
 
9
12
 
10
13
  class RuffSubsystem(ExternalTool):
@@ -19,11 +22,11 @@ class RuffSubsystem(ExternalTool):
19
22
 
20
23
  default_version = "0.9.6"
21
24
  default_known_versions = [
22
- # Ruff 0.9.6 - January 2025
23
- "0.9.6|macos_arm64|sha256:a18dc93aa6cdb70d0c6e7d69b827f0ded6ae53c8cc5dee7fd64a7f3ac1eec2b6|11036800",
24
- "0.9.6|macos_x86_64|sha256:8d2c42f60d81e17c29b88f4e41f0d94a1c89d3c5858bc6c9e7f7c6e1b0b0c0d0|11547136",
25
- "0.9.6|linux_arm64|sha256:c5c72a6d0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c|10941952",
26
- "0.9.6|linux_x86_64|sha256:d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4|11689472",
25
+ # Ruff 0.9.6 - Format: version|platform|sha256_hash|file_size_bytes
26
+ "0.9.6|macos_arm64|a3132eb5e3d95f36d378144082276fbed0309789dadb19d8a4c41ec5e80451fb|11124436",
27
+ "0.9.6|macos_x86_64|ec88c095036b25e95391ea202fcc9496d565f4e43152db10785eb9757ea0815d|11663591",
28
+ "0.9.6|linux_arm64|cf796c953def5a7102002372893942fac875ac718355698a4a70405104dfbb6c|11946730",
29
+ "0.9.6|linux_x86_64|bed850f15d4d5aaaef2b6a131bfecd5b9d7d3191596249d07e576bd9fd37078e|12511815",
27
30
  ]
28
31
 
29
32
  def generate_url(self, plat: Platform) -> str:
@@ -40,7 +43,14 @@ class RuffSubsystem(ExternalTool):
40
43
 
41
44
  def generate_exe(self, plat: Platform) -> str:
42
45
  """Return the path to the ruff executable within the downloaded archive."""
43
- return "ruff"
46
+ platform_mapping = {
47
+ "macos_arm64": "aarch64-apple-darwin",
48
+ "macos_x86_64": "x86_64-apple-darwin",
49
+ "linux_arm64": "aarch64-unknown-linux-gnu",
50
+ "linux_x86_64": "x86_64-unknown-linux-gnu",
51
+ }
52
+ plat_str = platform_mapping.get(plat.value, "x86_64-unknown-linux-gnu")
53
+ return f"ruff-{plat_str}/ruff"
44
54
 
45
55
  # Skip option required by Pants for tool subsystems
46
56
  skip = SkipOption("lint", "fmt")
@@ -112,3 +122,11 @@ class RuffSubsystem(ExternalTool):
112
122
  ],
113
123
  help="Rules to skip in __init__.py files.",
114
124
  )
125
+
126
+
127
+ def rules():
128
+ """Return rules for the Ruff subsystem."""
129
+ return (
130
+ *collect_rules(),
131
+ UnionRule(ExportableTool, RuffSubsystem),
132
+ )
@@ -2,8 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from pants.core.goals.generate_lockfiles import ExportableTool
5
6
  from pants.core.util_rules.external_tool import ExternalTool
6
7
  from pants.engine.platform import Platform
8
+ from pants.engine.rules import collect_rules
9
+ from pants.engine.unions import UnionRule
7
10
  from pants.option.option_types import BoolOption, StrListOption, StrOption
8
11
 
9
12
 
@@ -99,3 +102,11 @@ class TySubsystem(ExternalTool):
99
102
  default="text",
100
103
  help="Output format for type errors ('text', 'json', 'github').",
101
104
  )
105
+
106
+
107
+ def rules():
108
+ """Return rules for the ty subsystem."""
109
+ return (
110
+ *collect_rules(),
111
+ UnionRule(ExportableTool, TySubsystem),
112
+ )
@@ -2,8 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from pants.core.goals.generate_lockfiles import ExportableTool
5
6
  from pants.core.util_rules.external_tool import ExternalTool
6
7
  from pants.engine.platform import Platform
8
+ from pants.engine.rules import collect_rules
9
+ from pants.engine.unions import UnionRule
7
10
  from pants.option.option_types import BoolOption, StrListOption, StrOption
8
11
 
9
12
 
@@ -85,3 +88,11 @@ class UvSubsystem(ExternalTool):
85
88
  default=[],
86
89
  help="Additional arguments to pass to uv commands.",
87
90
  )
91
+
92
+
93
+ def rules():
94
+ """Return rules for the uv subsystem."""
95
+ return (
96
+ *collect_rules(),
97
+ UnionRule(ExportableTool, UvSubsystem),
98
+ )
@@ -1,138 +0,0 @@
1
- """Rules for Ruff formatting."""
2
-
3
- from dataclasses import dataclass
4
- from typing import Iterable
5
-
6
- from pants.core.goals.fmt import FmtResult, FmtTargetsRequest, PartitionerType
7
- from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
8
- from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
9
- from pants.engine.fs import Digest, MergeDigests, Snapshot
10
- from pants.engine.platform import Platform
11
- from pants.engine.process import FallibleProcessResult, Process
12
- from pants.engine.rules import Get, MultiGet, collect_rules, rule
13
- from pants.engine.target import FieldSet, Target
14
- from pants.util.logging import LogLevel
15
-
16
- from pants_baseline.subsystems.baseline import BaselineSubsystem
17
- from pants_baseline.subsystems.ruff import RuffSubsystem
18
- from pants_baseline.targets import BaselineSourcesField, SkipFormatField
19
-
20
-
21
- @dataclass(frozen=True)
22
- class RuffFmtFieldSet(FieldSet):
23
- """Field set for Ruff formatting."""
24
-
25
- required_fields = (BaselineSourcesField,)
26
-
27
- sources: BaselineSourcesField
28
- skip_fmt: SkipFormatField
29
-
30
- @classmethod
31
- def opt_out(cls, tgt: Target) -> bool:
32
- """Allow targets to opt out of formatting."""
33
- return tgt.get(SkipFormatField).value
34
-
35
-
36
- class RuffFmtRequest(FmtTargetsRequest):
37
- """Request to run Ruff formatting."""
38
-
39
- field_set_type = RuffFmtFieldSet
40
- tool_subsystem = RuffSubsystem
41
- partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
42
-
43
-
44
- @rule(desc="Format with Ruff", level=LogLevel.DEBUG)
45
- async def run_ruff_fmt(
46
- request: RuffFmtRequest.Batch,
47
- ruff_subsystem: RuffSubsystem,
48
- baseline_subsystem: BaselineSubsystem,
49
- platform: Platform,
50
- ) -> FmtResult:
51
- """Run Ruff formatter on Python files."""
52
- field_sets = request.elements
53
- snapshot = request.snapshot
54
-
55
- if not baseline_subsystem.enabled:
56
- return FmtResult(
57
- input=snapshot,
58
- output=snapshot,
59
- stdout="",
60
- stderr="",
61
- formatter_name="ruff",
62
- )
63
-
64
- if not field_sets:
65
- return FmtResult(
66
- input=snapshot,
67
- output=snapshot,
68
- stdout="No targets to format",
69
- stderr="",
70
- formatter_name="ruff",
71
- )
72
-
73
- # Download ruff and get source files in parallel
74
- downloaded_ruff, sources = await MultiGet(
75
- Get(DownloadedExternalTool, ExternalToolRequest, ruff_subsystem.get_request(platform)),
76
- Get(
77
- SourceFiles,
78
- SourceFilesRequest(
79
- sources_fields=[fs.sources for fs in field_sets],
80
- for_sources_types=(BaselineSourcesField,),
81
- ),
82
- ),
83
- )
84
-
85
- if not sources.files:
86
- return FmtResult(
87
- input=snapshot,
88
- output=snapshot,
89
- stdout="No files to format",
90
- stderr="",
91
- formatter_name="ruff",
92
- )
93
-
94
- # Merge the ruff binary with the source files
95
- input_digest = await Get(
96
- Digest,
97
- MergeDigests([downloaded_ruff.digest, sources.snapshot.digest]),
98
- )
99
-
100
- # Build Ruff format command
101
- argv = [
102
- downloaded_ruff.exe,
103
- "format",
104
- f"--target-version=py{baseline_subsystem.python_version.replace('.', '')}",
105
- f"--line-length={baseline_subsystem.line_length}",
106
- f"--quote-style={ruff_subsystem.quote_style}",
107
- f"--indent-style={ruff_subsystem.indent_style}",
108
- *sources.files,
109
- ]
110
-
111
- process = Process(
112
- argv=argv,
113
- input_digest=input_digest,
114
- output_files=sources.files,
115
- description=f"Run Ruff format on {len(sources.files)} files",
116
- level=LogLevel.DEBUG,
117
- )
118
-
119
- result = await Get(FallibleProcessResult, Process, process)
120
-
121
- output_digest: Digest = result.output_digest
122
- output_snapshot = await Get(Snapshot, Digest, output_digest)
123
-
124
- return FmtResult(
125
- input=sources.snapshot,
126
- output=output_snapshot,
127
- stdout=result.stdout.decode(),
128
- stderr=result.stderr.decode(),
129
- formatter_name="ruff",
130
- )
131
-
132
-
133
- def rules() -> Iterable:
134
- """Return all format rules."""
135
- return [
136
- *collect_rules(),
137
- *RuffFmtRequest.rules(),
138
- ]
@@ -1,138 +0,0 @@
1
- """Rules for Ruff linting."""
2
-
3
- from dataclasses import dataclass
4
- from typing import Iterable
5
-
6
- from pants.core.goals.lint import LintResult, LintTargetsRequest, PartitionerType
7
- from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
8
- from pants.core.util_rules.partitions import Partitions
9
- from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
10
- from pants.engine.fs import Digest, MergeDigests
11
- from pants.engine.platform import Platform
12
- from pants.engine.process import FallibleProcessResult, Process
13
- from pants.engine.rules import Get, MultiGet, collect_rules, rule
14
- from pants.engine.target import FieldSet, Target
15
- from pants.util.logging import LogLevel
16
-
17
- from pants_baseline.subsystems.baseline import BaselineSubsystem
18
- from pants_baseline.subsystems.ruff import RuffSubsystem
19
- from pants_baseline.targets import BaselineSourcesField, SkipLintField
20
-
21
-
22
- @dataclass(frozen=True)
23
- class RuffLintFieldSet(FieldSet):
24
- """Field set for Ruff linting."""
25
-
26
- required_fields = (BaselineSourcesField,)
27
-
28
- sources: BaselineSourcesField
29
- skip_lint: SkipLintField
30
-
31
- @classmethod
32
- def opt_out(cls, tgt: Target) -> bool:
33
- """Allow targets to opt out of linting."""
34
- return tgt.get(SkipLintField).value
35
-
36
-
37
- class RuffLintRequest(LintTargetsRequest):
38
- """Request to run Ruff linting."""
39
-
40
- field_set_type = RuffLintFieldSet
41
- tool_subsystem = RuffSubsystem
42
- partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
43
-
44
-
45
- @rule(desc="Lint with Ruff", level=LogLevel.DEBUG)
46
- async def run_ruff_lint(
47
- request: RuffLintRequest.Batch,
48
- ruff_subsystem: RuffSubsystem,
49
- baseline_subsystem: BaselineSubsystem,
50
- platform: Platform,
51
- ) -> LintResult:
52
- """Run Ruff linter on Python files."""
53
- if not baseline_subsystem.enabled:
54
- return LintResult(
55
- exit_code=0,
56
- stdout="",
57
- stderr="",
58
- linter_name="ruff",
59
- partition_description=None,
60
- )
61
-
62
- field_sets = request.elements
63
-
64
- if not field_sets:
65
- return LintResult(
66
- exit_code=0,
67
- stdout="No targets to lint",
68
- stderr="",
69
- linter_name="ruff",
70
- partition_description=None,
71
- )
72
-
73
- # Download ruff and get source files in parallel
74
- downloaded_ruff, sources = await MultiGet(
75
- Get(DownloadedExternalTool, ExternalToolRequest, ruff_subsystem.get_request(platform)),
76
- Get(
77
- SourceFiles,
78
- SourceFilesRequest(
79
- sources_fields=[fs.sources for fs in field_sets],
80
- for_sources_types=(BaselineSourcesField,),
81
- ),
82
- ),
83
- )
84
-
85
- if not sources.files:
86
- return LintResult(
87
- exit_code=0,
88
- stdout="No files to lint",
89
- stderr="",
90
- linter_name="ruff",
91
- partition_description=None,
92
- )
93
-
94
- # Merge the ruff binary with the source files
95
- input_digest = await Get(
96
- Digest,
97
- MergeDigests([downloaded_ruff.digest, sources.snapshot.digest]),
98
- )
99
-
100
- # Build Ruff command
101
- select_args = [f"--select={','.join(ruff_subsystem.select)}"] if ruff_subsystem.select else []
102
- ignore_args = [f"--ignore={','.join(ruff_subsystem.ignore)}"] if ruff_subsystem.ignore else []
103
-
104
- argv = [
105
- downloaded_ruff.exe,
106
- "check",
107
- f"--target-version=py{baseline_subsystem.python_version.replace('.', '')}",
108
- f"--line-length={baseline_subsystem.line_length}",
109
- *select_args,
110
- *ignore_args,
111
- "--output-format=text",
112
- *sources.files,
113
- ]
114
-
115
- process = Process(
116
- argv=argv,
117
- input_digest=input_digest,
118
- description=f"Run Ruff lint on {len(sources.files)} files",
119
- level=LogLevel.DEBUG,
120
- )
121
-
122
- result = await Get(FallibleProcessResult, Process, process)
123
-
124
- return LintResult(
125
- exit_code=result.exit_code,
126
- stdout=result.stdout.decode(),
127
- stderr=result.stderr.decode(),
128
- linter_name="ruff",
129
- partition_description=request.partition_metadata,
130
- )
131
-
132
-
133
- def rules() -> Iterable:
134
- """Return all lint rules."""
135
- return [
136
- *collect_rules(),
137
- *RuffLintRequest.rules(),
138
- ]