@@ -4059,13 +4059,13 @@ def py_quit():
40594059 # This is to prevent pyscripts from editing it. (e.g. locals().clear()). It also ensures a pyscript's
40604060 # environment won't be filled with data from a previously run pyscript. Only make a shallow copy since
40614061 # it's OK for py_locals to contain objects which are editable in a pyscript.
4062- localvars = dict ( self .py_locals )
4063- localvars [self .py_bridge_name ] = py_bridge
4064- localvars ['quit' ] = py_quit
4065- localvars ['exit' ] = py_quit
4062+ local_vars = self .py_locals . copy ( )
4063+ local_vars [self .py_bridge_name ] = py_bridge
4064+ local_vars ['quit' ] = py_quit
4065+ local_vars ['exit' ] = py_quit
40664066
40674067 if self .self_in_py :
4068- localvars ['self' ] = self
4068+ local_vars ['self' ] = self
40694069
40704070 # Handle case where we were called by run_pyscript
40714071 if pyscript is not None :
@@ -4079,16 +4079,16 @@ def py_quit():
40794079 self .pexcept ("Error reading script file '{}': {}" .format (expanded_filename , ex ))
40804080 return
40814081
4082- localvars ['__name__' ] = '__main__'
4083- localvars ['__file__' ] = expanded_filename
4082+ local_vars ['__name__' ] = '__main__'
4083+ local_vars ['__file__' ] = expanded_filename
40844084
40854085 # Place the script's directory at sys.path[0] just as Python does when executing a script
40864086 saved_sys_path = list (sys .path )
40874087 sys .path .insert (0 , os .path .dirname (os .path .abspath (expanded_filename )))
40884088
40894089 else :
40904090 # This is the default name chosen by InteractiveConsole when no locals are passed in
4091- localvars ['__name__' ] = '__console__'
4091+ local_vars ['__name__' ] = '__console__'
40924092
40934093 if args .command :
40944094 py_code_to_run = args .command
@@ -4100,7 +4100,7 @@ def py_quit():
41004100 py_bridge .cmd_echo = True
41014101
41024102 # Create the Python interpreter
4103- interp = InteractiveConsole (locals = localvars )
4103+ interp = InteractiveConsole (locals = local_vars )
41044104
41054105 # Check if we are running Python code
41064106 if py_code_to_run :
@@ -4201,24 +4201,20 @@ def do_ipy(self, _: argparse.Namespace) -> Optional[bool]:
42014201 PyBridge ,
42024202 )
42034203
4204- # noinspection PyUnusedLocal
4205- def load_ipy (cmd2_app : Cmd , py_bridge : PyBridge ):
4204+ def load_ipy (ipy_locals : Dict [str , Any ]) -> None :
42064205 """
42074206 Embed an IPython shell in an environment that is restricted to only the variables in this function
42084207
4209- :param cmd2_app: instance of the cmd2 app
4210- :param py_bridge: a PyBridge
4208+ :param ipy_locals: locals dictionary for the IPython environment
42114209 """
4212- # Create a variable pointing to py_bridge and name it using the value of py_bridge_name
4213- exec ("{} = py_bridge" .format (cmd2_app .py_bridge_name ))
4210+ # Copy ipy_locals into this function's locals
4211+ for key , val in ipy_locals .items ():
4212+ locals ()[key ] = val
42144213
4215- # Add self variable pointing to cmd2_app, if allowed
4216- if cmd2_app .self_in_py :
4217- exec ("self = cmd2_app" )
4218-
4219- # Delete these names from the environment so IPython can't use them
4220- del cmd2_app
4221- del py_bridge
4214+ # Delete these names from the environment so IPython won't see them
4215+ del key
4216+ del val
4217+ del ipy_locals
42224218
42234219 # Start ipy shell
42244220 embed (
@@ -4235,9 +4231,18 @@ def load_ipy(cmd2_app: Cmd, py_bridge: PyBridge):
42354231
42364232 try :
42374233 self ._in_py = True
4238- new_py_bridge = PyBridge (self )
4239- load_ipy (self , new_py_bridge )
4240- return new_py_bridge .stop
4234+ py_bridge = PyBridge (self )
4235+
4236+ # Make a copy of self.py_locals for the locals dictionary in the IPython environment we are creating.
4237+ # This is to prevent ipy from editing it. (e.g. locals().clear()). Only make a shallow copy since
4238+ # it's OK for py_locals to contain objects which are editable in ipy.
4239+ local_vars = self .py_locals .copy ()
4240+ local_vars [self .py_bridge_name ] = py_bridge
4241+ if self .self_in_py :
4242+ local_vars ['self' ] = self
4243+
4244+ load_ipy (local_vars )
4245+ return py_bridge .stop
42414246 finally :
42424247 self ._in_py = False
42434248
0 commit comments