3535import inspect
3636import os
3737import re
38- import shlex
3938import sys
4039import threading
4140from typing import Any , Callable , Dict , List , Mapping , Optional , Tuple , Type , Union , IO
4847from . import utils
4948from .argparse_completer import AutoCompleter , ACArgumentParser , ACTION_ARG_CHOICES
5049from .clipboard import can_clip , get_paste_buffer , write_to_paste_buffer
51- from .parsing import StatementParser , Statement , Macro , MacroArg
5250from .history import History , HistoryItem
51+ from .parsing import StatementParser , Statement , Macro , MacroArg , shlex_split , get_command_arg_list
5352
5453# Set up readline
5554from .rl_utils import rl_type , RlType , rl_get_point , rl_set_prompt , vt100_support , rl_make_safe_prompt
@@ -150,24 +149,6 @@ def categorize(func: Union[Callable, Iterable], category: str) -> None:
150149 setattr (func , HELP_CATEGORY , category )
151150
152151
153- def parse_quoted_string (string : str , preserve_quotes : bool ) -> List [str ]:
154- """
155- Parse a quoted string into a list of arguments
156- :param string: the string being parsed
157- :param preserve_quotes: if True, then quotes will not be stripped
158- """
159- if isinstance (string , list ):
160- # arguments are already a list, return the list we were passed
161- lexed_arglist = string
162- else :
163- # Use shlex to split the command line into a list of arguments based on shell rules
164- lexed_arglist = shlex .split (string , comments = False , posix = False )
165-
166- if not preserve_quotes :
167- lexed_arglist = [utils .strip_quotes (arg ) for arg in lexed_arglist ]
168- return lexed_arglist
169-
170-
171152def with_category (category : str ) -> Callable :
172153 """A decorator to apply a category to a command function."""
173154 def cat_decorator (func ):
@@ -178,8 +159,7 @@ def cat_decorator(func):
178159
179160def with_argument_list (* args : List [Callable ], preserve_quotes : bool = False ) -> Callable [[List ], Optional [bool ]]:
180161 """A decorator to alter the arguments passed to a do_* cmd2 method. Default passes a string of whatever the user
181- typed. With this decorator, the decorated method will receive a list of arguments parsed from user input using
182- shlex.split().
162+ typed. With this decorator, the decorated method will receive a list of arguments parsed from user input.
183163
184164 :param args: Single-element positional argument list containing do_* method this decorator is wrapping
185165 :param preserve_quotes: if True, then argument quotes will not be stripped
@@ -189,9 +169,9 @@ def with_argument_list(*args: List[Callable], preserve_quotes: bool = False) ->
189169
190170 def arg_decorator (func : Callable ):
191171 @functools .wraps (func )
192- def cmd_wrapper (self , cmdline ):
193- lexed_arglist = parse_quoted_string ( cmdline , preserve_quotes )
194- return func (self , lexed_arglist )
172+ def cmd_wrapper (cmd2_instance , statement : Union [ Statement , str ] ):
173+ parsed_arglist = get_command_arg_list ( statement , preserve_quotes )
174+ return func (cmd2_instance , parsed_arglist )
195175
196176 cmd_wrapper .__doc__ = func .__doc__
197177 return cmd_wrapper
@@ -214,16 +194,17 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, preserve
214194 import functools
215195
216196 # noinspection PyProtectedMember
217- def arg_decorator (func : Callable [[ Statement ], Optional [ bool ]] ):
197+ def arg_decorator (func : Callable ):
218198 @functools .wraps (func )
219- def cmd_wrapper (instance , cmdline ):
220- lexed_arglist = parse_quoted_string (cmdline , preserve_quotes )
199+ def cmd_wrapper (cmd2_instance , statement : Union [Statement , str ]):
200+ parsed_arglist = get_command_arg_list (statement , preserve_quotes )
201+
221202 try :
222- args , unknown = argparser .parse_known_args (lexed_arglist )
203+ args , unknown = argparser .parse_known_args (parsed_arglist )
223204 except SystemExit :
224205 return
225206 else :
226- return func (instance , args , unknown )
207+ return func (cmd2_instance , args , unknown )
227208
228209 # argparser defaults the program name to sys.argv[0]
229210 # we want it to be the name of our command
@@ -256,16 +237,18 @@ def with_argparser(argparser: argparse.ArgumentParser,
256237 import functools
257238
258239 # noinspection PyProtectedMember
259- def arg_decorator (func : Callable [[ Statement ], Optional [ bool ]] ):
240+ def arg_decorator (func : Callable ):
260241 @functools .wraps (func )
261- def cmd_wrapper (instance , cmdline ):
262- lexed_arglist = parse_quoted_string (cmdline , preserve_quotes )
242+ def cmd_wrapper (cmd2_instance , statement : Union [Statement , str ]):
243+
244+ parsed_arglist = get_command_arg_list (statement , preserve_quotes )
245+
263246 try :
264- args = argparser .parse_args (lexed_arglist )
247+ args = argparser .parse_args (parsed_arglist )
265248 except SystemExit :
266249 return
267250 else :
268- return func (instance , args )
251+ return func (cmd2_instance , args )
269252
270253 # argparser defaults the program name to sys.argv[0]
271254 # we want it to be the name of our command
@@ -742,8 +725,7 @@ def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> Tuple[Li
742725 # Parse the line into tokens
743726 while True :
744727 try :
745- # Use non-POSIX parsing to keep the quotes around the tokens
746- initial_tokens = shlex .split (tmp_line [:tmp_endidx ], comments = False , posix = False )
728+ initial_tokens = shlex_split (tmp_line [:tmp_endidx ])
747729
748730 # If the cursor is at an empty token outside of a quoted string,
749731 # then that is the token being completed. Add it to the list.
@@ -1735,7 +1717,7 @@ def _run_cmdfinalization_hooks(self, stop: bool, statement: Optional[Statement])
17351717 # Fix those annoying problems that occur with terminal programs like "less" when you pipe to them
17361718 if self .stdin .isatty ():
17371719 import subprocess
1738- proc = subprocess .Popen (shlex . split ( 'stty sane' ) )
1720+ proc = subprocess .Popen ([ 'stty' , ' sane'] )
17391721 proc .communicate ()
17401722
17411723 try :
0 commit comments