@@ -3245,42 +3245,55 @@ def _restore_cmd2_env(self, cmd2_env: _SavedCmd2Env) -> None:
32453245 py_parser .add_argument ('command' , nargs = argparse .OPTIONAL , help = "command to run" )
32463246 py_parser .add_argument ('remainder' , nargs = argparse .REMAINDER , help = "remainder of command" )
32473247
3248+ # This is a hidden flag for telling do_py to run a pyscript. It is intended only to be used by run_pyscript
3249+ # after it sets up sys.argv for the script being run. When this flag is present, it takes precedence over all
3250+ # other arguments. run_pyscript uses this method instead of "py run('file')" because file names with spaces cause
3251+ # issues with our parser, which isn't meant to parse Python statements.
3252+ py_parser .add_argument ('--pyscript' , help = argparse .SUPPRESS )
3253+
32483254 # Preserve quotes since we are passing these strings to Python
32493255 @with_argparser (py_parser , preserve_quotes = True )
3250- def do_py (self , args : argparse .Namespace ) -> bool :
3251- """Invoke Python command or shell"""
3256+ def do_py (self , args : argparse .Namespace ) -> Optional [bool ]:
3257+ """
3258+ Enter an interactive Python shell
3259+ :return: True if running of commands should stop
3260+ """
32523261 from .py_bridge import PyBridge
32533262 if self ._in_py :
32543263 err = "Recursively entering interactive Python consoles is not allowed."
32553264 self .perror (err )
3256- return False
3265+ return
32573266
32583267 py_bridge = PyBridge (self )
3268+ py_code_to_run = ''
3269+
3270+ # Handle case where we were called by run_pyscript
3271+ if args .pyscript :
3272+ py_code_to_run = 'run({!r})' .format (args .pyscript )
3273+
3274+ elif args .command :
3275+ py_code_to_run = args .command
3276+ if args .remainder :
3277+ py_code_to_run += ' ' + ' ' .join (args .remainder )
3278+
3279+ # Set cmd_echo to True so PyBridge statements like: py app('help')
3280+ # run at the command line will print their output.
3281+ py_bridge .cmd_echo = True
32593282
32603283 try :
32613284 self ._in_py = True
32623285
3263- # Support the run command even if called prior to invoking an interactive interpreter
32643286 def py_run (filename : str ):
32653287 """Run a Python script file in the interactive console.
3266- :param filename: filename of *.py script file to run
3288+ :param filename: filename of script file to run
32673289 """
32683290 expanded_filename = os .path .expanduser (filename )
32693291
3270- if not expanded_filename .endswith ('.py' ):
3271- self .pwarning ("'{}' does not have a .py extension" .format (expanded_filename ))
3272- selection = self .select ('Yes No' , 'Continue to try to run it as a Python script? ' )
3273- if selection != 'Yes' :
3274- return
3275-
3276- # cmd_echo defaults to False for scripts. The user can always toggle this value in their script.
3277- py_bridge .cmd_echo = False
3278-
32793292 try :
32803293 with open (expanded_filename ) as f :
32813294 interp .runcode (f .read ())
32823295 except OSError as ex :
3283- self .pexcept ("Error opening script file '{}': {}" .format (expanded_filename , ex ))
3296+ self .pexcept ("Error reading script file '{}': {}" .format (expanded_filename , ex ))
32843297
32853298 def py_quit ():
32863299 """Function callable from the interactive Python console to exit that environment"""
@@ -3301,24 +3314,16 @@ def py_quit():
33013314 interp = InteractiveConsole (locals = localvars )
33023315 interp .runcode ('import sys, os;sys.path.insert(0, os.getcwd())' )
33033316
3304- # Check if the user is running a Python statement on the command line
3305- if args .command :
3306- full_command = args .command
3307- if args .remainder :
3308- full_command += ' ' + ' ' .join (args .remainder )
3309-
3310- # Set cmd_echo to True so PyBridge statements like: py app('help')
3311- # run at the command line will print their output.
3312- py_bridge .cmd_echo = True
3313-
3317+ # Check if we are running Python code
3318+ if py_code_to_run :
33143319 # noinspection PyBroadException
33153320 try :
3316- interp .runcode (full_command )
3321+ interp .runcode (py_code_to_run )
33173322 except BaseException :
3318- # We don't care about any exception that happened in the interactive console
3323+ # We don't care about any exception that happened in the Python code
33193324 pass
33203325
3321- # If there are no args, then we will open an interactive Python shell
3326+ # Otherwise we will open an interactive Python shell
33223327 else :
33233328 cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
33243329 instructions = ('End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()`.\n '
@@ -3360,21 +3365,31 @@ def py_quit():
33603365 help = 'arguments to pass to script' , completer_method = path_complete )
33613366
33623367 @with_argparser (run_pyscript_parser )
3363- def do_run_pyscript (self , args : argparse .Namespace ) -> bool :
3364- """Run a Python script file inside the console"""
3365- script_path = os .path .expanduser (args .script_path )
3368+ def do_run_pyscript (self , args : argparse .Namespace ) -> Optional [bool ]:
3369+ """
3370+ Run a Python script file inside the console
3371+ :return: True if running of commands should stop
3372+ """
3373+ # Expand ~ before placing this path in sys.argv just as a shell would
3374+ args .script_path = os .path .expanduser (args .script_path )
3375+
3376+ # Add some protection against accidentally running a non-Python file. The happens when users
3377+ # mix up run_script and run_pyscript.
3378+ if not args .script_path .endswith ('.py' ):
3379+ self .pwarning ("'{}' does not have a .py extension" .format (args .script_path ))
3380+ selection = self .select ('Yes No' , 'Continue to try to run it as a Python script? ' )
3381+ if selection != 'Yes' :
3382+ return
3383+
33663384 py_return = False
33673385
33683386 # Save current command line arguments
33693387 orig_args = sys .argv
33703388
33713389 try :
33723390 # Overwrite sys.argv to allow the script to take command line arguments
3373- sys .argv = [script_path ] + args .script_arguments
3374-
3375- # Run the script - use repr formatting to escape things which
3376- # need to be escaped to prevent issues on Windows
3377- py_return = self .do_py ("run({!r})" .format (script_path ))
3391+ sys .argv = [args .script_path ] + args .script_arguments
3392+ py_return = self .do_py ('--pyscript {}' .format (utils .quote_string_if_needed (args .script_path )))
33783393
33793394 except KeyboardInterrupt :
33803395 pass
@@ -3783,6 +3798,8 @@ def do_run_script(self, args: argparse.Namespace) -> Optional[bool]:
37833798 self .perror ("'{}' is not an ASCII or UTF-8 encoded text file" .format (expanded_path ))
37843799 return
37853800
3801+ # Add some protection against accidentally running a Python file. The happens when users
3802+ # mix up run_script and run_pyscript.
37863803 if expanded_path .endswith ('.py' ):
37873804 self .pwarning ("'{}' appears to be a Python file" .format (expanded_path ))
37883805 selection = self .select ('Yes No' , 'Continue to try to run it as a text script? ' )
0 commit comments