Skip to content

Commit 56fe9ec

Browse files
authored
Enable ruff SIM ruleset which uses flake8-simplify rules to simplify code (#1422)
1 parent 8289d0e commit 56fe9ec

File tree

14 files changed

+85
-114
lines changed

14 files changed

+85
-114
lines changed

cmd2/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
"""This simply imports certain things for backwards compatibility."""
22

33
import argparse
4+
import contextlib
45
import importlib.metadata as importlib_metadata
56
import sys
67

7-
try:
8+
with contextlib.suppress(importlib_metadata.PackageNotFoundError):
89
__version__ = importlib_metadata.version(__name__)
9-
except importlib_metadata.PackageNotFoundError: # pragma: no cover
10-
# package is not installed
11-
pass
1210

1311
from .ansi import (
1412
Bg,

cmd2/argparse_completer.py

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,7 @@ def _looks_like_flag(token: str, parser: argparse.ArgumentParser) -> bool:
9292
return False
9393

9494
# Flags can't have a space
95-
if ' ' in token:
96-
return False
97-
98-
# Starts like a flag
99-
return True
95+
return ' ' not in token
10096

10197

10298
class _ArgumentState:
@@ -368,34 +364,30 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
368364
# Otherwise treat as a positional argument
369365
else:
370366
# If we aren't current tracking a positional, then get the next positional arg to handle this token
371-
if pos_arg_state is None:
372-
# Make sure we are still have positional arguments to parse
373-
if remaining_positionals:
374-
action = remaining_positionals.popleft()
375-
376-
# Are we at a subcommand? If so, forward to the matching completer
377-
if action == self._subcommand_action:
378-
if token in self._subcommand_action.choices:
379-
# Merge self._parent_tokens and consumed_arg_values
380-
parent_tokens = {**self._parent_tokens, **consumed_arg_values}
381-
382-
# Include the subcommand name if its destination was set
383-
if action.dest != argparse.SUPPRESS:
384-
parent_tokens[action.dest] = [token]
385-
386-
parser: argparse.ArgumentParser = self._subcommand_action.choices[token]
387-
completer_type = self._cmd2_app._determine_ap_completer_type(parser)
388-
389-
completer = completer_type(parser, self._cmd2_app, parent_tokens=parent_tokens)
390-
391-
return completer.complete(
392-
text, line, begidx, endidx, tokens[token_index + 1 :], cmd_set=cmd_set
393-
)
394-
# Invalid subcommand entered, so no way to complete remaining tokens
395-
return []
396-
397-
# Otherwise keep track of the argument
398-
pos_arg_state = _ArgumentState(action)
367+
if pos_arg_state is None and remaining_positionals:
368+
action = remaining_positionals.popleft()
369+
370+
# Are we at a subcommand? If so, forward to the matching completer
371+
if action == self._subcommand_action:
372+
if token in self._subcommand_action.choices:
373+
# Merge self._parent_tokens and consumed_arg_values
374+
parent_tokens = {**self._parent_tokens, **consumed_arg_values}
375+
376+
# Include the subcommand name if its destination was set
377+
if action.dest != argparse.SUPPRESS:
378+
parent_tokens[action.dest] = [token]
379+
380+
parser: argparse.ArgumentParser = self._subcommand_action.choices[token]
381+
completer_type = self._cmd2_app._determine_ap_completer_type(parser)
382+
383+
completer = completer_type(parser, self._cmd2_app, parent_tokens=parent_tokens)
384+
385+
return completer.complete(text, line, begidx, endidx, tokens[token_index + 1 :], cmd_set=cmd_set)
386+
# Invalid subcommand entered, so no way to complete remaining tokens
387+
return []
388+
389+
# Otherwise keep track of the argument
390+
pos_arg_state = _ArgumentState(action)
399391

400392
# Check if we have a positional to consume this token
401393
if pos_arg_state is not None:

cmd2/argparse_custom.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -828,12 +828,8 @@ def _add_argument_wrapper(
828828
def _get_nargs_pattern_wrapper(self: argparse.ArgumentParser, action: argparse.Action) -> str:
829829
# Wrapper around ArgumentParser._get_nargs_pattern behavior to support nargs ranges
830830
nargs_range = action.get_nargs_range() # type: ignore[attr-defined]
831-
if nargs_range is not None:
832-
if nargs_range[1] == constants.INFINITY:
833-
range_max = ''
834-
else:
835-
range_max = nargs_range[1] # type: ignore[assignment]
836-
831+
if nargs_range:
832+
range_max = '' if nargs_range[1] == constants.INFINITY else nargs_range[1]
837833
nargs_pattern = f'(-*A{{{nargs_range[0]},{range_max}}}-*)'
838834

839835
# if this is an optional action, -- is not allowed
@@ -1265,14 +1261,12 @@ def add_subparsers(self, **kwargs: Any) -> argparse._SubParsersAction: # type:
12651261
def error(self, message: str) -> NoReturn:
12661262
"""Custom override that applies custom formatting to the error message."""
12671263
lines = message.split('\n')
1268-
linum = 0
12691264
formatted_message = ''
1270-
for line in lines:
1265+
for linum, line in enumerate(lines):
12711266
if linum == 0:
12721267
formatted_message = 'Error: ' + line
12731268
else:
12741269
formatted_message += '\n ' + line
1275-
linum += 1
12761270

12771271
self.print_usage(sys.stderr)
12781272
formatted_message = ansi.style_error(formatted_message)

cmd2/cmd2.py

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
# setting is True
3131
import argparse
3232
import cmd
33+
import contextlib
3334
import copy
3435
import functools
3536
import glob
@@ -48,10 +49,6 @@
4849
namedtuple,
4950
)
5051
from collections.abc import Callable, Iterable, Mapping
51-
from contextlib import (
52-
redirect_stdout,
53-
suppress,
54-
)
5552
from types import (
5653
FrameType,
5754
ModuleType,
@@ -123,10 +120,8 @@
123120
)
124121

125122
# NOTE: When using gnureadline with Python 3.13, start_ipython needs to be imported before any readline-related stuff
126-
try:
123+
with contextlib.suppress(ImportError):
127124
from IPython import start_ipython # type: ignore[import]
128-
except ImportError:
129-
pass
130125

131126
from .rl_utils import (
132127
RlType,
@@ -1098,9 +1093,8 @@ def add_settable(self, settable: Settable) -> None:
10981093
10991094
:param settable: Settable object being added
11001095
"""
1101-
if not self.always_prefix_settables:
1102-
if settable.name in self.settables and settable.name not in self._settables:
1103-
raise KeyError(f'Duplicate settable: {settable.name}')
1096+
if not self.always_prefix_settables and settable.name in self.settables and settable.name not in self._settables:
1097+
raise KeyError(f'Duplicate settable: {settable.name}')
11041098
self._settables[settable.name] = settable
11051099

11061100
def remove_settable(self, name: str) -> None:
@@ -1306,7 +1300,7 @@ def ppaged(self, msg: Any, *, end: str = '\n', chop: bool = False) -> None:
13061300
# Don't try to use the pager when being run by a continuous integration system like Jenkins + pexpect.
13071301
functional_terminal = False
13081302

1309-
if self.stdin.isatty() and self.stdout.isatty():
1303+
if self.stdin.isatty() and self.stdout.isatty(): # noqa: SIM102
13101304
if sys.platform.startswith('win') or os.environ.get('TERM') is not None:
13111305
functional_terminal = True
13121306

@@ -2844,8 +2838,8 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState:
28442838
read_fd, write_fd = os.pipe()
28452839

28462840
# Open each side of the pipe
2847-
subproc_stdin = open(read_fd)
2848-
new_stdout: TextIO = cast(TextIO, open(write_fd, 'w'))
2841+
subproc_stdin = open(read_fd) # noqa: SIM115
2842+
new_stdout: TextIO = cast(TextIO, open(write_fd, 'w')) # noqa: SIM115
28492843

28502844
# Create pipe process in a separate group to isolate our signals from it. If a Ctrl-C event occurs,
28512845
# our sigint handler will forward it only to the most recent pipe process. This makes sure pipe
@@ -2875,7 +2869,7 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState:
28752869
# like: !ls -l | grep user | wc -l > out.txt. But this makes it difficult to know if the pipe process
28762870
# started OK, since the shell itself always starts. Therefore, we will wait a short time and check
28772871
# if the pipe process is still running.
2878-
with suppress(subprocess.TimeoutExpired):
2872+
with contextlib.suppress(subprocess.TimeoutExpired):
28792873
proc.wait(0.2)
28802874

28812875
# Check if the pipe process already exited
@@ -2894,7 +2888,7 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState:
28942888
mode = 'a' if statement.output == constants.REDIRECTION_APPEND else 'w'
28952889
try:
28962890
# Use line buffering
2897-
new_stdout = cast(TextIO, open(utils.strip_quotes(statement.output_to), mode=mode, buffering=1))
2891+
new_stdout = cast(TextIO, open(utils.strip_quotes(statement.output_to), mode=mode, buffering=1)) # noqa: SIM115
28982892
except OSError as ex:
28992893
raise RedirectionError(f'Failed to redirect because: {ex}')
29002894

@@ -2915,7 +2909,7 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState:
29152909
# no point opening up the temporary file
29162910
current_paste_buffer = get_paste_buffer()
29172911
# create a temporary file to store output
2918-
new_stdout = cast(TextIO, tempfile.TemporaryFile(mode="w+"))
2912+
new_stdout = cast(TextIO, tempfile.TemporaryFile(mode="w+")) # noqa: SIM115
29192913
redir_saved_state.redirecting = True
29202914
sys.stdout = self.stdout = new_stdout
29212915

@@ -2941,11 +2935,9 @@ def _restore_output(self, statement: Statement, saved_redir_state: utils.Redirec
29412935
self.stdout.seek(0)
29422936
write_to_paste_buffer(self.stdout.read())
29432937

2944-
try:
2938+
with contextlib.suppress(BrokenPipeError):
29452939
# Close the file or pipe that stdout was redirected to
29462940
self.stdout.close()
2947-
except BrokenPipeError:
2948-
pass
29492941

29502942
# Restore the stdout values
29512943
self.stdout = cast(TextIO, saved_redir_state.saved_self_stdout)
@@ -3199,11 +3191,9 @@ def _read_command_line(self, prompt: str) -> str:
31993191
"""
32003192
try:
32013193
# Wrap in try since terminal_lock may not be locked
3202-
try:
3194+
with contextlib.suppress(RuntimeError):
32033195
# Command line is about to be drawn. Allow asynchronous changes to the terminal.
32043196
self.terminal_lock.release()
3205-
except RuntimeError:
3206-
pass
32073197
return self.read_input(prompt, completion_mode=utils.CompletionMode.COMMANDS)
32083198
except EOFError:
32093199
return 'eof'
@@ -3948,7 +3938,7 @@ def _print_topics(self, header: str, cmds: list[str], verbose: bool) -> None:
39483938
result = io.StringIO()
39493939

39503940
# try to redirect system stdout
3951-
with redirect_stdout(result):
3941+
with contextlib.redirect_stdout(result):
39523942
# save our internal stdout
39533943
stdout_orig = self.stdout
39543944
try:
@@ -4245,7 +4235,7 @@ def _reset_py_display() -> None:
42454235
# Delete any prompts that have been set
42464236
attributes = ['ps1', 'ps2', 'ps3']
42474237
for cur_attr in attributes:
4248-
with suppress(KeyError):
4238+
with contextlib.suppress(KeyError):
42494239
del sys.__dict__[cur_attr]
42504240

42514241
# Reset functions
@@ -4423,7 +4413,7 @@ def py_quit() -> None:
44234413

44244414
# Check if we are running Python code
44254415
if py_code_to_run:
4426-
try:
4416+
try: # noqa: SIM105
44274417
interp.runcode(py_code_to_run) # type: ignore[arg-type]
44284418
except BaseException: # noqa: BLE001, S110
44294419
# We don't care about any exception that happened in the Python code
@@ -4646,7 +4636,7 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
46464636
self.last_result = False
46474637

46484638
# -v must be used alone with no other options
4649-
if args.verbose:
4639+
if args.verbose: # noqa: SIM102
46504640
if args.clear or args.edit or args.output_file or args.run or args.transcript or args.expanded or args.script:
46514641
self.poutput("-v cannot be used with any other options")
46524642
self.poutput(self.history_parser.format_usage())

cmd2/rl_utils.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Imports the proper Readline for the platform and provides utility functions for it."""
22

3+
import contextlib
34
import sys
45
from enum import (
56
Enum,
@@ -27,10 +28,8 @@
2728
import gnureadline as readline # type: ignore[import]
2829
except ImportError:
2930
# Note: If this actually fails, you should install gnureadline on Linux/Mac or pyreadline3 on Windows.
30-
try:
31+
with contextlib.suppress(ImportError):
3132
import readline # type: ignore[no-redef]
32-
except ImportError: # pragma: no cover
33-
pass
3433

3534

3635
class RlType(Enum):

cmd2/transcript.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,8 @@ def _fetch_transcripts(self) -> None:
6464
self.transcripts = {}
6565
testfiles = cast(list[str], getattr(self.cmdapp, 'testfiles', []))
6666
for fname in testfiles:
67-
tfile = open(fname)
68-
self.transcripts[fname] = iter(tfile.readlines())
69-
tfile.close()
67+
with open(fname) as tfile:
68+
self.transcripts[fname] = iter(tfile.readlines())
7069

7170
def _test_transcript(self, fname: str, transcript: Iterator[str]) -> None:
7271
if self.cmdapp is None:

cmd2/utils.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import argparse
44
import collections
5+
import contextlib
56
import functools
67
import glob
78
import inspect
@@ -646,11 +647,9 @@ def _write_bytes(stream: Union[StdSim, TextIO], to_write: Union[bytes, str]) ->
646647
if isinstance(to_write, str):
647648
to_write = to_write.encode()
648649

649-
try:
650+
# BrokenPipeError can occur if output is being piped to a process that closed
651+
with contextlib.suppress(BrokenPipeError):
650652
stream.buffer.write(to_write)
651-
except BrokenPipeError:
652-
# This occurs if output is being piped to a process that closed
653-
pass
654653

655654

656655
class ContextFlag:
@@ -877,12 +876,8 @@ def align_text(
877876
line_styles = list(get_styles_dict(line).values())
878877

879878
# Calculate how wide each side of filling needs to be
880-
if line_width >= width:
881-
# Don't return here even though the line needs no fill chars.
882-
# There may be styles sequences to restore.
883-
total_fill_width = 0
884-
else:
885-
total_fill_width = width - line_width
879+
total_fill_width = 0 if line_width >= width else width - line_width
880+
# Even if the line needs no fill chars, there may be styles sequences to restore
886881

887882
if alignment == TextAlignment.LEFT:
888883
left_fill_width = 0

examples/custom_parser.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@ def __init__(self, *args, **kwargs) -> None:
1919
def error(self, message: str) -> None:
2020
"""Custom override that applies custom formatting to the error message."""
2121
lines = message.split('\n')
22-
linum = 0
2322
formatted_message = ''
24-
for line in lines:
23+
for linum, line in enumerate(lines):
2524
if linum == 0:
2625
formatted_message = 'Error: ' + line
2726
else:
2827
formatted_message += '\n ' + line
29-
linum += 1
3028

3129
self.print_usage(sys.stderr)
3230

examples/scripts/save_help_text.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ def main() -> None:
6060
# Open the output file
6161
outfile_path = os.path.expanduser(sys.argv[1])
6262
try:
63-
outfile = open(outfile_path, 'w')
63+
with open(outfile_path, 'w') as outfile:
64+
pass
6465
except OSError as e:
6566
print(f"Error opening {outfile_path} because: {e}")
6667
return

plugins/ext_test/tasks.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- setuptools >= 39.1.0
77
"""
88

9+
import contextlib
910
import os
1011
import pathlib
1112
import shutil
@@ -27,10 +28,8 @@ def rmrf(items, verbose=True):
2728
print(f"Removing {item}")
2829
shutil.rmtree(item, ignore_errors=True)
2930
# rmtree doesn't remove bare files
30-
try:
31+
with contextlib.suppress(FileNotFoundError):
3132
os.remove(item)
32-
except FileNotFoundError:
33-
pass
3433

3534

3635
# create namespaces

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ select = [
206206
"RET", # flake8-return (various warnings related to implicit vs explicit return statements)
207207
"RSE", # flake8-raise (warn about unnecessary parentheses on raised exceptions)
208208
# "RUF", # Ruff-specific rules (miscellaneous grab bag of lint checks specific to Ruff)
209-
"S", # flake8-bandit (security oriented checks, but extremely pedantic - do not attempt to apply to unit test files)
210-
# "SIM", # flake8-simplify (rules to attempt to simplify code)
209+
"S", # flake8-bandit (security oriented checks, but extremely pedantic - do not attempt to apply to unit test files)
210+
"SIM", # flake8-simplify (rules to attempt to simplify code)
211211
# "SLF", # flake8-self (warn when protected members are accessed outside of a class or file)
212212
"SLOT", # flake8-slots (warn about subclasses that should define __slots__)
213213
"T10", # flake8-debugger (check for pdb traces left in Python code)

0 commit comments

Comments
 (0)