Skip to content

Commit 13177c7

Browse files
authored
Merge pull request #215 from sandialabs/update-ruff-linters
Update ruff linters
2 parents 7f39996 + ef76d16 commit 13177c7

7 files changed

+100
-62
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ repos:
99
- repo: https://github.com/astral-sh/ruff-pre-commit
1010
rev: v0.12.2
1111
hooks:
12-
- id: ruff
12+
- id: ruff-check
1313
- id: ruff-format
1414

1515
- repo: https://github.com/gitleaks/gitleaks

example/ex_6_creating_retryable_stages.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
# SPDX-License-Identifier: BSD-3-Clause
99

10+
from __future__ import annotations
11+
1012
import argparse
1113
import functools
1214
import sys
@@ -25,7 +27,7 @@ def __init__(
2527
console_force_terminal: Optional[bool] = None,
2628
console_log_path: bool = True,
2729
print_commands: bool = True,
28-
):
30+
) -> None:
2931
super().__init__(
3032
stages,
3133
console_force_terminal=console_force_terminal,

example/ex_7_customizing_the_summary.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
# SPDX-License-Identifier: BSD-3-Clause
99

10+
from __future__ import annotations
11+
1012
import argparse
1113
import functools
1214
import platform
@@ -27,7 +29,7 @@ def __init__(
2729
console_force_terminal: Optional[bool] = None,
2830
console_log_path: bool = True,
2931
print_commands: bool = True,
30-
):
32+
) -> None:
3133
super().__init__(
3234
stages,
3335
console_force_terminal=console_force_terminal,

pyproject.toml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,40 +73,74 @@ line-length = 79
7373
[tool.ruff.lint]
7474
extend-select = [
7575
"A",
76+
"AIR",
77+
"ANN",
78+
"ARG",
79+
"ASYNC",
7680
"B",
7781
"BLE",
7882
"C4",
7983
"C90",
8084
"D",
85+
"DJ",
8186
"DTZ",
8287
"E",
8388
"EM",
8489
"ERA",
8590
"EXE",
91+
"F",
92+
"FA",
8693
"FBT",
94+
"FIX",
95+
"FLY",
96+
"FURB",
97+
"G",
98+
"ICN",
99+
"INP",
100+
"INT",
101+
"ISC",
102+
"LOG",
103+
"N",
87104
"NPY",
105+
"PD",
106+
"PERF",
88107
"PGH",
108+
"PIE",
89109
"PL",
90110
"PT",
91111
"PTH",
112+
"PYI",
113+
"Q",
92114
"RET",
93115
"RSE",
94116
"RUF",
95117
"S",
96118
"SIM",
119+
"SLF",
120+
"SLOT",
121+
"T10",
122+
"T20",
123+
"TC",
124+
"TD",
97125
"TID",
98126
"TCH",
99127
"TRY",
100128
"UP",
101129
"W",
130+
"YTT",
102131
]
103132
ignore = [
104133
"D212",
105134
]
106135

107136

108137
[tool.ruff.lint.per-file-ignores]
109-
"**/test_*.py" = ["S101"]
138+
"**/test_*.py" = [
139+
"S101",
140+
"SLF001",
141+
"T201",
142+
]
143+
"doc/source/conf.py" = ["INP001"]
110144
"example/*.py" = [
111145
"D101",
112146
"D102",
@@ -121,6 +155,10 @@ ignore = [
121155
convention = "google"
122156

123157

158+
[tool.ruff.lint.pyupgrade]
159+
keep-runtime-typing = true
160+
161+
124162
[tool.semantic_release]
125163
build_command = "python3 -m pip install poetry && poetry build"
126164
commit_message = """

staged_script/staged_script.py

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
# SPDX-License-Identifier: BSD-3-Clause
1313

14+
from __future__ import annotations
15+
1416
import functools
1517
import re
1618
import shlex
@@ -24,7 +26,7 @@
2426
from datetime import datetime, timedelta, timezone
2527
from pathlib import Path
2628
from subprocess import CompletedProcess
27-
from typing import Callable, NamedTuple, Optional
29+
from typing import Any, Callable, NamedTuple, NoReturn, Optional
2830

2931
import __main__
3032
import rich.traceback
@@ -41,6 +43,28 @@
4143
rich.traceback.install()
4244

4345

46+
def validate_stage_name(stage_name: str) -> None:
47+
"""
48+
Validate a stage name.
49+
50+
Ensure a stage name consists of only lowercase letters. This is
51+
both to simplify implementation details within the
52+
:class:`StagedScript` class, and to provide the best user experience
53+
for users of your :class:`StagedScript` subclasses.
54+
55+
Args:
56+
stage_name: The name of the stage.
57+
58+
Raises:
59+
ValueError: If the stage name is invalid.
60+
"""
61+
if not re.match("^[a-z]+$", stage_name):
62+
message = (
63+
f"Stage name {stage_name!r} must contain only lowercase letters."
64+
)
65+
raise ValueError(message)
66+
67+
4468
class StagedScript:
4569
"""
4670
The base class for all staged scripts.
@@ -121,7 +145,7 @@ def __init__(
121145
console_force_terminal: Optional[bool] = None,
122146
console_log_path: bool = True,
123147
print_commands: bool = True,
124-
):
148+
) -> None:
125149
"""
126150
Initialize a :class:`StagedScript` object.
127151
@@ -145,7 +169,7 @@ def __init__(
145169
and optionally pass in additional arguments.
146170
"""
147171
for stage in stages:
148-
self._validate_stage_name(stage)
172+
validate_stage_name(stage)
149173
self.args = Namespace()
150174
self.commands_executed: list[str] = []
151175
self.console = Console(
@@ -163,29 +187,6 @@ def __init__(
163187
self.stages_to_run: set[str] = set()
164188
self.start_time = datetime.now(tz=timezone.utc)
165189

166-
@staticmethod
167-
def _validate_stage_name(stage_name: str) -> None:
168-
"""
169-
Validate the stage name.
170-
171-
Ensure the stage name consists of only lowercase letters. This
172-
is both to simplify implementation details within the class, and
173-
to provide the best user experience for users of your
174-
:class:`StagedScript` subclasses.
175-
176-
Args:
177-
stage_name: The name of the stage.
178-
179-
Raises:
180-
ValueError: If the stage name is invalid.
181-
"""
182-
if not re.match("^[a-z]+$", stage_name):
183-
message = (
184-
f"Stage name {stage_name!r} must contain only lowercase "
185-
"letters."
186-
)
187-
raise ValueError(message)
188-
189190
#
190191
# The `stage` decorator.
191192
#
@@ -243,13 +244,11 @@ def stage(stage_name: str, heading: str) -> Callable:
243244
heading: A heading message to print indicating what will
244245
happen in the stage.
245246
"""
246-
__class__._validate_stage_name( # type: ignore[name-defined]
247-
stage_name
248-
)
247+
validate_stage_name(stage_name)
249248

250249
def decorator(func: Callable) -> Callable:
251250
def get_phase_method( # noqa: D417
252-
self,
251+
self, # noqa: ANN001
253252
method_name: str,
254253
) -> Callable:
255254
"""
@@ -274,9 +273,9 @@ def get_phase_method( # noqa: D417
274273
)
275274

276275
def run_retryable_phases( # noqa: D417
277-
self,
278-
*args,
279-
**kwargs,
276+
self, # noqa: ANN001
277+
*args: Any, # noqa: ANN401
278+
**kwargs: Any, # noqa: ANN401
280279
) -> None:
281280
"""
282281
Run the retryable phases.
@@ -311,7 +310,7 @@ def run_retryable_phases( # noqa: D417
311310
get_phase_method(self, "_end_stage")()
312311

313312
@functools.wraps(func)
314-
def wrapper(self, *args, **kwargs) -> None:
313+
def wrapper(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN001, ANN401
315314
"""
316315
Turn a function into a stage.
317316
@@ -365,7 +364,6 @@ def _run_pre_stage_actions(self) -> None:
365364
were met before attempting the stage, and erroring out
366365
appropriately if not.
367366
"""
368-
pass
369367

370368
def _begin_stage(self, heading: str) -> None:
371369
"""
@@ -495,7 +493,6 @@ def _run_post_stage_actions(self) -> None:
495493
were met before moving on, and erroring out appropriately if
496494
not.
497495
"""
498-
pass
499496

500497
def _prepare_to_retry_stage(self, retry_state: RetryCallState) -> None:
501498
"""
@@ -762,7 +759,7 @@ def parse_args(self, argv: list[str]) -> None:
762759
]:
763760
setattr(self, retry_arg, getattr(self.args, retry_arg, None))
764761

765-
def raise_parser_error(self, message):
762+
def raise_parser_error(self, message: str) -> NoReturn:
766763
"""
767764
Raise a parser error.
768765
@@ -791,7 +788,7 @@ def run(
791788
*,
792789
pretty_print: bool = False,
793790
print_command: Optional[bool] = None,
794-
**kwargs,
791+
**kwargs: Any, # noqa: ANN401
795792
) -> CompletedProcess:
796793
"""
797794
Run a command in the underlying shell.
@@ -1023,8 +1020,6 @@ class RetryStage(TryAgain):
10231020
that a stage should be retried.
10241021
"""
10251022

1026-
pass
1027-
10281023

10291024
class HelpFormatter(
10301025
ArgumentDefaultsHelpFormatter, RawDescriptionHelpFormatter
@@ -1036,5 +1031,3 @@ class HelpFormatter(
10361031
treats the description as raw text (doesn't do any automatic
10371032
formatting) and shows default values of arguments.
10381033
"""
1039-
1040-
pass

test/test_staged_script.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
# SPDX-License-Identifier: BSD-3-Clause
88

9+
from __future__ import annotations
10+
911
import shlex
1012
from datetime import datetime, timedelta, timezone
1113
from subprocess import CompletedProcess
@@ -16,6 +18,7 @@
1618
from rich.console import Console
1719

1820
from staged_script import StagedScript, StageDuration
21+
from staged_script.staged_script import validate_stage_name
1922

2023

2124
@pytest.fixture
@@ -37,21 +40,21 @@ def test_print_dry_run_message(
3740
assert expected in captured.out
3841

3942

40-
def test__validate_stage_name() -> None:
41-
"""Test the :func:`validate_stage_name` method."""
42-
StagedScript._validate_stage_name("valid")
43+
def test_validate_stage_name() -> None:
44+
"""Test the :func:`validate_stage_name` function."""
45+
validate_stage_name("valid")
4346

4447

4548
@pytest.mark.parametrize(
4649
"stage_name", ["Uppercase", "spa ces", "hyphen-ated", "under_scores"]
4750
)
48-
def test__validate_stage_name_raises(stage_name: str) -> None:
51+
def test_validate_stage_name_raises(stage_name: str) -> None:
4952
"""Ensure :func:`validate_stage_name` raises an exception when needed."""
5053
with pytest.raises(
5154
ValueError,
5255
match=f"'{stage_name}' must contain only lowercase letters",
5356
):
54-
StagedScript._validate_stage_name(stage_name)
57+
validate_stage_name(stage_name)
5558

5659

5760
def test__begin_stage(
@@ -91,15 +94,15 @@ def test__skip_stage(
9194
@pytest.mark.parametrize("retry_attempts", [0, 5])
9295
@patch("tenacity.Retrying")
9396
def test__handle_stage_retry_error(
94-
mock_Retrying: MagicMock,
97+
mock_retrying: MagicMock,
9598
retry_attempts: int,
9699
script: StagedScript,
97100
capsys: pytest.CaptureFixture,
98101
) -> None:
99102
"""Test the :func:`_handle_stage_retry_error` method."""
100103
script.current_stage = "test"
101104
script.test_retry_attempts = retry_attempts # type: ignore[attr-defined]
102-
retry = mock_Retrying()
105+
retry = mock_retrying()
103106
retry.statistics = {
104107
"delay_since_first_attempt": 1234,
105108
"attempt_number": retry_attempts,
@@ -121,14 +124,14 @@ def test__handle_stage_retry_error(
121124

122125
@patch("tenacity.RetryCallState")
123126
def test__prepare_to_retry_stage(
124-
mock_RetryCallState: MagicMock,
127+
mock_retry_call_state: MagicMock,
125128
script: StagedScript,
126129
capsys: pytest.CaptureFixture,
127130
) -> None:
128131
"""Test the :func:`_prepare_to_retry_stage` method."""
129132
script.current_stage = "test"
130-
retry_state = mock_RetryCallState()
131-
retry_state.__repr__ = lambda self: "mock_RetryCallState.__repr__"
133+
retry_state = mock_retry_call_state()
134+
retry_state.__repr__ = lambda _self: "mock_RetryCallState.__repr__"
132135
script._prepare_to_retry_stage(retry_state)
133136
captured = capsys.readouterr()
134137
for text in [

0 commit comments

Comments
 (0)