Skip to content

Commit 9e62a0a

Browse files
kmvanbruntanselor
authored andcommitted
Refactored _perform_completion() to remove need for a cast() call
Changed CommandResult.stderr to a str instead of Optional[str]
1 parent 48d26a3 commit 9e62a0a

File tree

3 files changed

+23
-26
lines changed

3 files changed

+23
-26
lines changed

cmd2/cmd2.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1856,12 +1856,10 @@ def _perform_completion(
18561856
ArgparseCompleter,
18571857
)
18581858

1859-
unclosed_quote = ''
1860-
command: Optional[str] = None
1861-
1862-
# If custom_settings is None, then we are completing a command's arguments
1859+
# If custom_settings is None, then we are completing a command's argument.
1860+
# Parse the command line to get the command token.
1861+
command = ''
18631862
if custom_settings is None:
1864-
# Parse the command line
18651863
statement = self.statement_parser.parse_command_only(line)
18661864
command = statement.command
18671865

@@ -1891,7 +1889,7 @@ def _perform_completion(
18911889
if not tokens: # pragma: no cover
18921890
return
18931891

1894-
# Determine the completer function to use
1892+
# Determine the completer function to use for the command's argument
18951893
if custom_settings is None:
18961894
# Check if a macro was entered
18971895
if command in self.macros:
@@ -1923,7 +1921,7 @@ def _perform_completion(
19231921
# Not a recognized macro or command
19241922
else:
19251923
# Check if this command should be run as a shell command
1926-
if self.default_to_shell and command in utils.get_exes_in_path(cast(str, command)):
1924+
if self.default_to_shell and command in utils.get_exes_in_path(command):
19271925
completer_func = self.path_complete
19281926
else:
19291927
completer_func = self.completedefault # type: ignore[assignment]
@@ -1941,11 +1939,15 @@ def _perform_completion(
19411939
# Get the token being completed with any opening quote preserved
19421940
raw_completion_token = raw_tokens[-1]
19431941

1942+
# Used for adding quotes to the completion token
1943+
completion_token_quote = ''
1944+
19441945
# Check if the token being completed has an opening quote
19451946
if raw_completion_token and raw_completion_token[0] in constants.QUOTES:
19461947

1947-
# Since the token is still being completed, we know the opening quote is unclosed
1948-
unclosed_quote = raw_completion_token[0]
1948+
# Since the token is still being completed, we know the opening quote is unclosed.
1949+
# Save the quote so we can add a matching closing quote later.
1950+
completion_token_quote = raw_completion_token[0]
19491951

19501952
# readline still performs word breaks after a quote. Therefore something like quoted search
19511953
# text with a space would have resulted in begidx pointing to the middle of the token we
@@ -1981,7 +1983,7 @@ def _perform_completion(
19811983
self.display_matches = copy.copy(self.completion_matches)
19821984

19831985
# Check if we need to add an opening quote
1984-
if not unclosed_quote:
1986+
if not completion_token_quote:
19851987

19861988
add_quote = False
19871989

@@ -2004,19 +2006,19 @@ def _perform_completion(
20042006
if add_quote:
20052007
# Figure out what kind of quote to add and save it as the unclosed_quote
20062008
if any('"' in match for match in self.completion_matches):
2007-
unclosed_quote = "'"
2009+
completion_token_quote = "'"
20082010
else:
2009-
unclosed_quote = '"'
2011+
completion_token_quote = '"'
20102012

2011-
self.completion_matches = [unclosed_quote + match for match in self.completion_matches]
2013+
self.completion_matches = [completion_token_quote + match for match in self.completion_matches]
20122014

20132015
# Check if we need to remove text from the beginning of tab completions
20142016
elif text_to_remove:
20152017
self.completion_matches = [match.replace(text_to_remove, '', 1) for match in self.completion_matches]
20162018

20172019
# If we have one result, then add a closing quote if needed and allowed
2018-
if len(self.completion_matches) == 1 and self.allow_closing_quote and unclosed_quote:
2019-
self.completion_matches[0] += unclosed_quote
2020+
if len(self.completion_matches) == 1 and self.allow_closing_quote and completion_token_quote:
2021+
self.completion_matches[0] += completion_token_quote
20202022

20212023
def complete( # type: ignore[override]
20222024
self, text: str, state: int, custom_settings: Optional[utils.CustomCompletionSettings] = None

cmd2/py_bridge.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ class CommandResult(NamedTuple):
3333
3434
:stdout: str - output captured from stdout while this command is executing
3535
:stderr: str - output captured from stderr while this command is executing
36-
None if no error captured.
3736
:stop: bool - return value of onecmd_plus_hooks after it runs the given
3837
command line.
3938
:data: possible data populated by the command.
@@ -66,7 +65,7 @@ class CommandResult(NamedTuple):
6665
"""
6766

6867
stdout: str = ''
69-
stderr: Optional[str] = None
68+
stderr: str = ''
7069
stop: bool = False
7170
data: Any = None
7271

@@ -132,10 +131,10 @@ def __call__(self, command: str, *, echo: Optional[bool] = None) -> CommandResul
132131
self._cmd2_app.stdout = cast(IO[str], copy_cmd_stdout.inner_stream)
133132
self.stop = stop or self.stop
134133

135-
# Save the output. If stderr is empty, set it to None.
134+
# Save the result
136135
result = CommandResult(
137136
stdout=copy_cmd_stdout.getvalue(),
138-
stderr=copy_stderr.getvalue() if copy_stderr.getvalue() else None,
137+
stderr=copy_stderr.getvalue(),
139138
stop=stop,
140139
data=self._cmd2_app.last_result,
141140
)

tests_isolated/test_commandset/test_commandset.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,28 +257,24 @@ def test_commandset_decorators(command_sets_app):
257257
assert 'extra1' in result.data['unknown']
258258
assert 'extra2' in result.data['unknown']
259259
assert result.data['arg1'] == 'juice'
260-
assert result.stderr is None
260+
assert not result.stderr
261261

262262
result = command_sets_app.app_cmd('durian juice extra1 extra2')
263263
assert len(result.data['args']) == 3
264264
assert 'juice' in result.data['args']
265265
assert 'extra1' in result.data['args']
266266
assert 'extra2' in result.data['args']
267-
assert result.stderr is None
267+
assert not result.stderr
268268

269269
result = command_sets_app.app_cmd('durian')
270270
assert len(result.data['args']) == 0
271-
assert result.stderr is None
271+
assert not result.stderr
272272

273273
result = command_sets_app.app_cmd('elderberry')
274-
assert result.stderr is not None
275-
assert len(result.stderr) > 0
276274
assert 'arguments are required' in result.stderr
277275
assert result.data is None
278276

279277
result = command_sets_app.app_cmd('elderberry a b')
280-
assert result.stderr is not None
281-
assert len(result.stderr) > 0
282278
assert 'unrecognized arguments' in result.stderr
283279
assert result.data is None
284280

0 commit comments

Comments
 (0)