Skip to content

Commit bad78a4

Browse files
committed
Move completion exception handling back to Cmd.complete().
1 parent 67d4a66 commit bad78a4

File tree

3 files changed

+96
-91
lines changed

3 files changed

+96
-91
lines changed

cmd2/cmd2.py

Lines changed: 87 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,7 @@ def format_exception(self, exception: BaseException) -> str:
14881488
)
14891489
final_msg.append(help_msg)
14901490

1491-
console.print(final_msg)
1491+
console.print(final_msg, end="")
14921492

14931493
return capture.get()
14941494

@@ -1506,7 +1506,7 @@ def pexcept(
15061506
method and still call `super()` without encountering unexpected keyword argument errors.
15071507
"""
15081508
formatted_exception = self.format_exception(exception)
1509-
self.print_to(sys.stderr, formatted_exception)
1509+
self.print_to(sys.stderr, formatted_exception + "\n")
15101510

15111511
def pfeedback(
15121512
self,
@@ -2437,82 +2437,99 @@ def complete(
24372437
:param endidx: ending index of text
24382438
:param custom_settings: used when not completing the main command line
24392439
:return: a Completions object
2440-
:raises CompletionError: if a completion-related exception occurs
2441-
:raises Exception: for any unhandled underlying processing errors
24422440
"""
2443-
# Check if we are completing a multiline command
2444-
if self._at_continuation_prompt:
2445-
# lstrip and prepend the previously typed portion of this multiline command
2446-
lstripped_previous = self._multiline_in_progress.lstrip()
2447-
line = lstripped_previous + line
2448-
2449-
# Increment the indexes to account for the prepended text
2450-
begidx = len(lstripped_previous) + begidx
2451-
endidx = len(lstripped_previous) + endidx
2452-
else:
2453-
# lstrip the original line
2454-
orig_line = line
2455-
line = orig_line.lstrip()
2456-
num_stripped = len(orig_line) - len(line)
2457-
2458-
# Calculate new indexes for the stripped line. If the cursor is at a position before the end of a
2459-
# line of spaces, then the following math could result in negative indexes. Enforce a max of 0.
2460-
begidx = max(begidx - num_stripped, 0)
2461-
endidx = max(endidx - num_stripped, 0)
2462-
2463-
# Shortcuts are not word break characters when completing. Therefore, shortcuts become part
2464-
# of the text variable if there isn't a word break, like a space, after it. We need to remove it
2465-
# from text and update the indexes. This only applies if we are at the beginning of the command line.
2466-
shortcut_to_restore = ''
2467-
if begidx == 0 and custom_settings is None:
2468-
for shortcut, _ in self.statement_parser.shortcuts:
2469-
if text.startswith(shortcut):
2470-
# Save the shortcut to restore later
2471-
shortcut_to_restore = shortcut
2472-
2473-
# Adjust text and where it begins
2474-
text = text[len(shortcut_to_restore) :]
2475-
begidx += len(shortcut_to_restore)
2476-
break
2441+
try:
2442+
# Check if we are completing a multiline command
2443+
if self._at_continuation_prompt:
2444+
# lstrip and prepend the previously typed portion of this multiline command
2445+
lstripped_previous = self._multiline_in_progress.lstrip()
2446+
line = lstripped_previous + line
2447+
2448+
# Increment the indexes to account for the prepended text
2449+
begidx = len(lstripped_previous) + begidx
2450+
endidx = len(lstripped_previous) + endidx
24772451
else:
2478-
# No shortcut was found. Complete the command token.
2479-
parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(add_help=False)
2480-
parser.add_argument(
2481-
'command',
2482-
metavar="COMMAND",
2483-
help="command, alias, or macro name",
2484-
choices=self._get_commands_aliases_and_macros_choices(),
2485-
)
2486-
custom_settings = utils.CustomCompletionSettings(parser)
2452+
# lstrip the original line
2453+
orig_line = line
2454+
line = orig_line.lstrip()
2455+
num_stripped = len(orig_line) - len(line)
2456+
2457+
# Calculate new indexes for the stripped line. If the cursor is at a position before the end of a
2458+
# line of spaces, then the following math could result in negative indexes. Enforce a max of 0.
2459+
begidx = max(begidx - num_stripped, 0)
2460+
endidx = max(endidx - num_stripped, 0)
2461+
2462+
# Shortcuts are not word break characters when completing. Therefore, shortcuts become part
2463+
# of the text variable if there isn't a word break, like a space, after it. We need to remove it
2464+
# from text and update the indexes. This only applies if we are at the beginning of the command line.
2465+
shortcut_to_restore = ''
2466+
if begidx == 0 and custom_settings is None:
2467+
for shortcut, _ in self.statement_parser.shortcuts:
2468+
if text.startswith(shortcut):
2469+
# Save the shortcut to restore later
2470+
shortcut_to_restore = shortcut
2471+
2472+
# Adjust text and where it begins
2473+
text = text[len(shortcut_to_restore) :]
2474+
begidx += len(shortcut_to_restore)
2475+
break
2476+
else:
2477+
# No shortcut was found. Complete the command token.
2478+
parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(add_help=False)
2479+
parser.add_argument(
2480+
'command',
2481+
metavar="COMMAND",
2482+
help="command, alias, or macro name",
2483+
choices=self._get_commands_aliases_and_macros_choices(),
2484+
)
2485+
custom_settings = utils.CustomCompletionSettings(parser)
24872486

2488-
completions = self._perform_completion(text, line, begidx, endidx, custom_settings)
2487+
completions = self._perform_completion(text, line, begidx, endidx, custom_settings)
24892488

2490-
# Check if we need to restore a shortcut in the completion text
2491-
# so it doesn't get erased from the command line.
2492-
if completions and shortcut_to_restore:
2493-
new_items = [
2494-
dataclasses.replace(
2495-
item,
2496-
text=shortcut_to_restore + item.text,
2489+
# Check if we need to restore a shortcut in the completion text
2490+
# so it doesn't get erased from the command line.
2491+
if completions and shortcut_to_restore:
2492+
new_items = [
2493+
dataclasses.replace(
2494+
item,
2495+
text=shortcut_to_restore + item.text,
2496+
)
2497+
for item in completions
2498+
]
2499+
2500+
# Update items and set _quote_from_offset so that any auto-inserted
2501+
# opening quote is placed after the shortcut.
2502+
completions = dataclasses.replace(
2503+
completions,
2504+
items=new_items,
2505+
_search_text_offset=len(shortcut_to_restore),
24972506
)
2498-
for item in completions
2499-
]
25002507

2501-
# Update items and set _quote_from_offset so that any auto-inserted
2502-
# opening quote is placed after the shortcut.
2503-
completions = dataclasses.replace(
2504-
completions,
2505-
items=new_items,
2506-
_search_text_offset=len(shortcut_to_restore),
2507-
)
2508+
# Swap between COLUMN and MULTI_COLUMN style based on the number of matches.
2509+
if len(completions) > self.max_column_completion_results:
2510+
self.session.complete_style = CompleteStyle.MULTI_COLUMN
2511+
else:
2512+
self.session.complete_style = CompleteStyle.COLUMN
25082513

2509-
# Swap between COLUMN and MULTI_COLUMN style based on the number of matches.
2510-
if len(completions) > self.max_column_completion_results:
2511-
self.session.complete_style = CompleteStyle.MULTI_COLUMN
2512-
else:
2513-
self.session.complete_style = CompleteStyle.COLUMN
2514+
return completions # noqa: TRY300
2515+
2516+
except CompletionError as ex:
2517+
err_str = str(ex)
2518+
completion_error = ""
25142519

2515-
return completions
2520+
# Don't display anything if the error is blank (e.g. _NoResultsError for an argument which supresses hints)
2521+
if err_str:
2522+
console = ru.Cmd2GeneralConsole()
2523+
with console.capture() as capture:
2524+
console.print(
2525+
Text(err_str, style=Cmd2Style.ERROR if ex.apply_style else ""),
2526+
end="",
2527+
)
2528+
completion_error = capture.get()
2529+
return Completions(completion_error=completion_error)
2530+
except Exception as ex: # noqa: BLE001
2531+
formatted_exception = self.format_exception(ex)
2532+
return Completions(completion_error=formatted_exception)
25162533

25172534
def in_script(self) -> bool:
25182535
"""Return whether a text script is running."""

cmd2/completion.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ class Completions(CompletionResultsBase):
207207
# An optional hint which prints above completion suggestions
208208
completion_hint: str = ""
209209

210+
# Optional message to display if an error occurs during completion
211+
completion_error: str = ""
212+
210213
# An optional table string populated by the argparse completer
211214
completion_table: str = ""
212215

cmd2/pt_utils.py

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@
1616
from prompt_toolkit.formatted_text import ANSI
1717
from prompt_toolkit.history import History
1818
from prompt_toolkit.lexers import Lexer
19-
from rich.text import Text
2019

2120
from . import (
2221
constants,
2322
utils,
2423
)
25-
from . import rich_utils as ru
26-
from .exceptions import CompletionError
27-
from .styles import Cmd2Style
2824

2925
if TYPE_CHECKING:
3026
from .cmd2 import Cmd
@@ -62,23 +58,12 @@ def get_completions(self, document: Document, _complete_event: object) -> Iterab
6258
endidx = cursor_pos
6359
text = line[begidx:endidx]
6460

65-
try:
66-
completions = self.cmd_app.complete(
67-
text, line=line, begidx=begidx, endidx=endidx, custom_settings=self.custom_settings
68-
)
69-
except CompletionError as ex:
70-
# Don't print unless error has length
71-
err_str = str(ex)
72-
if err_str:
73-
general_console = ru.Cmd2GeneralConsole()
74-
with general_console.capture() as capture:
75-
styled_err = Text(err_str, style=Cmd2Style.ERROR if ex.apply_style else "")
76-
general_console.print(styled_err, end="")
77-
print_formatted_text(ANSI(capture.get()))
78-
return
79-
except Exception as ex: # noqa: BLE001
80-
formatted_exception = self.cmd_app.format_exception(ex)
81-
print_formatted_text(ANSI(formatted_exception))
61+
completions = self.cmd_app.complete(
62+
text, line=line, begidx=begidx, endidx=endidx, custom_settings=self.custom_settings
63+
)
64+
65+
if completions.completion_error:
66+
print_formatted_text(ANSI(completions.completion_error + "\n"))
8267
return
8368

8469
# Print completion table if present

0 commit comments

Comments
 (0)