jaymd96-pants-baseline 0.1.2__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
- {jaymd96_pants_baseline-0.1.2.dist-info → jaymd96_pants_baseline-0.2.0.dist-info}/METADATA +36 -3
- jaymd96_pants_baseline-0.2.0.dist-info/RECORD +26 -0
- pants_baseline/__about__.py +1 -1
- pants_baseline/bundled_claude_plugins.py +29 -0
- pants_baseline/goals/audit.py +3 -4
- pants_baseline/goals/fmt.py +37 -11
- pants_baseline/goals/lint.py +36 -14
- pants_baseline/goals/test.py +1 -0
- pants_baseline/goals/typecheck.py +41 -11
- pants_baseline/register.py +17 -16
- pants_baseline/rules/audit_rules.py +30 -4
- pants_baseline/rules/fmt_rules.py +47 -29
- pants_baseline/rules/lint_rules.py +33 -28
- pants_baseline/rules/test_rules.py +6 -8
- pants_baseline/rules/typecheck_rules.py +30 -22
- pants_baseline/subsystems/baseline.py +1 -1
- pants_baseline/subsystems/ruff.py +31 -7
- pants_baseline/subsystems/ty.py +27 -6
- pants_baseline/subsystems/uv.py +27 -6
- pants_baseline/targets.py +6 -5
- jaymd96_pants_baseline-0.1.2.dist-info/RECORD +0 -25
- {jaymd96_pants_baseline-0.1.2.dist-info → jaymd96_pants_baseline-0.2.0.dist-info}/WHEEL +0 -0
- {jaymd96_pants_baseline-0.1.2.dist-info → jaymd96_pants_baseline-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jaymd96-pants-baseline
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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
|
|
@@ -21,6 +21,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Build Tools
|
|
22
22
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
23
|
Requires-Python: <4,>=3.11
|
|
24
|
+
Provides-Extra: claude
|
|
25
|
+
Requires-Dist: jaymd96-pants-claude-plugins>=0.2.0; extra == 'claude'
|
|
24
26
|
Provides-Extra: dev
|
|
25
27
|
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
26
28
|
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
@@ -54,7 +56,7 @@ backend_packages = [
|
|
|
54
56
|
]
|
|
55
57
|
|
|
56
58
|
plugins = [
|
|
57
|
-
"jaymd96-pants-baseline==0.
|
|
59
|
+
"jaymd96-pants-baseline==0.2.0",
|
|
58
60
|
]
|
|
59
61
|
```
|
|
60
62
|
|
|
@@ -308,7 +310,7 @@ backend_packages = [
|
|
|
308
310
|
"pants.backend.python",
|
|
309
311
|
"pants_baseline",
|
|
310
312
|
]
|
|
311
|
-
plugins = ["jaymd96-pants-baseline==0.
|
|
313
|
+
plugins = ["jaymd96-pants-baseline==0.2.0"]
|
|
312
314
|
|
|
313
315
|
[python]
|
|
314
316
|
interpreter_constraints = ["CPython>=3.13,<4"]
|
|
@@ -346,6 +348,37 @@ strict = true
|
|
|
346
348
|
| MyPy/Pyright | ty |
|
|
347
349
|
| pip-audit | uv audit |
|
|
348
350
|
|
|
351
|
+
## Claude Code Integration
|
|
352
|
+
|
|
353
|
+
This plugin bundles recommended [Claude Code](https://claude.ai/code) plugins for enhanced AI-assisted development workflows.
|
|
354
|
+
|
|
355
|
+
### Install Bundled Claude Plugins
|
|
356
|
+
|
|
357
|
+
When combined with [jaymd96-pants-claude-plugins](https://github.com/jaymd96/pants-claude-plugins):
|
|
358
|
+
|
|
359
|
+
```toml
|
|
360
|
+
# pants.toml
|
|
361
|
+
[GLOBAL]
|
|
362
|
+
plugins = [
|
|
363
|
+
"jaymd96-pants-baseline==0.2.0",
|
|
364
|
+
"jaymd96-pants-claude-plugins>=0.2.0",
|
|
365
|
+
]
|
|
366
|
+
backend_packages = [
|
|
367
|
+
"pants_baseline",
|
|
368
|
+
"pants_claude_plugins",
|
|
369
|
+
]
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Then run:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
pants claude-install --include-bundled ::
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
This automatically installs:
|
|
379
|
+
- **github** - GitHub integration for PR workflows and issue management
|
|
380
|
+
- **commit-commands** - Git workflow commands for commits, pushes, and PRs
|
|
381
|
+
|
|
349
382
|
## Development
|
|
350
383
|
|
|
351
384
|
### Setup
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
pants_baseline/__about__.py,sha256=wxKDEvde9_K-nhyadAqBTbIh_mR7ckFwDJRU14BkV8A,98
|
|
2
|
+
pants_baseline/__init__.py,sha256=uVRGi1D2gFjc7emmeewWdcvpO-NsUuKsMbX3rztOxWU,655
|
|
3
|
+
pants_baseline/bundled_claude_plugins.py,sha256=jfNzFrvZBvv8Iz1XMLomgIa993ZYH_DMNOk-apFs8-g,926
|
|
4
|
+
pants_baseline/register.py,sha256=gdODsaI7VIzU2tGONANsg9KbKZkHidmLs2AyvUNIypg,1675
|
|
5
|
+
pants_baseline/targets.py,sha256=Z9O09Aqd5inMqwIUFt4HSE9HtWjn8pGSU4FZHg22Tpo,3379
|
|
6
|
+
pants_baseline/goals/__init__.py,sha256=pf6KU2CIQuDkx8ER3IS0H-kuNbBtX-AH5B5SnSP9_yw,192
|
|
7
|
+
pants_baseline/goals/audit.py,sha256=i7zyyKssWw6EE2zN5putWP9sikkcjUKn7g3Jrapv994,2380
|
|
8
|
+
pants_baseline/goals/fmt.py,sha256=CyUPhy42rscUzJp4DhSRp3FGhGYcIKN42b46l1hJ9tY,2432
|
|
9
|
+
pants_baseline/goals/lint.py,sha256=vGyG-wvjgjE4dgglmTOiFNnng2bQbOQXkZ3Fd2HdiOU,2435
|
|
10
|
+
pants_baseline/goals/test.py,sha256=THW4kJAFbAzPCjLbq1dxg81T19QdywXYiwTNKSB4z8M,1653
|
|
11
|
+
pants_baseline/goals/typecheck.py,sha256=nqfwy1BZqhEzlDX-_hme9mKuDI2KqH-XpN4ygzWMQ6Y,2626
|
|
12
|
+
pants_baseline/rules/__init__.py,sha256=UpvDpGVImhRfp2_VeUNsRPGiWjBbMI6AV1-Yx3kS0Gg,252
|
|
13
|
+
pants_baseline/rules/audit_rules.py,sha256=gr-2u0u07z62Q42c9vXVGJng8UPIP5JzZ-SKrl9z7yM,2762
|
|
14
|
+
pants_baseline/rules/fmt_rules.py,sha256=O-JQVk565H8w6jlsG-cqndzNaig3alepqTV0zKhfyDw,4174
|
|
15
|
+
pants_baseline/rules/lint_rules.py,sha256=rYsbaPyYNDVDJn_dF91ews8iskcj2QoqnzZInLxkIzc,4219
|
|
16
|
+
pants_baseline/rules/test_rules.py,sha256=jj4lk3-mueOPujPFPK2TCdBFZZA_vovdDdHED9jGp7U,4225
|
|
17
|
+
pants_baseline/rules/typecheck_rules.py,sha256=XmL6uIXwjphB1YDVMrCM6A_Wa_1H-gHuLg8x59v_YQo,4499
|
|
18
|
+
pants_baseline/subsystems/__init__.py,sha256=LteH_qmUIgRAnXYmmi7f6o894QfpY3hMNH5dlvJbSoM,387
|
|
19
|
+
pants_baseline/subsystems/baseline.py,sha256=KWDRMdLOUN5cNHntY8f97bSsoITXwmX4ET6n2aW-cRk,2049
|
|
20
|
+
pants_baseline/subsystems/ruff.py,sha256=NW0AFv59-j6ANkZG8LjvuXPzWBD4yDsEr7Pb6eZbdck,3941
|
|
21
|
+
pants_baseline/subsystems/ty.py,sha256=OpaU8Z7Bk6kj5QAfhPom5L9v8sKNR8XF664_U5mUVJw,3419
|
|
22
|
+
pants_baseline/subsystems/uv.py,sha256=dXmVzg4ZxzHa4g_TowEZXMYuhKiTOHjqqadkNrZQ9jk,3066
|
|
23
|
+
jaymd96_pants_baseline-0.2.0.dist-info/METADATA,sha256=XsPPcHLtQ4TdFAAjbezNEDcPhcw_Hj7t68NiNwlfcIc,8764
|
|
24
|
+
jaymd96_pants_baseline-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
25
|
+
jaymd96_pants_baseline-0.2.0.dist-info/licenses/LICENSE,sha256=oLGLZv7XKM_oKCbdMW1bZB37SXsdexmhNSuh3Xg4m4I,10754
|
|
26
|
+
jaymd96_pants_baseline-0.2.0.dist-info/RECORD,,
|
pants_baseline/__about__.py
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Bundled Claude Code plugins for pants-baseline.
|
|
2
|
+
|
|
3
|
+
These plugins are automatically discovered and installed when users run:
|
|
4
|
+
pants claude-install --include-bundled ::
|
|
5
|
+
|
|
6
|
+
This requires the jaymd96-pants-claude-plugins package to be installed.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Marketplaces to add before installing plugins
|
|
10
|
+
# The demo marketplace contains commit-commands and other workflow plugins
|
|
11
|
+
BUNDLED_MARKETPLACES = [
|
|
12
|
+
"anthropics/claude-code", # Demo plugins marketplace
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
# Claude Code plugins to install
|
|
16
|
+
BUNDLED_CLAUDE_PLUGINS = [
|
|
17
|
+
{
|
|
18
|
+
"plugin": "github",
|
|
19
|
+
"marketplace": "claude-plugins-official",
|
|
20
|
+
"scope": "project",
|
|
21
|
+
"description": "GitHub integration for PR workflows and issue management",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"plugin": "commit-commands",
|
|
25
|
+
"marketplace": "anthropics-claude-code",
|
|
26
|
+
"scope": "project",
|
|
27
|
+
"description": "Git workflow commands for commits, pushes, and PRs",
|
|
28
|
+
},
|
|
29
|
+
]
|
pants_baseline/goals/audit.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Audit goal for uv security scanning."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
from typing import Iterable
|
|
6
4
|
|
|
7
5
|
from pants.engine.console import Console
|
|
@@ -25,6 +23,7 @@ class BaselineAudit(Goal):
|
|
|
25
23
|
"""Goal to run uv security audit."""
|
|
26
24
|
|
|
27
25
|
subsystem_cls = BaselineAuditSubsystem
|
|
26
|
+
environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
@goal_rule
|
|
@@ -49,12 +48,12 @@ async def run_baseline_audit(
|
|
|
49
48
|
console.print_stdout(f" Ignoring: {', '.join(uv_subsystem.audit_ignore_vulns)}")
|
|
50
49
|
console.print_stdout("")
|
|
51
50
|
|
|
52
|
-
audit_request = UvAuditRequest(
|
|
51
|
+
audit_request: UvAuditRequest = UvAuditRequest(
|
|
53
52
|
lock_file=uv_subsystem.lock_file,
|
|
54
53
|
ignore_vulns=tuple(uv_subsystem.audit_ignore_vulns),
|
|
55
54
|
output_format=uv_subsystem.output_format,
|
|
56
55
|
)
|
|
57
|
-
result = await Get(AuditResult,
|
|
56
|
+
result = await Get(AuditResult, UvAuditRequest, audit_request)
|
|
58
57
|
|
|
59
58
|
if result.stdout:
|
|
60
59
|
console.print_stdout(result.stdout)
|
pants_baseline/goals/fmt.py
CHANGED
|
@@ -4,13 +4,16 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Iterable
|
|
6
6
|
|
|
7
|
+
from pants.core.goals.fmt import FmtResult
|
|
7
8
|
from pants.engine.console import Console
|
|
8
9
|
from pants.engine.goal import Goal, GoalSubsystem
|
|
9
|
-
from pants.engine.rules import collect_rules, goal_rule
|
|
10
|
-
from pants.engine.target import
|
|
10
|
+
from pants.engine.rules import Get, collect_rules, goal_rule
|
|
11
|
+
from pants.engine.target import FilteredTargets
|
|
11
12
|
|
|
13
|
+
from pants_baseline.rules.fmt_rules import RuffFmtFieldSet, RuffFmtRequest
|
|
12
14
|
from pants_baseline.subsystems.baseline import BaselineSubsystem
|
|
13
15
|
from pants_baseline.subsystems.ruff import RuffSubsystem
|
|
16
|
+
from pants_baseline.targets import BaselineSourcesField
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class BaselineFmtSubsystem(GoalSubsystem):
|
|
@@ -24,12 +27,13 @@ class BaselineFmt(Goal):
|
|
|
24
27
|
"""Goal to run Ruff formatting."""
|
|
25
28
|
|
|
26
29
|
subsystem_cls = BaselineFmtSubsystem
|
|
30
|
+
environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
@goal_rule
|
|
30
34
|
async def run_baseline_fmt(
|
|
31
35
|
console: Console,
|
|
32
|
-
targets:
|
|
36
|
+
targets: FilteredTargets,
|
|
33
37
|
baseline_subsystem: BaselineSubsystem,
|
|
34
38
|
ruff_subsystem: RuffSubsystem,
|
|
35
39
|
) -> BaselineFmt:
|
|
@@ -38,15 +42,37 @@ async def run_baseline_fmt(
|
|
|
38
42
|
console.print_stdout("Python baseline is disabled.")
|
|
39
43
|
return BaselineFmt(exit_code=0)
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.print_stdout("")
|
|
45
|
+
# Filter targets that have BaselineSourcesField
|
|
46
|
+
applicable_targets = [
|
|
47
|
+
t for t in targets
|
|
48
|
+
if t.has_field(BaselineSourcesField)
|
|
49
|
+
]
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
if not applicable_targets:
|
|
52
|
+
console.print_stdout("No baseline_python_project targets found.")
|
|
53
|
+
return BaselineFmt(exit_code=0)
|
|
54
|
+
|
|
55
|
+
# Create field sets for each target
|
|
56
|
+
field_sets = [
|
|
57
|
+
RuffFmtFieldSet.create(t)
|
|
58
|
+
for t in applicable_targets
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Create the fmt request and run it
|
|
62
|
+
request = RuffFmtRequest(field_sets)
|
|
63
|
+
result = await Get(FmtResult, RuffFmtRequest, request)
|
|
64
|
+
|
|
65
|
+
# Print results
|
|
66
|
+
if result.stdout:
|
|
67
|
+
console.print_stdout(result.stdout)
|
|
68
|
+
if result.stderr:
|
|
69
|
+
console.print_stderr(result.stderr)
|
|
70
|
+
|
|
71
|
+
files_changed = result.input != result.output
|
|
72
|
+
if files_changed:
|
|
73
|
+
console.print_stdout(f"✓ Formatted {len(field_sets)} target(s)")
|
|
74
|
+
else:
|
|
75
|
+
console.print_stdout(f"✓ {len(field_sets)} target(s) already formatted")
|
|
50
76
|
|
|
51
77
|
return BaselineFmt(exit_code=0)
|
|
52
78
|
|
pants_baseline/goals/lint.py
CHANGED
|
@@ -4,17 +4,16 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Iterable
|
|
6
6
|
|
|
7
|
-
from pants.core.goals.lint import
|
|
7
|
+
from pants.core.goals.lint import LintResult
|
|
8
8
|
from pants.engine.console import Console
|
|
9
|
-
from pants.engine.fs import PathGlobs, Paths
|
|
10
9
|
from pants.engine.goal import Goal, GoalSubsystem
|
|
11
10
|
from pants.engine.rules import Get, collect_rules, goal_rule
|
|
12
|
-
from pants.engine.target import
|
|
13
|
-
from pants.util.logging import LogLevel
|
|
11
|
+
from pants.engine.target import FieldSet, FilteredTargets
|
|
14
12
|
|
|
15
|
-
from pants_baseline.rules.lint_rules import RuffLintRequest
|
|
13
|
+
from pants_baseline.rules.lint_rules import RuffLintFieldSet, RuffLintRequest
|
|
16
14
|
from pants_baseline.subsystems.baseline import BaselineSubsystem
|
|
17
15
|
from pants_baseline.subsystems.ruff import RuffSubsystem
|
|
16
|
+
from pants_baseline.targets import BaselineSourcesField
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class BaselineLintSubsystem(GoalSubsystem):
|
|
@@ -28,12 +27,13 @@ class BaselineLint(Goal):
|
|
|
28
27
|
"""Goal to run Ruff linting."""
|
|
29
28
|
|
|
30
29
|
subsystem_cls = BaselineLintSubsystem
|
|
30
|
+
environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
@goal_rule
|
|
34
34
|
async def run_baseline_lint(
|
|
35
35
|
console: Console,
|
|
36
|
-
targets:
|
|
36
|
+
targets: FilteredTargets,
|
|
37
37
|
baseline_subsystem: BaselineSubsystem,
|
|
38
38
|
ruff_subsystem: RuffSubsystem,
|
|
39
39
|
) -> BaselineLint:
|
|
@@ -42,16 +42,38 @@ async def run_baseline_lint(
|
|
|
42
42
|
console.print_stdout("Python baseline is disabled.")
|
|
43
43
|
return BaselineLint(exit_code=0)
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
# Filter targets that have BaselineSourcesField
|
|
46
|
+
applicable_targets = [
|
|
47
|
+
t for t in targets
|
|
48
|
+
if t.has_field(BaselineSourcesField)
|
|
49
|
+
]
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
if not applicable_targets:
|
|
52
|
+
console.print_stdout("No baseline_python_project targets found.")
|
|
53
|
+
return BaselineLint(exit_code=0)
|
|
54
|
+
|
|
55
|
+
# Create field sets for each target
|
|
56
|
+
field_sets = [
|
|
57
|
+
RuffLintFieldSet.create(t)
|
|
58
|
+
for t in applicable_targets
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Create the lint request and run it
|
|
62
|
+
request = RuffLintRequest(field_sets)
|
|
63
|
+
result = await Get(LintResult, RuffLintRequest, request)
|
|
64
|
+
|
|
65
|
+
# Print results
|
|
66
|
+
if result.stdout:
|
|
67
|
+
console.print_stdout(result.stdout)
|
|
68
|
+
if result.stderr:
|
|
69
|
+
console.print_stderr(result.stderr)
|
|
70
|
+
|
|
71
|
+
if result.exit_code == 0:
|
|
72
|
+
console.print_stdout(f"✓ Linted {len(field_sets)} target(s) successfully")
|
|
73
|
+
else:
|
|
74
|
+
console.print_stderr(f"✗ Linting failed with exit code {result.exit_code}")
|
|
53
75
|
|
|
54
|
-
return BaselineLint(exit_code=
|
|
76
|
+
return BaselineLint(exit_code=result.exit_code)
|
|
55
77
|
|
|
56
78
|
|
|
57
79
|
def rules() -> Iterable:
|
pants_baseline/goals/test.py
CHANGED
|
@@ -4,13 +4,16 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Iterable
|
|
6
6
|
|
|
7
|
+
from pants.core.goals.check import CheckResults
|
|
7
8
|
from pants.engine.console import Console
|
|
8
9
|
from pants.engine.goal import Goal, GoalSubsystem
|
|
9
|
-
from pants.engine.rules import collect_rules, goal_rule
|
|
10
|
-
from pants.engine.target import
|
|
10
|
+
from pants.engine.rules import Get, collect_rules, goal_rule
|
|
11
|
+
from pants.engine.target import FilteredTargets
|
|
11
12
|
|
|
13
|
+
from pants_baseline.rules.typecheck_rules import TyCheckRequest, TyFieldSet
|
|
12
14
|
from pants_baseline.subsystems.baseline import BaselineSubsystem
|
|
13
15
|
from pants_baseline.subsystems.ty import TySubsystem
|
|
16
|
+
from pants_baseline.targets import BaselineSourcesField
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class BaselineTypecheckSubsystem(GoalSubsystem):
|
|
@@ -24,12 +27,13 @@ class BaselineTypecheck(Goal):
|
|
|
24
27
|
"""Goal to run ty type checking."""
|
|
25
28
|
|
|
26
29
|
subsystem_cls = BaselineTypecheckSubsystem
|
|
30
|
+
environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
@goal_rule
|
|
30
34
|
async def run_baseline_typecheck(
|
|
31
35
|
console: Console,
|
|
32
|
-
targets:
|
|
36
|
+
targets: FilteredTargets,
|
|
33
37
|
baseline_subsystem: BaselineSubsystem,
|
|
34
38
|
ty_subsystem: TySubsystem,
|
|
35
39
|
) -> BaselineTypecheck:
|
|
@@ -38,16 +42,42 @@ async def run_baseline_typecheck(
|
|
|
38
42
|
console.print_stdout("Python baseline is disabled.")
|
|
39
43
|
return BaselineTypecheck(exit_code=0)
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
# Filter targets that have BaselineSourcesField
|
|
46
|
+
applicable_targets = [
|
|
47
|
+
t for t in targets
|
|
48
|
+
if t.has_field(BaselineSourcesField)
|
|
49
|
+
]
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
if not applicable_targets:
|
|
52
|
+
console.print_stdout("No baseline_python_project targets found.")
|
|
53
|
+
return BaselineTypecheck(exit_code=0)
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
# Create field sets for each target
|
|
56
|
+
field_sets = [
|
|
57
|
+
TyFieldSet.create(t)
|
|
58
|
+
for t in applicable_targets
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Create the check request and run it
|
|
62
|
+
request = TyCheckRequest(field_sets)
|
|
63
|
+
results = await Get(CheckResults, TyCheckRequest, request)
|
|
64
|
+
|
|
65
|
+
# Print results
|
|
66
|
+
exit_code = 0
|
|
67
|
+
for result in results.results:
|
|
68
|
+
if result.stdout:
|
|
69
|
+
console.print_stdout(result.stdout)
|
|
70
|
+
if result.stderr:
|
|
71
|
+
console.print_stderr(result.stderr)
|
|
72
|
+
if result.exit_code != 0:
|
|
73
|
+
exit_code = result.exit_code
|
|
74
|
+
|
|
75
|
+
if exit_code == 0:
|
|
76
|
+
console.print_stdout(f"✓ Type checked {len(field_sets)} target(s) successfully")
|
|
77
|
+
else:
|
|
78
|
+
console.print_stderr(f"✗ Type checking failed with exit code {exit_code}")
|
|
79
|
+
|
|
80
|
+
return BaselineTypecheck(exit_code=exit_code)
|
|
51
81
|
|
|
52
82
|
|
|
53
83
|
def rules() -> Iterable:
|
pants_baseline/register.py
CHANGED
|
@@ -6,7 +6,7 @@ It registers all rules, targets, and subsystems provided by this plugin.
|
|
|
6
6
|
|
|
7
7
|
from typing import Iterable
|
|
8
8
|
|
|
9
|
-
from pants.engine.rules import Rule
|
|
9
|
+
from pants.engine.rules import Rule
|
|
10
10
|
|
|
11
11
|
from pants_baseline.goals import audit as audit_goal
|
|
12
12
|
from pants_baseline.goals import fmt as fmt_goal
|
|
@@ -22,22 +22,23 @@ from pants_baseline.targets import BaselinePythonProject
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def rules() -> Iterable[Rule]:
|
|
25
|
-
"""Return all rules provided by this plugin.
|
|
25
|
+
"""Return all rules provided by this plugin.
|
|
26
|
+
|
|
27
|
+
Each module's rules() function returns both @rule decorated functions
|
|
28
|
+
AND UnionRule registrations. We must call the rules() functions directly
|
|
29
|
+
rather than using collect_rules() which only collects @rule functions.
|
|
30
|
+
"""
|
|
26
31
|
return [
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*BaselineSubsystem.rules(),
|
|
38
|
-
*RuffSubsystem.rules(),
|
|
39
|
-
*TySubsystem.rules(),
|
|
40
|
-
*UvSubsystem.rules(),
|
|
32
|
+
*lint_rules.rules(),
|
|
33
|
+
*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(),
|
|
41
42
|
]
|
|
42
43
|
|
|
43
44
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
"""Rules for uv security auditing."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
from dataclasses import dataclass
|
|
6
4
|
from typing import Iterable
|
|
7
5
|
|
|
6
|
+
from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
|
|
7
|
+
from pants.engine.fs import Digest, MergeDigests, PathGlobs, Snapshot
|
|
8
|
+
from pants.engine.platform import Platform
|
|
8
9
|
from pants.engine.process import FallibleProcessResult, Process
|
|
9
10
|
from pants.engine.rules import Get, collect_rules, rule
|
|
10
11
|
from pants.util.logging import LogLevel
|
|
11
12
|
|
|
13
|
+
from pants_baseline.subsystems.uv import UvSubsystem
|
|
14
|
+
|
|
12
15
|
|
|
13
16
|
@dataclass(frozen=True)
|
|
14
17
|
class AuditResult:
|
|
@@ -32,15 +35,37 @@ class UvAuditRequest:
|
|
|
32
35
|
@rule(desc="Audit dependencies with uv", level=LogLevel.DEBUG)
|
|
33
36
|
async def run_uv_audit(
|
|
34
37
|
request: UvAuditRequest,
|
|
38
|
+
uv_subsystem: UvSubsystem,
|
|
39
|
+
platform: Platform,
|
|
35
40
|
) -> AuditResult:
|
|
36
41
|
"""Run uv audit on dependencies."""
|
|
42
|
+
# Download uv
|
|
43
|
+
downloaded_uv = await Get(
|
|
44
|
+
DownloadedExternalTool,
|
|
45
|
+
ExternalToolRequest,
|
|
46
|
+
uv_subsystem.get_request(platform),
|
|
47
|
+
)
|
|
48
|
+
|
|
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"]),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Merge the uv binary with lock files
|
|
56
|
+
input_digest = await Get(
|
|
57
|
+
Digest,
|
|
58
|
+
MergeDigests([downloaded_uv.digest, lock_file_snapshot.digest]),
|
|
59
|
+
)
|
|
60
|
+
|
|
37
61
|
# Build ignore args
|
|
38
62
|
ignore_args = []
|
|
39
63
|
for vuln in request.ignore_vulns:
|
|
40
64
|
ignore_args.extend(["--ignore", vuln])
|
|
41
65
|
|
|
42
66
|
argv = [
|
|
43
|
-
|
|
67
|
+
downloaded_uv.exe,
|
|
68
|
+
"pip",
|
|
44
69
|
"audit",
|
|
45
70
|
f"--output-format={request.output_format}",
|
|
46
71
|
*ignore_args,
|
|
@@ -48,11 +73,12 @@ async def run_uv_audit(
|
|
|
48
73
|
|
|
49
74
|
process = Process(
|
|
50
75
|
argv=argv,
|
|
76
|
+
input_digest=input_digest,
|
|
51
77
|
description="Run uv security audit",
|
|
52
78
|
level=LogLevel.DEBUG,
|
|
53
79
|
)
|
|
54
80
|
|
|
55
|
-
result = await Get(FallibleProcessResult,
|
|
81
|
+
result = await Get(FallibleProcessResult, Process, process)
|
|
56
82
|
|
|
57
83
|
stdout = result.stdout.decode()
|
|
58
84
|
stderr = result.stderr.decode()
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
"""Rules for Ruff formatting."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
from dataclasses import dataclass
|
|
6
4
|
from typing import Iterable
|
|
7
5
|
|
|
8
|
-
from pants.core.goals.fmt import FmtResult, FmtTargetsRequest
|
|
6
|
+
from pants.core.goals.fmt import FmtResult, FmtTargetsRequest, PartitionerType
|
|
7
|
+
from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
|
|
9
8
|
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
|
|
10
|
-
from pants.engine.fs import Digest, Snapshot
|
|
9
|
+
from pants.engine.fs import Digest, MergeDigests, Snapshot
|
|
10
|
+
from pants.engine.platform import Platform
|
|
11
11
|
from pants.engine.process import FallibleProcessResult, Process
|
|
12
|
-
from pants.engine.rules import Get, collect_rules, rule
|
|
13
|
-
from pants.engine.target import FieldSet
|
|
14
|
-
from pants.engine.unions import UnionRule
|
|
12
|
+
from pants.engine.rules import Get, MultiGet, collect_rules, rule
|
|
13
|
+
from pants.engine.target import FieldSet, Target
|
|
15
14
|
from pants.util.logging import LogLevel
|
|
16
15
|
|
|
17
16
|
from pants_baseline.subsystems.baseline import BaselineSubsystem
|
|
@@ -28,63 +27,81 @@ class RuffFmtFieldSet(FieldSet):
|
|
|
28
27
|
sources: BaselineSourcesField
|
|
29
28
|
skip_fmt: SkipFormatField
|
|
30
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
|
+
|
|
31
35
|
|
|
32
36
|
class RuffFmtRequest(FmtTargetsRequest):
|
|
33
37
|
"""Request to run Ruff formatting."""
|
|
34
38
|
|
|
35
39
|
field_set_type = RuffFmtFieldSet
|
|
36
|
-
|
|
40
|
+
tool_subsystem = RuffSubsystem
|
|
41
|
+
partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
|
|
37
42
|
|
|
38
43
|
|
|
39
44
|
@rule(desc="Format with Ruff", level=LogLevel.DEBUG)
|
|
40
45
|
async def run_ruff_fmt(
|
|
41
|
-
request: RuffFmtRequest,
|
|
46
|
+
request: RuffFmtRequest.Batch,
|
|
42
47
|
ruff_subsystem: RuffSubsystem,
|
|
43
48
|
baseline_subsystem: BaselineSubsystem,
|
|
49
|
+
platform: Platform,
|
|
44
50
|
) -> FmtResult:
|
|
45
51
|
"""Run Ruff formatter on Python files."""
|
|
52
|
+
field_sets = request.elements
|
|
53
|
+
snapshot = request.snapshot
|
|
54
|
+
|
|
46
55
|
if not baseline_subsystem.enabled:
|
|
47
56
|
return FmtResult(
|
|
48
|
-
input=
|
|
49
|
-
output=
|
|
57
|
+
input=snapshot,
|
|
58
|
+
output=snapshot,
|
|
50
59
|
stdout="",
|
|
51
60
|
stderr="",
|
|
52
61
|
formatter_name="ruff",
|
|
53
62
|
)
|
|
54
63
|
|
|
55
|
-
# Filter out skipped targets
|
|
56
|
-
field_sets = [fs for fs in request.field_sets if not fs.skip_fmt.value]
|
|
57
|
-
|
|
58
64
|
if not field_sets:
|
|
59
65
|
return FmtResult(
|
|
60
|
-
input=
|
|
61
|
-
output=
|
|
66
|
+
input=snapshot,
|
|
67
|
+
output=snapshot,
|
|
62
68
|
stdout="No targets to format",
|
|
63
69
|
stderr="",
|
|
64
70
|
formatter_name="ruff",
|
|
65
71
|
)
|
|
66
72
|
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
+
),
|
|
71
83
|
)
|
|
72
|
-
sources = await Get(SourceFiles, {SourceFilesRequest: source_files_request})
|
|
73
84
|
|
|
74
85
|
if not sources.files:
|
|
75
86
|
return FmtResult(
|
|
76
|
-
input=
|
|
77
|
-
output=
|
|
87
|
+
input=snapshot,
|
|
88
|
+
output=snapshot,
|
|
78
89
|
stdout="No files to format",
|
|
79
90
|
stderr="",
|
|
80
91
|
formatter_name="ruff",
|
|
81
92
|
)
|
|
82
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
|
+
|
|
83
100
|
# Build Ruff format command
|
|
84
101
|
argv = [
|
|
85
|
-
|
|
102
|
+
downloaded_ruff.exe,
|
|
86
103
|
"format",
|
|
87
|
-
f"--target-version={baseline_subsystem.
|
|
104
|
+
f"--target-version=py{baseline_subsystem.python_version.replace('.', '')}",
|
|
88
105
|
f"--line-length={baseline_subsystem.line_length}",
|
|
89
106
|
f"--quote-style={ruff_subsystem.quote_style}",
|
|
90
107
|
f"--indent-style={ruff_subsystem.indent_style}",
|
|
@@ -93,15 +110,16 @@ async def run_ruff_fmt(
|
|
|
93
110
|
|
|
94
111
|
process = Process(
|
|
95
112
|
argv=argv,
|
|
96
|
-
input_digest=
|
|
113
|
+
input_digest=input_digest,
|
|
97
114
|
output_files=sources.files,
|
|
98
115
|
description=f"Run Ruff format on {len(sources.files)} files",
|
|
99
116
|
level=LogLevel.DEBUG,
|
|
100
117
|
)
|
|
101
118
|
|
|
102
|
-
result = await Get(FallibleProcessResult,
|
|
119
|
+
result = await Get(FallibleProcessResult, Process, process)
|
|
103
120
|
|
|
104
|
-
|
|
121
|
+
output_digest: Digest = result.output_digest
|
|
122
|
+
output_snapshot = await Get(Snapshot, Digest, output_digest)
|
|
105
123
|
|
|
106
124
|
return FmtResult(
|
|
107
125
|
input=sources.snapshot,
|
|
@@ -116,5 +134,5 @@ def rules() -> Iterable:
|
|
|
116
134
|
"""Return all format rules."""
|
|
117
135
|
return [
|
|
118
136
|
*collect_rules(),
|
|
119
|
-
|
|
137
|
+
*RuffFmtRequest.rules(),
|
|
120
138
|
]
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"""Rules for Ruff linting."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
from dataclasses import dataclass
|
|
6
4
|
from typing import Iterable
|
|
7
5
|
|
|
8
|
-
from pants.core.goals.lint import LintResult, LintTargetsRequest
|
|
6
|
+
from pants.core.goals.lint import LintResult, LintTargetsRequest, PartitionerType
|
|
9
7
|
from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
|
|
8
|
+
from pants.core.util_rules.partitions import Partitions
|
|
10
9
|
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
|
|
11
10
|
from pants.engine.fs import Digest, MergeDigests
|
|
12
11
|
from pants.engine.platform import Platform
|
|
13
12
|
from pants.engine.process import FallibleProcessResult, Process
|
|
14
13
|
from pants.engine.rules import Get, MultiGet, collect_rules, rule
|
|
15
14
|
from pants.engine.target import FieldSet, Target
|
|
16
|
-
from pants.engine.unions import UnionRule
|
|
17
15
|
from pants.util.logging import LogLevel
|
|
18
16
|
|
|
19
17
|
from pants_baseline.subsystems.baseline import BaselineSubsystem
|
|
@@ -30,26 +28,23 @@ class RuffLintFieldSet(FieldSet):
|
|
|
30
28
|
sources: BaselineSourcesField
|
|
31
29
|
skip_lint: SkipLintField
|
|
32
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
|
+
|
|
33
36
|
|
|
34
37
|
class RuffLintRequest(LintTargetsRequest):
|
|
35
38
|
"""Request to run Ruff linting."""
|
|
36
39
|
|
|
37
40
|
field_set_type = RuffLintFieldSet
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@dataclass(frozen=True)
|
|
42
|
-
class RuffLintResult:
|
|
43
|
-
"""Result of running Ruff lint."""
|
|
44
|
-
|
|
45
|
-
exit_code: int
|
|
46
|
-
stdout: str
|
|
47
|
-
stderr: str
|
|
41
|
+
tool_subsystem = RuffSubsystem
|
|
42
|
+
partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
|
|
48
43
|
|
|
49
44
|
|
|
50
45
|
@rule(desc="Lint with Ruff", level=LogLevel.DEBUG)
|
|
51
46
|
async def run_ruff_lint(
|
|
52
|
-
request: RuffLintRequest,
|
|
47
|
+
request: RuffLintRequest.Batch,
|
|
53
48
|
ruff_subsystem: RuffSubsystem,
|
|
54
49
|
baseline_subsystem: BaselineSubsystem,
|
|
55
50
|
platform: Platform,
|
|
@@ -64,8 +59,7 @@ async def run_ruff_lint(
|
|
|
64
59
|
partition_description=None,
|
|
65
60
|
)
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
field_sets = [fs for fs in request.field_sets if not fs.skip_lint.value]
|
|
62
|
+
field_sets = request.elements
|
|
69
63
|
|
|
70
64
|
if not field_sets:
|
|
71
65
|
return LintResult(
|
|
@@ -76,12 +70,17 @@ async def run_ruff_lint(
|
|
|
76
70
|
partition_description=None,
|
|
77
71
|
)
|
|
78
72
|
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
83
|
)
|
|
84
|
-
sources = await Get(SourceFiles, {SourceFilesRequest: source_files_request})
|
|
85
84
|
|
|
86
85
|
if not sources.files:
|
|
87
86
|
return LintResult(
|
|
@@ -92,14 +91,20 @@ async def run_ruff_lint(
|
|
|
92
91
|
partition_description=None,
|
|
93
92
|
)
|
|
94
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
|
+
|
|
95
100
|
# Build Ruff command
|
|
96
101
|
select_args = [f"--select={','.join(ruff_subsystem.select)}"] if ruff_subsystem.select else []
|
|
97
102
|
ignore_args = [f"--ignore={','.join(ruff_subsystem.ignore)}"] if ruff_subsystem.ignore else []
|
|
98
103
|
|
|
99
104
|
argv = [
|
|
100
|
-
|
|
105
|
+
downloaded_ruff.exe,
|
|
101
106
|
"check",
|
|
102
|
-
f"--target-version={baseline_subsystem.
|
|
107
|
+
f"--target-version=py{baseline_subsystem.python_version.replace('.', '')}",
|
|
103
108
|
f"--line-length={baseline_subsystem.line_length}",
|
|
104
109
|
*select_args,
|
|
105
110
|
*ignore_args,
|
|
@@ -109,19 +114,19 @@ async def run_ruff_lint(
|
|
|
109
114
|
|
|
110
115
|
process = Process(
|
|
111
116
|
argv=argv,
|
|
112
|
-
input_digest=
|
|
117
|
+
input_digest=input_digest,
|
|
113
118
|
description=f"Run Ruff lint on {len(sources.files)} files",
|
|
114
119
|
level=LogLevel.DEBUG,
|
|
115
120
|
)
|
|
116
121
|
|
|
117
|
-
result = await Get(FallibleProcessResult,
|
|
122
|
+
result = await Get(FallibleProcessResult, Process, process)
|
|
118
123
|
|
|
119
124
|
return LintResult(
|
|
120
125
|
exit_code=result.exit_code,
|
|
121
126
|
stdout=result.stdout.decode(),
|
|
122
127
|
stderr=result.stderr.decode(),
|
|
123
128
|
linter_name="ruff",
|
|
124
|
-
partition_description=
|
|
129
|
+
partition_description=request.partition_metadata,
|
|
125
130
|
)
|
|
126
131
|
|
|
127
132
|
|
|
@@ -129,5 +134,5 @@ def rules() -> Iterable:
|
|
|
129
134
|
"""Return all lint rules."""
|
|
130
135
|
return [
|
|
131
136
|
*collect_rules(),
|
|
132
|
-
|
|
137
|
+
*RuffLintRequest.rules(),
|
|
133
138
|
]
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Rules for pytest testing with coverage."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
from dataclasses import dataclass
|
|
6
4
|
from typing import Iterable
|
|
7
5
|
|
|
@@ -73,18 +71,18 @@ async def run_pytest(
|
|
|
73
71
|
)
|
|
74
72
|
|
|
75
73
|
# Get test source files
|
|
76
|
-
test_source_files_request = SourceFilesRequest(
|
|
74
|
+
test_source_files_request: SourceFilesRequest = SourceFilesRequest(
|
|
77
75
|
sources_fields=[fs.test_sources for fs in field_sets],
|
|
78
76
|
for_sources_types=(BaselineTestSourcesField,),
|
|
79
77
|
)
|
|
80
|
-
test_sources = await Get(SourceFiles,
|
|
78
|
+
test_sources = await Get(SourceFiles, SourceFilesRequest, test_source_files_request)
|
|
81
79
|
|
|
82
80
|
# Get source files for coverage
|
|
83
|
-
source_files_request = SourceFilesRequest(
|
|
81
|
+
source_files_request: SourceFilesRequest = SourceFilesRequest(
|
|
84
82
|
sources_fields=[fs.sources for fs in field_sets],
|
|
85
83
|
for_sources_types=(BaselineSourcesField,),
|
|
86
84
|
)
|
|
87
|
-
sources = await Get(SourceFiles,
|
|
85
|
+
sources = await Get(SourceFiles, SourceFilesRequest, source_files_request)
|
|
88
86
|
|
|
89
87
|
if not test_sources.files:
|
|
90
88
|
return TestResult(
|
|
@@ -121,14 +119,14 @@ async def run_pytest(
|
|
|
121
119
|
*test_sources.files,
|
|
122
120
|
]
|
|
123
121
|
|
|
124
|
-
process = Process(
|
|
122
|
+
process: Process = Process(
|
|
125
123
|
argv=argv,
|
|
126
124
|
input_digest=test_sources.snapshot.digest,
|
|
127
125
|
description=f"Run pytest on {len(test_sources.files)} test files",
|
|
128
126
|
level=LogLevel.DEBUG,
|
|
129
127
|
)
|
|
130
128
|
|
|
131
|
-
result = await Get(FallibleProcessResult,
|
|
129
|
+
result = await Get(FallibleProcessResult, Process, process)
|
|
132
130
|
|
|
133
131
|
return TestResult(
|
|
134
132
|
exit_code=result.exit_code,
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"""Rules for ty type checking."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
from dataclasses import dataclass
|
|
6
4
|
from typing import Iterable
|
|
7
5
|
|
|
8
6
|
from pants.core.goals.check import CheckRequest, CheckResult, CheckResults
|
|
7
|
+
from pants.core.util_rules.external_tool import DownloadedExternalTool, ExternalToolRequest
|
|
9
8
|
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
|
|
9
|
+
from pants.engine.fs import Digest, MergeDigests
|
|
10
|
+
from pants.engine.platform import Platform
|
|
10
11
|
from pants.engine.process import FallibleProcessResult, Process
|
|
11
|
-
from pants.engine.rules import Get, collect_rules, rule
|
|
12
|
-
from pants.engine.target import FieldSet
|
|
12
|
+
from pants.engine.rules import Get, MultiGet, collect_rules, rule
|
|
13
|
+
from pants.engine.target import FieldSet, Target
|
|
13
14
|
from pants.engine.unions import UnionRule
|
|
14
15
|
from pants.util.logging import LogLevel
|
|
15
16
|
|
|
@@ -27,6 +28,11 @@ class TyFieldSet(FieldSet):
|
|
|
27
28
|
sources: BaselineSourcesField
|
|
28
29
|
skip_typecheck: SkipTypecheckField
|
|
29
30
|
|
|
31
|
+
@classmethod
|
|
32
|
+
def opt_out(cls, tgt: Target) -> bool:
|
|
33
|
+
"""Allow targets to opt out of type checking."""
|
|
34
|
+
return tgt.get(SkipTypecheckField).value
|
|
35
|
+
|
|
30
36
|
|
|
31
37
|
class TyCheckRequest(CheckRequest):
|
|
32
38
|
"""Request to run ty type checking."""
|
|
@@ -35,21 +41,12 @@ class TyCheckRequest(CheckRequest):
|
|
|
35
41
|
tool_name = "ty"
|
|
36
42
|
|
|
37
43
|
|
|
38
|
-
@dataclass(frozen=True)
|
|
39
|
-
class TyCheckResult:
|
|
40
|
-
"""Result of running ty type check."""
|
|
41
|
-
|
|
42
|
-
exit_code: int
|
|
43
|
-
stdout: str
|
|
44
|
-
stderr: str
|
|
45
|
-
partition_description: str | None = None
|
|
46
|
-
|
|
47
|
-
|
|
48
44
|
@rule(desc="Type check with ty", level=LogLevel.DEBUG)
|
|
49
45
|
async def run_ty_check(
|
|
50
46
|
request: TyCheckRequest,
|
|
51
47
|
ty_subsystem: TySubsystem,
|
|
52
48
|
baseline_subsystem: BaselineSubsystem,
|
|
49
|
+
platform: Platform,
|
|
53
50
|
) -> CheckResults:
|
|
54
51
|
"""Run ty type checker on Python files."""
|
|
55
52
|
if not baseline_subsystem.enabled:
|
|
@@ -81,12 +78,17 @@ async def run_ty_check(
|
|
|
81
78
|
checker_name="ty",
|
|
82
79
|
)
|
|
83
80
|
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
),
|
|
88
91
|
)
|
|
89
|
-
sources = await Get(SourceFiles, {SourceFilesRequest: source_files_request})
|
|
90
92
|
|
|
91
93
|
if not sources.files:
|
|
92
94
|
return CheckResults(
|
|
@@ -101,12 +103,18 @@ async def run_ty_check(
|
|
|
101
103
|
checker_name="ty",
|
|
102
104
|
)
|
|
103
105
|
|
|
106
|
+
# Merge the ty binary with the source files
|
|
107
|
+
input_digest = await Get(
|
|
108
|
+
Digest,
|
|
109
|
+
MergeDigests([downloaded_ty.digest, sources.snapshot.digest]),
|
|
110
|
+
)
|
|
111
|
+
|
|
104
112
|
# Build ty command
|
|
105
113
|
strict_arg = ["--strict"] if ty_subsystem.strict else []
|
|
106
114
|
output_format_arg = [f"--output-format={ty_subsystem.output_format}"]
|
|
107
115
|
|
|
108
116
|
argv = [
|
|
109
|
-
|
|
117
|
+
downloaded_ty.exe,
|
|
110
118
|
"check",
|
|
111
119
|
f"--python-version={baseline_subsystem.python_version}",
|
|
112
120
|
*strict_arg,
|
|
@@ -116,12 +124,12 @@ async def run_ty_check(
|
|
|
116
124
|
|
|
117
125
|
process = Process(
|
|
118
126
|
argv=argv,
|
|
119
|
-
input_digest=
|
|
127
|
+
input_digest=input_digest,
|
|
120
128
|
description=f"Run ty type check on {len(sources.files)} files",
|
|
121
129
|
level=LogLevel.DEBUG,
|
|
122
130
|
)
|
|
123
131
|
|
|
124
|
-
result = await Get(FallibleProcessResult,
|
|
132
|
+
result = await Get(FallibleProcessResult, Process, process)
|
|
125
133
|
|
|
126
134
|
return CheckResults(
|
|
127
135
|
results=[
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from pants.
|
|
6
|
-
from pants.
|
|
5
|
+
from pants.core.util_rules.external_tool import ExternalTool
|
|
6
|
+
from pants.engine.platform import Platform
|
|
7
|
+
from pants.option.option_types import BoolOption, SkipOption, StrListOption, StrOption
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class RuffSubsystem(
|
|
10
|
+
class RuffSubsystem(ExternalTool):
|
|
10
11
|
"""Configuration for Ruff linting and formatting.
|
|
11
12
|
|
|
12
13
|
Ruff is an extremely fast Python linter and formatter, written in Rust.
|
|
@@ -16,10 +17,33 @@ class RuffSubsystem(Subsystem):
|
|
|
16
17
|
options_scope = "baseline-ruff"
|
|
17
18
|
help = "Ruff linting and formatting configuration for baseline plugin."
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
default_version = "0.9.6"
|
|
21
|
+
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",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
def generate_url(self, plat: Platform) -> str:
|
|
30
|
+
"""Generate download URL for ruff."""
|
|
31
|
+
version = self.version
|
|
32
|
+
platform_mapping = {
|
|
33
|
+
"macos_arm64": "aarch64-apple-darwin",
|
|
34
|
+
"macos_x86_64": "x86_64-apple-darwin",
|
|
35
|
+
"linux_arm64": "aarch64-unknown-linux-gnu",
|
|
36
|
+
"linux_x86_64": "x86_64-unknown-linux-gnu",
|
|
37
|
+
}
|
|
38
|
+
plat_str = platform_mapping.get(plat.value, "x86_64-unknown-linux-gnu")
|
|
39
|
+
return f"https://github.com/astral-sh/ruff/releases/download/{version}/ruff-{plat_str}.tar.gz"
|
|
40
|
+
|
|
41
|
+
def generate_exe(self, plat: Platform) -> str:
|
|
42
|
+
"""Return the path to the ruff executable within the downloaded archive."""
|
|
43
|
+
return "ruff"
|
|
44
|
+
|
|
45
|
+
# Skip option required by Pants for tool subsystems
|
|
46
|
+
skip = SkipOption("lint", "fmt")
|
|
23
47
|
|
|
24
48
|
# Linting configuration
|
|
25
49
|
select = StrListOption(
|
pants_baseline/subsystems/ty.py
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from pants.core.util_rules.external_tool import ExternalTool
|
|
6
|
+
from pants.engine.platform import Platform
|
|
5
7
|
from pants.option.option_types import BoolOption, StrListOption, StrOption
|
|
6
|
-
from pants.option.subsystem import Subsystem
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class TySubsystem(
|
|
10
|
+
class TySubsystem(ExternalTool):
|
|
10
11
|
"""Configuration for ty type checker.
|
|
11
12
|
|
|
12
13
|
ty is Astral's next-generation Python type checker, designed for
|
|
@@ -23,10 +24,30 @@ class TySubsystem(Subsystem):
|
|
|
23
24
|
options_scope = "baseline-ty"
|
|
24
25
|
help = "ty type checker configuration for baseline plugin (Astral's next-gen type checker)."
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
default_version = "0.0.1-alpha.10"
|
|
28
|
+
default_known_versions = [
|
|
29
|
+
# ty is still in alpha - these are placeholder hashes
|
|
30
|
+
"0.0.1-alpha.10|macos_arm64|sha256:0000000000000000000000000000000000000000000000000000000000000000|5000000",
|
|
31
|
+
"0.0.1-alpha.10|macos_x86_64|sha256:0000000000000000000000000000000000000000000000000000000000000000|5000000",
|
|
32
|
+
"0.0.1-alpha.10|linux_arm64|sha256:0000000000000000000000000000000000000000000000000000000000000000|5000000",
|
|
33
|
+
"0.0.1-alpha.10|linux_x86_64|sha256:0000000000000000000000000000000000000000000000000000000000000000|5000000",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
def generate_url(self, plat: Platform) -> str:
|
|
37
|
+
"""Generate download URL for ty."""
|
|
38
|
+
version = self.version
|
|
39
|
+
platform_mapping = {
|
|
40
|
+
"macos_arm64": "aarch64-apple-darwin",
|
|
41
|
+
"macos_x86_64": "x86_64-apple-darwin",
|
|
42
|
+
"linux_arm64": "aarch64-unknown-linux-gnu",
|
|
43
|
+
"linux_x86_64": "x86_64-unknown-linux-gnu",
|
|
44
|
+
}
|
|
45
|
+
plat_str = platform_mapping.get(plat.value, "x86_64-unknown-linux-gnu")
|
|
46
|
+
return f"https://github.com/astral-sh/ty/releases/download/{version}/ty-{plat_str}.tar.gz"
|
|
47
|
+
|
|
48
|
+
def generate_exe(self, plat: Platform) -> str:
|
|
49
|
+
"""Return the path to the ty executable within the downloaded archive."""
|
|
50
|
+
return "ty"
|
|
30
51
|
|
|
31
52
|
# Type checking mode
|
|
32
53
|
strict = BoolOption(
|
pants_baseline/subsystems/uv.py
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from pants.core.util_rules.external_tool import ExternalTool
|
|
6
|
+
from pants.engine.platform import Platform
|
|
5
7
|
from pants.option.option_types import BoolOption, StrListOption, StrOption
|
|
6
|
-
from pants.option.subsystem import Subsystem
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class UvSubsystem(
|
|
10
|
+
class UvSubsystem(ExternalTool):
|
|
10
11
|
"""Configuration for uv dependency management and security auditing.
|
|
11
12
|
|
|
12
13
|
uv is Astral's ultra-fast Python package installer and resolver,
|
|
@@ -21,10 +22,30 @@ class UvSubsystem(Subsystem):
|
|
|
21
22
|
options_scope = "baseline-uv"
|
|
22
23
|
help = "uv dependency management and security auditing configuration for baseline plugin."
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
default_version = "0.5.21"
|
|
26
|
+
default_known_versions = [
|
|
27
|
+
# uv 0.5.21
|
|
28
|
+
"0.5.21|macos_arm64|sha256:0000000000000000000000000000000000000000000000000000000000000000|15000000",
|
|
29
|
+
"0.5.21|macos_x86_64|sha256:0000000000000000000000000000000000000000000000000000000000000000|15000000",
|
|
30
|
+
"0.5.21|linux_arm64|sha256:0000000000000000000000000000000000000000000000000000000000000000|15000000",
|
|
31
|
+
"0.5.21|linux_x86_64|sha256:0000000000000000000000000000000000000000000000000000000000000000|15000000",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
def generate_url(self, plat: Platform) -> str:
|
|
35
|
+
"""Generate download URL for uv."""
|
|
36
|
+
version = self.version
|
|
37
|
+
platform_mapping = {
|
|
38
|
+
"macos_arm64": "aarch64-apple-darwin",
|
|
39
|
+
"macos_x86_64": "x86_64-apple-darwin",
|
|
40
|
+
"linux_arm64": "aarch64-unknown-linux-gnu",
|
|
41
|
+
"linux_x86_64": "x86_64-unknown-linux-gnu",
|
|
42
|
+
}
|
|
43
|
+
plat_str = platform_mapping.get(plat.value, "x86_64-unknown-linux-gnu")
|
|
44
|
+
return f"https://github.com/astral-sh/uv/releases/download/{version}/uv-{plat_str}.tar.gz"
|
|
45
|
+
|
|
46
|
+
def generate_exe(self, plat: Platform) -> str:
|
|
47
|
+
"""Return the path to the uv executable within the downloaded archive."""
|
|
48
|
+
return "uv"
|
|
28
49
|
|
|
29
50
|
# Security auditing
|
|
30
51
|
audit_enabled = BoolOption(
|
pants_baseline/targets.py
CHANGED
|
@@ -6,25 +6,26 @@ from pants.engine.target import (
|
|
|
6
6
|
COMMON_TARGET_FIELDS,
|
|
7
7
|
BoolField,
|
|
8
8
|
IntField,
|
|
9
|
+
MultipleSourcesField,
|
|
9
10
|
StringField,
|
|
10
|
-
StringSequenceField,
|
|
11
11
|
Target,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class BaselineSourcesField(
|
|
15
|
+
class BaselineSourcesField(MultipleSourcesField):
|
|
16
16
|
"""Source files for the baseline Python project."""
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
default = ("src/**/*.py",)
|
|
19
|
+
expected_file_extensions = (".py", ".pyi")
|
|
20
20
|
help = "Python source files to include in baseline checks."
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class BaselineTestSourcesField(
|
|
23
|
+
class BaselineTestSourcesField(MultipleSourcesField):
|
|
24
24
|
"""Test source files for the baseline Python project."""
|
|
25
25
|
|
|
26
26
|
alias = "test_sources"
|
|
27
27
|
default = ("tests/**/*.py",)
|
|
28
|
+
expected_file_extensions = (".py", ".pyi")
|
|
28
29
|
help = "Test files to include in baseline checks."
|
|
29
30
|
|
|
30
31
|
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
pants_baseline/__about__.py,sha256=emEUrAUfvUyL9_8_zLXhpGfpLneSXOrp3muo2ermC_k,98
|
|
2
|
-
pants_baseline/__init__.py,sha256=uVRGi1D2gFjc7emmeewWdcvpO-NsUuKsMbX3rztOxWU,655
|
|
3
|
-
pants_baseline/register.py,sha256=AnQrzGYcPY4f7jMvgDVvs9bo-oY1XgOAwEO3fo813yg,1656
|
|
4
|
-
pants_baseline/targets.py,sha256=G8ejj3QYaGHtLHbxirTyTjnEAWYxlpHFHYKV-_B4lMo,3300
|
|
5
|
-
pants_baseline/goals/__init__.py,sha256=pf6KU2CIQuDkx8ER3IS0H-kuNbBtX-AH5B5SnSP9_yw,192
|
|
6
|
-
pants_baseline/goals/audit.py,sha256=pzw1iczFb2yZKUXzQ9oggXUikv3uoZ-Nc5tVFJeTRLA,2339
|
|
7
|
-
pants_baseline/goals/fmt.py,sha256=QNeQHBqR-wdLKE0oTkI3jtE3sFOmOp4EP0VXGkmG6Gg,1731
|
|
8
|
-
pants_baseline/goals/lint.py,sha256=hvIRPd-zfh5zC6Q_RvEWSbqJyszGEtvCpPdKwrTe0Vo,1867
|
|
9
|
-
pants_baseline/goals/test.py,sha256=cUSE7NyuXaEtZnBe22MqPAu-Fqjtn5oCpttmeU1XIk4,1590
|
|
10
|
-
pants_baseline/goals/typecheck.py,sha256=LuuOW3lvzOhgsNf5-W0SC1jvNnI5Nt4ZQAGwOPg6ZoA,1706
|
|
11
|
-
pants_baseline/rules/__init__.py,sha256=UpvDpGVImhRfp2_VeUNsRPGiWjBbMI6AV1-Yx3kS0Gg,252
|
|
12
|
-
pants_baseline/rules/audit_rules.py,sha256=aauxy-yjah19EVBjWDOcwsRvQAIhsAtLg5VOKCbsRaM,1918
|
|
13
|
-
pants_baseline/rules/fmt_rules.py,sha256=dxUyDcRHPPk04_q2ITDKu8VEvHV-vDFDaY99YCvE_yk,3534
|
|
14
|
-
pants_baseline/rules/lint_rules.py,sha256=BKoGyfO66hSnieMkZ8f0ghpoVQR0eSIZtZglmCPkPEA,3862
|
|
15
|
-
pants_baseline/rules/test_rules.py,sha256=gZug806LGT2Vr_GFhUdjAaRrg9RbVda-_78XhXcr0f0,4218
|
|
16
|
-
pants_baseline/rules/typecheck_rules.py,sha256=JxkvJ6lJn_UvDF4IMMmmBAPWiw6kms_hTrdUE_EAzs8,4006
|
|
17
|
-
pants_baseline/subsystems/__init__.py,sha256=LteH_qmUIgRAnXYmmi7f6o894QfpY3hMNH5dlvJbSoM,387
|
|
18
|
-
pants_baseline/subsystems/baseline.py,sha256=M20muLytoWZnA4ZY3lkbSsdCXt6OMPH6IMJfXULAQSo,2049
|
|
19
|
-
pants_baseline/subsystems/ruff.py,sha256=v0yHmbdgsPMMgjxJrs-YaKPE_n7rE6h-D0HGhXheyyE,2601
|
|
20
|
-
pants_baseline/subsystems/ty.py,sha256=6M205VZD5TpX6T4vWZ4UFs5Y3T2rGqYtzidmiRh_6kg,2126
|
|
21
|
-
pants_baseline/subsystems/uv.py,sha256=K6G8KR3l-Jo18aMh183gfhEelJR57cb20yIqKUMw3K0,1851
|
|
22
|
-
jaymd96_pants_baseline-0.1.2.dist-info/METADATA,sha256=iahXBTWMePOuc64POXU-cljsgB4nyDv2eT1ALiXmrIM,7934
|
|
23
|
-
jaymd96_pants_baseline-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
24
|
-
jaymd96_pants_baseline-0.1.2.dist-info/licenses/LICENSE,sha256=oLGLZv7XKM_oKCbdMW1bZB37SXsdexmhNSuh3Xg4m4I,10754
|
|
25
|
-
jaymd96_pants_baseline-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
{jaymd96_pants_baseline-0.1.2.dist-info → jaymd96_pants_baseline-0.2.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|