summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2021-03-24 13:39:54 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2021-03-24 16:33:27 -0400
commitfc3c9b9e17e3cb89f771cc618517ebd968730938 (patch)
treefabbd6858c6090c78e80a037b3fbfdfd13f02123
parentae03c59de609878e068920380667c5fd45f26650 (diff)
downloadcmd2-git-fc3c9b9e17e3cb89f771cc618517ebd968730938.tar.gz
Removed ipy's access to the CLI's globals() dictionary
-rw-r--r--cmd2/cmd2.py55
-rw-r--r--docs/features/embedded_python_shells.rst13
2 files changed, 33 insertions, 35 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index b6c44eb3..b04b7771 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -168,7 +168,7 @@ ipython_available = True
try:
# noinspection PyUnresolvedReferences,PyPackageRequirements
from IPython import ( # type: ignore[import]
- embed,
+ start_ipython,
)
except ImportError: # pragma: no cover
ipython_available = False
@@ -4197,34 +4197,21 @@ class Cmd(cmd.Cmd):
:return: True if running of commands should stop
"""
+ # noinspection PyPackageRequirements
+ from IPython.terminal.interactiveshell import (
+ TerminalInteractiveShell,
+ )
+ from IPython.terminal.ipapp import (
+ TerminalIPythonApp,
+ )
+ from traitlets.config.loader import (
+ Config as TraitletsConfig,
+ )
+
from .py_bridge import (
PyBridge,
)
- def load_ipy(ipy_locals: Dict[str, Any]) -> None:
- """
- Embed an IPython shell in an environment that is restricted to only the variables in this function
-
- :param ipy_locals: locals dictionary for the IPython environment
- """
- # Copy ipy_locals into this function's locals
- for key, val in ipy_locals.items():
- locals()[key] = val
-
- # Delete these names from the environment so IPython won't see them
- del key
- del val
- del ipy_locals
-
- # Start ipy shell
- embed(
- banner1=(
- 'Entering an embedded IPython shell. Type quit or <Ctrl>-d to exit.\n'
- 'Run Python code from external files with: run filename.py\n'
- ),
- exit_msg='Leaving IPython, back to {}'.format(sys.argv[0]),
- )
-
if self.in_pyscript():
self.perror("Recursively entering interactive Python shells is not allowed")
return
@@ -4241,7 +4228,23 @@ class Cmd(cmd.Cmd):
if self.self_in_py:
local_vars['self'] = self
- load_ipy(local_vars)
+ # Configure IPython
+ config = TraitletsConfig()
+ config.InteractiveShell.banner2 = (
+ 'Entering an embedded IPython shell. Type quit or <Ctrl>-d to exit.\n'
+ 'Run Python code from external files with: run filename.py\n'
+ )
+
+ # Start IPython
+ start_ipython(config=config, argv=[], user_ns=local_vars)
+
+ # The IPython application is a singleton and won't be recreated next time
+ # this function runs. That's a problem since the contents of local_vars
+ # may need to be changed. Therefore we must destroy all instances of the
+ # relevant classes.
+ TerminalIPythonApp.clear_instance()
+ TerminalInteractiveShell.clear_instance()
+
return py_bridge.stop
finally:
self._in_py = False
diff --git a/docs/features/embedded_python_shells.rst b/docs/features/embedded_python_shells.rst
index 71fb8197..cbedf992 100644
--- a/docs/features/embedded_python_shells.rst
+++ b/docs/features/embedded_python_shells.rst
@@ -12,6 +12,9 @@ You may optionally enable full access to to your application by setting
python session, which is a reference to your ``cmd2`` application. This can be
useful for debugging your application.
+Any local or global variable created within the Python session will not persist
+in the CLI's environment.
+
Anything in ``self.py_locals`` is always available in the Python environment.
The ``app`` object (or your custom name) provides access to application
@@ -48,14 +51,6 @@ More Python examples:
>>> quit()
Python was here >
-Using the ``py`` command is tightly integrated with your main ``cmd2``
-application and any variables created or changed will persist for the life of
-the application::
-
- (Cmd) py x = 5
- (Cmd) py print(x)
- 5
-
The ``py`` command also allows you to run Python scripts via ``py
run('myscript.py')``. This provides a more complicated and more powerful
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
interactive Python session, this shell can access your application instance via
``self`` if ``self.self_in_py`` is ``True`` and any changes to your application
made via ``self`` will persist. However, any local or global variable created
-within the ``ipy`` shell will not persist.
+within the ``ipy`` shell will not persist in the CLI's environment
Also, as in the interactive Python session, the ``ipy`` shell has access to the
contents of ``self.py_locals`` and can call back into the application using the