From 622ac6fa542028881332495fd8c35aa69fda7b06 Mon Sep 17 00:00:00 2001 From: seem Date: Mon, 4 Jul 2022 16:40:36 +1000 Subject: [PATCH] fix #9 --- execnb/_modidx.py | 1 + execnb/shell.py | 17 ++++++------ nbs/02_shell.ipynb | 65 ++++++++++++++++++++++++++++++---------------- nbs/sidebar.yml | 4 +-- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/execnb/_modidx.py b/execnb/_modidx.py index 4e857f2..0f060ab 100644 --- a/execnb/_modidx.py +++ b/execnb/_modidx.py @@ -55,6 +55,7 @@ 'execnb.shell': { 'execnb.shell.CaptureShell': 'https://fastai.github.io/execnb/shell#CaptureShell', 'execnb.shell.CaptureShell.cell': 'https://fastai.github.io/execnb/shell#CaptureShell.cell', 'execnb.shell.CaptureShell.enable_gui': 'https://fastai.github.io/execnb/shell#CaptureShell.enable_gui', + 'execnb.shell.CaptureShell.enable_matplotlib': 'https://fastai.github.io/execnb/shell#CaptureShell.enable_matplotlib', 'execnb.shell.CaptureShell.execute': 'https://fastai.github.io/execnb/shell#CaptureShell.execute', 'execnb.shell.CaptureShell.prettytb': 'https://fastai.github.io/execnb/shell#CaptureShell.prettytb', 'execnb.shell.CaptureShell.run': 'https://fastai.github.io/execnb/shell#CaptureShell.run', diff --git a/execnb/shell.py b/execnb/shell.py index c819a7a..7563445 100644 --- a/execnb/shell.py +++ b/execnb/shell.py @@ -40,12 +40,6 @@ def _out_exc(ename, evalue, traceback): return dict(ename=str(ename), evalue=str def _out_stream(text): return dict(name='stdout', output_type='stream', text=text.splitlines(False)) # %% ../nbs/02_shell.ipynb 7 -_CODE_MATPLOTLIB_INLINE = """ -from IPython import get_ipython -try: get_ipython().run_line_magic('matplotlib', 'inline') -except ModuleNotFoundError: pass -""".strip() - class CaptureShell(FastInteractiveShell): "Execute the IPython/Jupyter source code" def __init__(self, @@ -54,7 +48,8 @@ def __init__(self, InteractiveShell._instance = self self.out,self.count = [],1 self.exc = self.result = self._fname = self._cell_idx = None - self.run_cell(_CODE_MATPLOTLIB_INLINE) + try: self.enable_matplotlib('inline') + except ModuleNotFoundError: pass if path: self.set_path(path) def set_path(self, path): @@ -67,6 +62,12 @@ def enable_gui(self, gui=None): "Disable GUI (over-ridden; called by IPython)" pass + def enable_matplotlib(self, gui=None): + "Enable `matplotlib` in a nested shell" + from matplotlib_inline.backend_inline import configure_inline_support + configure_inline_support.current_backend = 'unset' + return super().enable_matplotlib(gui) + def _showtraceback(self, etype, evalue, stb: str): self.out.append(_out_exc(etype, evalue, stb)) self.exc = (etype, evalue, '\n'.join(stb)) @@ -169,7 +170,7 @@ def prettytb(self:CaptureShell, fname_str = f' in {fname}' if fname else '' return f"{type(self.exc[1]).__name__}{fname_str}:\n{_fence}\n{cell_str}\n" -# %% ../nbs/02_shell.ipynb 50 +# %% ../nbs/02_shell.ipynb 52 @call_parse def exec_nb( src:str, # Notebook path to read from diff --git a/nbs/02_shell.ipynb b/nbs/02_shell.ipynb index 550f297..ba99c50 100644 --- a/nbs/02_shell.ipynb +++ b/nbs/02_shell.ipynb @@ -90,12 +90,6 @@ "outputs": [], "source": [ "#|export\n", - "_CODE_MATPLOTLIB_INLINE = \"\"\"\n", - "from IPython import get_ipython\n", - "try: get_ipython().run_line_magic('matplotlib', 'inline')\n", - "except ModuleNotFoundError: pass\n", - "\"\"\".strip()\n", - "\n", "class CaptureShell(FastInteractiveShell):\n", " \"Execute the IPython/Jupyter source code\"\n", " def __init__(self,\n", @@ -104,7 +98,8 @@ " InteractiveShell._instance = self\n", " self.out,self.count = [],1\n", " self.exc = self.result = self._fname = self._cell_idx = None\n", - " self.run_cell(_CODE_MATPLOTLIB_INLINE)\n", + " try: self.enable_matplotlib('inline')\n", + " except ModuleNotFoundError: pass\n", " if path: self.set_path(path)\n", " \n", " def set_path(self, path):\n", @@ -117,6 +112,12 @@ " \"Disable GUI (over-ridden; called by IPython)\"\n", " pass\n", " \n", + " def enable_matplotlib(self, gui=None):\n", + " \"Enable `matplotlib` in a nested shell\"\n", + " from matplotlib_inline.backend_inline import configure_inline_support\n", + " configure_inline_support.current_backend = 'unset'\n", + " return super().enable_matplotlib(gui)\n", + " \n", " def _showtraceback(self, etype, evalue, stb: str):\n", " self.out.append(_out_exc(etype, evalue, stb))\n", " self.exc = (etype, evalue, '\\n'.join(stb))\n", @@ -219,8 +220,8 @@ " 'execution_count': 1},\n", " {'name': 'stdout',\n", " 'output_type': 'stream',\n", - " 'text': ['CPU times: user 1e+03 ns, sys: 0 ns, total: 1e+03 ns',\n", - " 'Wall time: 2.62 us']}]" + " 'text': ['CPU times: user 3 us, sys: 1 us, total: 4 us',\n", + " 'Wall time: 6.2 us']}]" ] }, "execution_count": null, @@ -299,9 +300,9 @@ "text": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", "\u001b[0;31mException\u001b[0m Traceback (most recent call last)\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n", - "\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Oops\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0m\n", + "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n", + "\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mOops\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "\n", "\u001b[0;31mException\u001b[0m: Oops\n" ] } @@ -513,7 +514,7 @@ " 'output_type': 'error',\n", " 'traceback': ['\\x1b[0;31m---------------------------------------------------------------------------\\x1b[0m',\n", " '\\x1b[0;31mException\\x1b[0m Traceback (most recent call last)',\n", - " '\\x1b[0;32m\\x1b[0m in \\x1b[0;36m\\x1b[0;34m\\x1b[0m\\n\\x1b[0;32m----> 1\\x1b[0;31m \\x1b[0;32mraise\\x1b[0m \\x1b[0mException\\x1b[0m\\x1b[0;34m(\\x1b[0m\\x1b[0;34m\"Oopsie!\"\\x1b[0m\\x1b[0;34m)\\x1b[0m\\x1b[0;34m\\x1b[0m\\x1b[0;34m\\x1b[0m\\x1b[0m\\n\\x1b[0m',\n", + " 'Input \\x1b[0;32mIn [1]\\x1b[0m, in \\x1b[0;36m\\x1b[0;34m()\\x1b[0m\\n\\x1b[0;32m----> 1\\x1b[0m \\x1b[38;5;28;01mraise\\x1b[39;00m \\x1b[38;5;167;01mException\\x1b[39;00m(\\x1b[38;5;124m\"\\x1b[39m\\x1b[38;5;124mOopsie!\\x1b[39m\\x1b[38;5;124m\"\\x1b[39m)\\n',\n", " '\\x1b[0;31mException\\x1b[0m: Oopsie!']}]" ] }, @@ -723,15 +724,15 @@ "While Executing Cell #2:\n", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n", - "\u001b[1;32m 1\u001b[0m \u001b[0;31m# some comments\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'hello'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m----> 3\u001b[0;31m \u001b[0mfoo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0;32m~/github/execnb/tests/err.py\u001b[0m in \u001b[0;36mfoo\u001b[0;34m()\u001b[0m\n", - "\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mfoo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0;36m13\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m98\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0m\n", + "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n", + "\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# some comments\u001b[39;00m\n", + "\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhello\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[0;32m----> 3\u001b[0m \u001b[43mfoo\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\n", + "File \u001b[0;32m~/code/execnb/tests/err.py:2\u001b[0m, in \u001b[0;36mfoo\u001b[0;34m()\u001b[0m\n", + "\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mfoo\u001b[39m():\n", + "\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;241m13\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m98\u001b[39m\n", + "\n", "\u001b[0;31mAssertionError\u001b[0m: \n", "\n" ] @@ -745,6 +746,26 @@ " print(s.prettytb())" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tests -" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# matplotlib images are captured when outer shell uses inline backend\n", + "import matplotlib\n", + "matplotlib.use('module://matplotlib_inline.backend_inline')\n", + "res = CaptureShell().run('import matplotlib.pyplot as plt; plt.plot([0,1]);')\n", + "assert any('image/png' in o['data'] for o in res),'Image not captured'" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/nbs/sidebar.yml b/nbs/sidebar.yml index 3dee214..f143324 100644 --- a/nbs/sidebar.yml +++ b/nbs/sidebar.yml @@ -1,6 +1,6 @@ website: sidebar: contents: + - index.ipynb - 01_nbio.ipynb - - 02_shell.ipynb - - index.ipynb \ No newline at end of file + - 02_shell.ipynb \ No newline at end of file