@@ -146,12 +146,12 @@ def with_argument_list(*args: List[Callable], preserve_quotes: bool = False) ->
146146
147147 def arg_decorator (func : Callable ):
148148 @functools .wraps (func )
149- def cmd_wrapper (cmd2_instance , statement : Union [Statement , str ]):
150- _ , parsed_arglist = cmd2_instance .statement_parser .get_command_arg_list (command_name ,
151- statement ,
152- preserve_quotes )
149+ def cmd_wrapper (cmd2_app , statement : Union [Statement , str ]):
150+ _ , parsed_arglist = cmd2_app .statement_parser .get_command_arg_list (command_name ,
151+ statement ,
152+ preserve_quotes )
153153
154- return func (cmd2_instance , parsed_arglist )
154+ return func (cmd2_app , parsed_arglist )
155155
156156 command_name = func .__name__ [len (COMMAND_FUNC_PREFIX ):]
157157 cmd_wrapper .__doc__ = func .__doc__
@@ -185,23 +185,23 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, *,
185185 # noinspection PyProtectedMember
186186 def arg_decorator (func : Callable ):
187187 @functools .wraps (func )
188- def cmd_wrapper (cmd2_instance , statement : Union [Statement , str ]):
189- statement , parsed_arglist = cmd2_instance .statement_parser .get_command_arg_list (command_name ,
190- statement ,
191- preserve_quotes )
188+ def cmd_wrapper (cmd2_app , statement : Union [Statement , str ]):
189+ statement , parsed_arglist = cmd2_app .statement_parser .get_command_arg_list (command_name ,
190+ statement ,
191+ preserve_quotes )
192192
193193 if ns_provider is None :
194194 namespace = None
195195 else :
196- namespace = ns_provider (cmd2_instance )
196+ namespace = ns_provider (cmd2_app )
197197
198198 try :
199199 args , unknown = argparser .parse_known_args (parsed_arglist , namespace )
200200 except SystemExit :
201201 return
202202 else :
203203 setattr (args , '__statement__' , statement )
204- return func (cmd2_instance , args , unknown )
204+ return func (cmd2_app , args , unknown )
205205
206206 # argparser defaults the program name to sys.argv[0]
207207 # we want it to be the name of our command
@@ -243,23 +243,23 @@ def with_argparser(argparser: argparse.ArgumentParser, *,
243243 # noinspection PyProtectedMember
244244 def arg_decorator (func : Callable ):
245245 @functools .wraps (func )
246- def cmd_wrapper (cmd2_instance , statement : Union [Statement , str ]):
247- statement , parsed_arglist = cmd2_instance .statement_parser .get_command_arg_list (command_name ,
248- statement ,
249- preserve_quotes )
246+ def cmd_wrapper (cmd2_app , statement : Union [Statement , str ]):
247+ statement , parsed_arglist = cmd2_app .statement_parser .get_command_arg_list (command_name ,
248+ statement ,
249+ preserve_quotes )
250250
251251 if ns_provider is None :
252252 namespace = None
253253 else :
254- namespace = ns_provider (cmd2_instance )
254+ namespace = ns_provider (cmd2_app )
255255
256256 try :
257257 args = argparser .parse_args (parsed_arglist , namespace )
258258 except SystemExit :
259259 return
260260 else :
261261 setattr (args , '__statement__' , statement )
262- return func (cmd2_instance , args )
262+ return func (cmd2_app , args )
263263
264264 # argparser defaults the program name to sys.argv[0]
265265 # we want it to be the name of our command
@@ -412,9 +412,14 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
412412 # Command aliases and macros
413413 self .macros = dict ()
414414
415- self . _pystate = {}
415+ # Keeps track of typed command history in the Python shell
416416 self ._py_history = []
417- self .pyscript_name = 'app'
417+
418+ # The name by which Python and IPython environments refer to the PyscriptBridge to call app commands
419+ self .py_bridge_name = 'app'
420+
421+ # Defines app-specific variables/functions available in Python shells and pyscripts
422+ self .py_locals = {}
418423
419424 self .statement_parser = StatementParser (allow_redirection = allow_redirection ,
420425 terminators = terminators ,
@@ -3231,17 +3236,17 @@ def py_quit():
32313236 raise EmbeddedConsoleExit
32323237
32333238 # Set up Python environment
3234- self ._pystate [self .pyscript_name ] = bridge
3235- self ._pystate ['run' ] = py_run
3236- self ._pystate ['quit' ] = py_quit
3237- self ._pystate ['exit' ] = py_quit
3239+ self .py_locals [self .py_bridge_name ] = bridge
3240+ self .py_locals ['run' ] = py_run
3241+ self .py_locals ['quit' ] = py_quit
3242+ self .py_locals ['exit' ] = py_quit
32383243
32393244 if self .locals_in_py :
3240- self ._pystate ['self' ] = self
3241- elif 'self' in self ._pystate :
3242- del self ._pystate ['self' ]
3245+ self .py_locals ['self' ] = self
3246+ elif 'self' in self .py_locals :
3247+ del self .py_locals ['self' ]
32433248
3244- localvars = self ._pystate
3249+ localvars = self .py_locals
32453250 interp = InteractiveConsole (locals = localvars )
32463251 interp .runcode ('import sys, os;sys.path.insert(0, os.getcwd())' )
32473252
@@ -3268,7 +3273,7 @@ def py_quit():
32683273 instructions = ('End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()`.\n '
32693274 'Non-Python commands can be issued with: {}("your command")\n '
32703275 'Run Python code from external script files with: run("script.py")'
3271- .format (self .pyscript_name ))
3276+ .format (self .py_bridge_name ))
32723277
32733278 saved_cmd2_env = None
32743279
@@ -3335,22 +3340,30 @@ def do_run_pyscript(self, args: argparse.Namespace) -> bool:
33353340 def do_ipy (self , _ : argparse .Namespace ) -> None :
33363341 """Enter an interactive IPython shell"""
33373342 from .pyscript_bridge import PyscriptBridge
3338- bridge = PyscriptBridge (self )
3339-
33403343 banner = ('Entering an embedded IPython shell. Type quit or <Ctrl>-d to exit.\n '
33413344 'Run Python code from external files with: run filename.py\n ' )
33423345 exit_msg = 'Leaving IPython, back to {}' .format (sys .argv [0 ])
33433346
3344- if self .locals_in_py :
3345- # noinspection PyUnusedLocal
3346- def load_ipy (cmd2_instance , app ):
3347- embed (banner1 = banner , exit_msg = exit_msg )
3348- load_ipy (self , bridge )
3349- else :
3350- # noinspection PyUnusedLocal
3351- def load_ipy (app ):
3352- embed (banner1 = banner , exit_msg = exit_msg )
3353- load_ipy (bridge )
3347+ def load_ipy (cmd2_app : Cmd ):
3348+ """
3349+ Embed an IPython shell in an environment that is restricted to only the variables in this function
3350+ :param cmd2_app: the instance of the cmd2 app
3351+ """
3352+ # Create a variable pointing to the PyscriptBridge and name it using the value of py_bridge_name
3353+ bridge = PyscriptBridge (cmd2_app )
3354+ exec ("{} = bridge" .format (cmd2_app .py_bridge_name ))
3355+
3356+ # Add self variable pointing to cmd2_app, if allowed
3357+ if cmd2_app .locals_in_py :
3358+ exec ("self = cmd2_app" )
3359+
3360+ # Delete these names from the environment
3361+ del bridge
3362+ del cmd2_app
3363+
3364+ embed (banner1 = banner , exit_msg = exit_msg )
3365+
3366+ load_ipy (self )
33543367
33553368 history_description = "View, run, edit, save, or clear previously entered commands"
33563369
0 commit comments