Skip to content

Commit b3eafe7

Browse files
committed
Added Cmd2ShlexError
1 parent d17f794 commit b3eafe7

File tree

5 files changed

+34
-20
lines changed

5 files changed

+34
-20
lines changed

cmd2/cmd2.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
from .argparse_custom import CompletionItem, DEFAULT_ARGUMENT_PARSER
5151
from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer
5252
from .decorators import with_argparser
53-
from .exceptions import Cmd2ArgparseException, EmbeddedConsoleExit, EmptyStatement
53+
from .exceptions import Cmd2ArgparseError, Cmd2ShlexError, EmbeddedConsoleExit, EmptyStatement
5454
from .history import History, HistoryItem
5555
from .parsing import StatementParser, Statement, Macro, MacroArg, shlex_split
5656
from .rl_utils import rl_type, RlType, rl_get_point, rl_set_prompt, vt100_support, rl_make_safe_prompt, rl_warning
@@ -1599,9 +1599,8 @@ def onecmd_plus_hooks(self, line: str, *, add_to_history: bool = True, py_bridge
15991599
stop = False
16001600
try:
16011601
statement = self._input_line_to_statement(line)
1602-
except (EmptyStatement, ValueError) as ex:
1603-
if isinstance(ex, ValueError):
1604-
# Since shlex.split() failed on syntax, let user know what's going on
1602+
except (EmptyStatement, Cmd2ShlexError) as ex:
1603+
if isinstance(ex, Cmd2ShlexError):
16051604
self.perror("Invalid syntax: {}".format(ex))
16061605
return self._run_cmdfinalization_hooks(stop, None)
16071606

@@ -1683,7 +1682,7 @@ def onecmd_plus_hooks(self, line: str, *, add_to_history: bool = True, py_bridge
16831682
# Stop saving command's stdout before command finalization hooks run
16841683
self.stdout.pause_storage = True
16851684

1686-
except (Cmd2ArgparseException, EmptyStatement):
1685+
except (Cmd2ArgparseError, EmptyStatement):
16871686
# Don't do anything, but do allow command finalization hooks to run
16881687
pass
16891688
except Exception as ex:
@@ -1743,6 +1742,8 @@ def _complete_statement(self, line: str) -> Statement:
17431742
17441743
:param line: the line being parsed
17451744
:return: the completed Statement
1745+
:raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation)
1746+
EmptyStatement when the resulting Statement is blank
17461747
"""
17471748
while True:
17481749
try:
@@ -1754,7 +1755,7 @@ def _complete_statement(self, line: str) -> Statement:
17541755
# it's not a multiline command, but we parsed it ok
17551756
# so we are done
17561757
break
1757-
except ValueError:
1758+
except Cmd2ShlexError:
17581759
# we have unclosed quotation marks, lets parse only the command
17591760
# and see if it's a multiline
17601761
statement = self.statement_parser.parse_command_only(line)
@@ -1791,7 +1792,7 @@ def _complete_statement(self, line: str) -> Statement:
17911792
self._at_continuation_prompt = False
17921793

17931794
if not statement.command:
1794-
raise EmptyStatement()
1795+
raise EmptyStatement
17951796
return statement
17961797

17971798
def _input_line_to_statement(self, line: str) -> Statement:
@@ -1800,6 +1801,8 @@ def _input_line_to_statement(self, line: str) -> Statement:
18001801
18011802
:param line: the line being parsed
18021803
:return: parsed command line as a Statement
1804+
:raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation)
1805+
EmptyStatement when the resulting Statement is blank
18031806
"""
18041807
used_macros = []
18051808
orig_line = None
@@ -1818,7 +1821,7 @@ def _input_line_to_statement(self, line: str) -> Statement:
18181821
used_macros.append(statement.command)
18191822
line = self._resolve_macro(statement)
18201823
if line is None:
1821-
raise EmptyStatement()
1824+
raise EmptyStatement
18221825
else:
18231826
break
18241827

cmd2/decorators.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Callable, List, Optional, Union
55

66
from . import constants
7-
from .exceptions import Cmd2ArgparseException
7+
from .exceptions import Cmd2ArgparseError
88
from .parsing import Statement
99

1010

@@ -145,7 +145,7 @@ def cmd_wrapper(cmd2_app, statement: Union[Statement, str]):
145145
try:
146146
args, unknown = parser.parse_known_args(parsed_arglist, namespace)
147147
except SystemExit:
148-
raise Cmd2ArgparseException
148+
raise Cmd2ArgparseError
149149
else:
150150
setattr(args, '__statement__', statement)
151151
return func(cmd2_app, args, unknown)
@@ -217,7 +217,7 @@ def cmd_wrapper(cmd2_app, statement: Union[Statement, str]):
217217
try:
218218
args = parser.parse_args(parsed_arglist, namespace)
219219
except SystemExit:
220-
raise Cmd2ArgparseException
220+
raise Cmd2ArgparseError
221221
else:
222222
setattr(args, '__statement__', statement)
223223
return func(cmd2_app, args)

cmd2/exceptions.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
22
"""Custom exceptions for cmd2. These are NOT part of the public API and are intended for internal use only."""
33

44

5-
class Cmd2ArgparseException(Exception):
6-
"""Custom exception class for when an argparse-decorated command has an error parsing its arguments"""
5+
class Cmd2ArgparseError(Exception):
6+
"""
7+
Custom exception class for when a command has an error parsing its arguments.
8+
This can be raised by argparse decorators or the command functions themselves.
9+
The main use of this exception is to tell cmd2 not to run Postcommand hooks.
10+
"""
11+
pass
12+
13+
14+
class Cmd2ShlexError(Exception):
15+
"""Raised when shlex fails to parse a command line string in StatementParser"""
716
pass
817

918

cmd2/parsing.py

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

1111
from . import constants
1212
from . import utils
13+
from .exceptions import Cmd2ShlexError
1314

1415

1516
def shlex_split(str_to_split: str) -> List[str]:
@@ -330,7 +331,7 @@ def tokenize(self, line: str) -> List[str]:
330331
331332
:param line: the command line being lexed
332333
:return: A list of tokens
333-
:raises ValueError: if there are unclosed quotation marks
334+
:raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation)
334335
"""
335336

336337
# expand shortcuts and aliases
@@ -341,7 +342,10 @@ def tokenize(self, line: str) -> List[str]:
341342
return []
342343

343344
# split on whitespace
344-
tokens = shlex_split(line)
345+
try:
346+
tokens = shlex_split(line)
347+
except ValueError as ex:
348+
raise Cmd2ShlexError(ex)
345349

346350
# custom lexing
347351
tokens = self.split_on_punctuation(tokens)
@@ -355,7 +359,7 @@ def parse(self, line: str) -> Statement:
355359
356360
:param line: the command line being parsed
357361
:return: a new :class:`~cmd2.Statement` object
358-
:raises ValueError: if there are unclosed quotation marks
362+
:raises: Cmd2ShlexError if a shlex error occurs (e.g. No closing quotation)
359363
"""
360364

361365
# handle the special case/hardcoded terminator of a blank line
@@ -518,8 +522,6 @@ def parse_command_only(self, rawinput: str) -> Statement:
518522
:param rawinput: the command line as entered by the user
519523
:return: a new :class:`~cmd2.Statement` object
520524
"""
521-
line = rawinput
522-
523525
# expand shortcuts and aliases
524526
line = self._expand(rawinput)
525527

tests/test_parsing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def test_tokenize(parser, line, tokens):
9696
assert tokens_to_test == tokens
9797

9898
def test_tokenize_unclosed_quotes(parser):
99-
with pytest.raises(ValueError):
99+
with pytest.raises(exceptions.Cmd2ShlexError):
100100
_ = parser.tokenize('command with "unclosed quotes')
101101

102102
@pytest.mark.parametrize('tokens,command,args', [
@@ -583,7 +583,7 @@ def test_parse_redirect_to_unicode_filename(parser):
583583
assert statement.output_to == 'café'
584584

585585
def test_parse_unclosed_quotes(parser):
586-
with pytest.raises(ValueError):
586+
with pytest.raises(exceptions.Cmd2ShlexError):
587587
_ = parser.tokenize("command with 'unclosed quotes")
588588

589589
def test_empty_statement_raises_exception():

0 commit comments

Comments
 (0)