Skip to content

Commit fc3c9b9

Browse files
committed
Removed ipy's access to the CLI's globals() dictionary
1 parent ae03c59 commit fc3c9b9

File tree

2 files changed

+33
-35
lines changed

2 files changed

+33
-35
lines changed

cmd2/cmd2.py

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
try:
169169
# noinspection PyUnresolvedReferences,PyPackageRequirements
170170
from IPython import ( # type: ignore[import]
171-
embed,
171+
start_ipython,
172172
)
173173
except ImportError: # pragma: no cover
174174
ipython_available = False
@@ -4197,34 +4197,21 @@ def do_ipy(self, _: argparse.Namespace) -> Optional[bool]:
41974197
41984198
:return: True if running of commands should stop
41994199
"""
4200+
# noinspection PyPackageRequirements
4201+
from IPython.terminal.interactiveshell import (
4202+
TerminalInteractiveShell,
4203+
)
4204+
from IPython.terminal.ipapp import (
4205+
TerminalIPythonApp,
4206+
)
4207+
from traitlets.config.loader import (
4208+
Config as TraitletsConfig,
4209+
)
4210+
42004211
from .py_bridge import (
42014212
PyBridge,
42024213
)
42034214

4204-
def load_ipy(ipy_locals: Dict[str, Any]) -> None:
4205-
"""
4206-
Embed an IPython shell in an environment that is restricted to only the variables in this function
4207-
4208-
:param ipy_locals: locals dictionary for the IPython environment
4209-
"""
4210-
# Copy ipy_locals into this function's locals
4211-
for key, val in ipy_locals.items():
4212-
locals()[key] = val
4213-
4214-
# Delete these names from the environment so IPython won't see them
4215-
del key
4216-
del val
4217-
del ipy_locals
4218-
4219-
# Start ipy shell
4220-
embed(
4221-
banner1=(
4222-
'Entering an embedded IPython shell. Type quit or <Ctrl>-d to exit.\n'
4223-
'Run Python code from external files with: run filename.py\n'
4224-
),
4225-
exit_msg='Leaving IPython, back to {}'.format(sys.argv[0]),
4226-
)
4227-
42284215
if self.in_pyscript():
42294216
self.perror("Recursively entering interactive Python shells is not allowed")
42304217
return
@@ -4241,7 +4228,23 @@ def load_ipy(ipy_locals: Dict[str, Any]) -> None:
42414228
if self.self_in_py:
42424229
local_vars['self'] = self
42434230

4244-
load_ipy(local_vars)
4231+
# Configure IPython
4232+
config = TraitletsConfig()
4233+
config.InteractiveShell.banner2 = (
4234+
'Entering an embedded IPython shell. Type quit or <Ctrl>-d to exit.\n'
4235+
'Run Python code from external files with: run filename.py\n'
4236+
)
4237+
4238+
# Start IPython
4239+
start_ipython(config=config, argv=[], user_ns=local_vars)
4240+
4241+
# The IPython application is a singleton and won't be recreated next time
4242+
# this function runs. That's a problem since the contents of local_vars
4243+
# may need to be changed. Therefore we must destroy all instances of the
4244+
# relevant classes.
4245+
TerminalIPythonApp.clear_instance()
4246+
TerminalInteractiveShell.clear_instance()
4247+
42454248
return py_bridge.stop
42464249
finally:
42474250
self._in_py = False

docs/features/embedded_python_shells.rst

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ You may optionally enable full access to to your application by setting
1212
python session, which is a reference to your ``cmd2`` application. This can be
1313
useful for debugging your application.
1414

15+
Any local or global variable created within the Python session will not persist
16+
in the CLI's environment.
17+
1518
Anything in ``self.py_locals`` is always available in the Python environment.
1619

1720
The ``app`` object (or your custom name) provides access to application
@@ -48,14 +51,6 @@ More Python examples:
4851
>>> quit()
4952
Python was here >
5053

51-
Using the ``py`` command is tightly integrated with your main ``cmd2``
52-
application and any variables created or changed will persist for the life of
53-
the application::
54-
55-
(Cmd) py x = 5
56-
(Cmd) py print(x)
57-
5
58-
5954
The ``py`` command also allows you to run Python scripts via ``py
6055
run('myscript.py')``. This provides a more complicated and more powerful
6156
scripting capability than that provided by the simple text file scripts
@@ -113,7 +108,7 @@ The ``ipy`` command enters an interactive IPython_ session. Similar to an
113108
interactive Python session, this shell can access your application instance via
114109
``self`` if ``self.self_in_py`` is ``True`` and any changes to your application
115110
made via ``self`` will persist. However, any local or global variable created
116-
within the ``ipy`` shell will not persist.
111+
within the ``ipy`` shell will not persist in the CLI's environment
117112

118113
Also, as in the interactive Python session, the ``ipy`` shell has access to the
119114
contents of ``self.py_locals`` and can call back into the application using the

0 commit comments

Comments
 (0)