<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - David Brochart</title><link href="https://jupyter.org/blog/" rel="alternate"/><link href="https://jupyter.org/blog/feeds/author-david-brochart.atom.xml" rel="self"/><id>https://jupyter.org/blog/</id><updated>2023-10-23T06:09:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>Plug your application into the Jupyter world</title><link href="https://jupyter.org/blog/posts/2023/plug-your-application-into-the-jupyter-world/" rel="alternate"/><published>2023-10-23T06:09:00+00:00</published><updated>2023-10-23T06:09:00+00:00</updated><author><name>David Brochart</name></author><id>tag:jupyter.org,2023-10-23:/blog/posts/2023/plug-your-application-into-the-jupyter-world/</id><summary type="html">&lt;p&gt;Kernels are a simple but powerful abstraction in the Jupyter architecture. They encapsulate language interpreters and make them accessible through a standardized interface. This is the key to Jupyter’s remarkable versatility, with over 100 supported languages.&lt;/p&gt;</summary><content type="html">&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2023/plug-your-application-into-the-jupyter-world/images/001-1_ENzq7i52UtqxESs4UTKBYw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Kernels are a simple but powerful abstraction in the Jupyter architecture. They encapsulate language interpreters and make them accessible through a standardized interface. This is the key to Jupyter’s remarkable versatility, with over 100 supported languages. Embedding a kernel in your custom application can seamlessly expose it to the Jupyter ecosystem. Not only can you attach JupyterLab to your program for e.g. state inspection, as you would do with a debugger, but you can truly extend your application with all the power of the Jupyter ecosystem.&lt;/p&gt;
&lt;h2 id="applications-gui-and-api"&gt;Applications: GUI and API&lt;/h2&gt;
&lt;p&gt;Several applications such as &lt;a href="https://www.freecad.org/"&gt;FreeCAD&lt;/a&gt;, &lt;a href="https://docs.blender.org/api/current/"&gt;Blender&lt;/a&gt;, or &lt;a href="https://docs.qgis.org/3.28/en/docs/pyqgis_developer_cookbook/index.html"&gt;QGIS&lt;/a&gt; provide a Python API to access their functionalities programmatically, and include an embedded Python console which allows using this API from within the application. Users can both use the graphical user interface and operate on the application in a scripted fashion depending on their needs.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Python consoles in FreeCAD, Blender, and QGIS (from left to right)." src="https://jupyter.org/blog/posts/2023/plug-your-application-into-the-jupyter-world/images/002-1_EMYG_XFNokXREwsUa9pbFA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Python consoles in FreeCAD, Blender, and QGIS (from left to right).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you think about it, these are the kinds of workflows that Jupyter has always been about, but Jupyter has evolved in the opposite direction. Instead of adding scripting to GUIs, Jupyter added GUIs to scripting. At first, Jupyter was IPython, a console for interactive computing. Then it went to the Web and became IPython Notebook, renamed to Jupyter Notebook. With the browser it became possible to display richer content and create small GUIs, using Jupyter Widgets. Then JupyterLab was created and allowed to create fully-fledged desktop-like applications in the browser, like 3-D CAD modelers.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterLab showing notebooks using JupyterCAD and ipyleaflet." src="https://jupyter.org/blog/posts/2023/plug-your-application-into-the-jupyter-world/images/003-1_m4WwlNYiIvMeZu1eXhrl7Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLab showing notebooks using JupyterCAD and ipyleaflet.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Jupyter put a lot of attention on building a good experience interacting with the Python REPL, which is not the main focus of domain-specific desktop applications. The Python console embedded in desktop applications is often quite raw. Allowing users to connect from Jupyter can enable a much better experience.&lt;/p&gt;
&lt;p&gt;What if there was a bridge that allowed you to connect both worlds, your application on one side and Jupyter on the other side? What is the common denominator allowing code to interact with an application, while also being shared with Jupyter? In the Jupyter infrastructure, we call it a kernel.&lt;/p&gt;
&lt;h2 id="jupyter-kernels"&gt;Jupyter kernels&lt;/h2&gt;
&lt;p&gt;Kernels provide a standardized way to expose language interpreters to applications, in a language-agnostic fashion. You can think of it as a server that accepts requests such as “execute this piece of code” and that replies with a result. The result is actually a data representation, it can be as simple as “3” for an input of “1+2”, or a complex interactive plot. A kernel usually lives in a separate process on your machine, but it can also run in the same process, or even remotely on another machine. Someone wanting to connect to this kernel will need the connection information such as the IP address and the ports of the different communication “channels”. It is stored in what is called a connection file.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The Jupyter kernel protocol allows clients to interact with different languages." src="https://jupyter.org/blog/posts/2023/plug-your-application-into-the-jupyter-world/images/004-1_ywYEVpCKlbL6aBEGERsXSw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Jupyter kernel protocol allows clients to interact with different languages.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Anyone who has access to this connection file can then execute code in the kernel. This is “shared code”, meaning that every client to this kernel eventually runs code in the same interpreter. If one client sets a variable “a=1”, other clients will see this variable. They are all running the “same program”. If the kernel runs in the same process as the application, then the kernel can interact directly with it. This way of sharing the execution engine serves as a bridge between the application and Jupyter.&lt;/p&gt;
&lt;p&gt;Now it is just a matter of exposing the connection file so that e.g. JupyterLab can see it. The application launching the kernel can save the connection file to a specific directory, and give the path to JupyterLab. And that’s it, JupyterLab can run a notebook using this kernel, allowing it to interact with the application.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/jupyter-xeus/xeus-qt-python"&gt;xeus-qt-python&lt;/a&gt; repository is a great place to start and see how to embed a Python kernel inside a Qt application. Under the hood, it uses &lt;a href="https://github.com/jupyter-xeus/xeus-qt"&gt;xeus-qt&lt;/a&gt;, an integration of the &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt; kernel architecture in the Qt event loop. The Qt application actually runs JupyterLab in a Qt WebEngine window. Users can run code in a notebook and interact with the application, which in turn can interact with the notebook. Here, we create a Qt button from a notebook. When clicking on the button, a callback prints a message in the notebook. This demonstrates a two-way binding between the Qt application and JupyterLab.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Example of a Qt application embedding JupyterLab." src="https://jupyter.org/blog/posts/2023/plug-your-application-into-the-jupyter-world/images/005-1_FTlpril_YV5W6-xYelNrtA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Example of a Qt application embedding JupyterLab.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href="https://github.com/Slicer/SlicerJupyter"&gt;SlicerJupyter&lt;/a&gt; from Kitware was one of the first applications to take advantage of this approach. You can read more in this &lt;a href="https://jupyter.org/blog/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/"&gt;blog post&lt;/a&gt;. We hope that this technology will unlock a new brand of use-cases and allow applications to reach a new audience through the Jupyter ecosystem. Please reach out to us if you need help!&lt;/p&gt;
&lt;h3 id="about-the-author"&gt;About the author&lt;/h3&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2023/plug-your-application-into-the-jupyter-world/images/006-1_gGWqElhixsQA-cGOzWcb7w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/davidbrochart"&gt;&lt;strong&gt;David Brochart&lt;/strong&gt;&lt;/a&gt; is a technical director at QuantStack. He mostly works on the Jupyter backend stack. He contributed to the development of real-time collaboration in JupyterLab, and created the &lt;a href="https://davidbrochart.github.io/jupyverse/"&gt;Jupyverse&lt;/a&gt; project, a next-generation implementation of the Jupyter server.&lt;/p&gt;
&lt;h3 id="acknowledgments"&gt;Acknowledgments&lt;/h3&gt;
&lt;p&gt;This work was made possible thanks to several contributors. &lt;a href="https://twitter.com/mari_meir"&gt;&lt;strong&gt;Mariana Meireles&lt;/strong&gt;&lt;/a&gt; was the initial author of &lt;a href="https://github.com/jupyter-xeus/xeus-qt"&gt;xeus-qt&lt;/a&gt;. &lt;a href="https://twitter.com/ThorstenBeier"&gt;&lt;strong&gt;Thorsten Beier&lt;/strong&gt;&lt;/a&gt; is a scientific software developer at QuantStack and contributes to the &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt; kernel ecosystem. He is the initial creator of the &lt;a href="https://github.com/jupyter-xeus/xeus-qt-python"&gt;xeus-qt-python&lt;/a&gt; demonstration. &lt;a href="https://twitter.com/JohanMabille"&gt;&lt;strong&gt;Johan Mabille&lt;/strong&gt;&lt;/a&gt; is a technical director at QuantStack and he is the main author of &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt;. &lt;strong&gt;Afshin Darian&lt;/strong&gt; is a technical director at QuantStack and worked on the UI in JupyterLab 4.0 allowing to use an external kernel.&lt;/p&gt;
</content><category term="kernels"/></entry><entry><title>Accelerating JupyterLab</title><link href="https://jupyter.org/blog/posts/2022/accelerating-jupyterlab/" rel="alternate"/><published>2022-10-10T17:38:00+00:00</published><updated>2022-10-10T17:38:00+00:00</updated><author><name>Frédéric Collonval</name></author><id>tag:jupyter.org,2022-10-10:/blog/posts/2022/accelerating-jupyterlab/</id><summary type="html">&lt;p&gt;How JupyterLab is switching to second gear for Version 4&lt;/p&gt;
</summary><content type="html">&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2022/accelerating-jupyterlab/images/001-1_EZX55-XLmck_LfFfV3KeaA.webp" alt="Illustration of an astronaut flying in space with a jet pack." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The next major release of JupyterLab will be significantly faster than previous versions. This was achieved both through systematic tracking of performance bugs and through significant upgrades to the Jupyter communication protocol and rendering mechanism for documents.&lt;/p&gt;
&lt;h2 id="1-setting-up-rigorous-performance-measurements"&gt;1. Setting up rigorous performance measurements&lt;/h2&gt;
&lt;p&gt;The first step to any measurable improvement in performance is to set up systematic measurement of performance.&lt;/p&gt;
&lt;p&gt;The JupyterLab project now includes a UI performance benchmarking tool, in the form of a GitHub action that can be triggered on any pull request to check how performance is impacted by the change. The implementation of this new GitHub action is available in this repository: &lt;a href="https://github.com/jupyterlab/benchmarks"&gt;https://github.com/jupyterlab/benchmarks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This tool measures the time required for performing the following actions: opening a test notebook, switching from the test notebook to a copy of it opened in another tab, switching from the test notebook to a text editor, switching back, searching for a word in the test notebook and closing the test notebook. There are multiple example notebooks in the test suites. Benchmark results are posted as comments on the pull request. You can see such a benchmark report here: &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/11494#issuecomment-976393815"&gt;#11494#issuecomment-976393815&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Example report from the new benchmarking tool" src="https://jupyter.org/blog/posts/2022/accelerating-jupyterlab/images/002-0_pEgLswpTd_LWmMsi.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Example report from the new benchmarking tool — each execution time distribution is represented by a box-plot graph (the box spans from the 1st to the 3rd quartiles with the white line positioned at the median value).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The addition of this benchmarking tool immediately allowed for optimization on how notebooks are &lt;em&gt;hidden&lt;/em&gt; when switching tabs. Hiding can be done by adding a CSS class that enables some CSS rule, or forcibly setting display to “none”. Depending on the browser, picking one way or another of hiding content may trigger a reflow of the entire page, so we made this a settable with an option in the JupyterLab config.&lt;/p&gt;
&lt;p&gt;The benchmark GitHub action was developed by &lt;strong&gt;Frédéric Collonval&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="2-upgrading-to-codemirror-6"&gt;2. Upgrading to CodeMirror 6&lt;/h2&gt;
&lt;p&gt;The rendering of the text editor used in notebooks can be very expensive, especially in the case of large notebooks with many cells. Jupyter has historically relied on CodeMirror as its based text editor.&lt;/p&gt;
&lt;p&gt;JupyterLab 4 includes an upgrade from CodeMirror 5 to CodeMirror 6, which is a complete rewrite of the text editor. This work can be found in pull requests &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/11638"&gt;#11638&lt;/a&gt;, &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/12877"&gt;#12877&lt;/a&gt;, and &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/12861"&gt;#12861&lt;/a&gt; — modifying over 150 files of the JupyterLab codebase. Benchmarks indicate a rendering speedup factor between 2 and 3 on the large notebooks used in the benchmarking suite.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Benchmark report on the CodeMirror 6 migration PR" src="https://jupyter.org/blog/posts/2022/accelerating-jupyterlab/images/003-0_68j3WUTV1hUtR4wC.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Benchmark report on the CodeMirror 6 migration PR&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: CodeMirror 6 is also an important stepping stone towards making Jupyter notebooks &lt;em&gt;&lt;strong&gt;accessible&lt;/strong&gt;&lt;/em&gt; to people who need screen readers and other devices.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The migration of JupyterLab to CodeMirror 6 was performed by &lt;strong&gt;Johan Mabille&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="3-virtual-rendering-of-notebooks"&gt;3. Virtual rendering of notebooks&lt;/h2&gt;
&lt;p&gt;In JupyterLab 4, the notebook will only render the parts of the documents that are visible in the viewport. It significantly improves the rendering speed of large notebooks. The main pull request implementing this feature is available here: &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/12554"&gt;#12554&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Significant preparation work was required for this Pull Request, especially regarding the “search feature” and the “table of content” components that both made use of the notebook view instead of the document model. This was done in PRs &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/11689"&gt;#11689&lt;/a&gt; and &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/12374"&gt;#12374&lt;/a&gt; respectively.&lt;/p&gt;
&lt;p&gt;The end results showed significant improvement in the rendering speed of large notebook files, with a speedup of 3 to 4, which come on top of the already improved performance from the CodeMirror 6 migration.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Benchmark report on the Virtual Rendering of notebooks" src="https://jupyter.org/blog/posts/2022/accelerating-jupyterlab/images/004-0_y7zl2IRtKoeyYp5e.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Benchmark report on the Virtual Rendering of notebooks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The virtual rendering of notebooks was developed by &lt;strong&gt;Frédéric Collonval&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="4-jupyter-protocol-alignment"&gt;4. Jupyter protocol alignment&lt;/h2&gt;
&lt;p&gt;The Jupyter server serves as a relay between the frontends such as JupyterLab or the notebook and kernels. The &lt;strong&gt;server ⇄ kernel&lt;/strong&gt; communication is done over ZeroMQ sockets, with the well-specified Jupyter kernel protocol. The &lt;strong&gt;server ⇄ client&lt;/strong&gt; communication is done over WebSockets.&lt;/p&gt;
&lt;p&gt;Unfortunately, up until recently, the &lt;strong&gt;server ⇄ kernel&lt;/strong&gt; (ZMQ), and the &lt;strong&gt;server ⇄ client&lt;/strong&gt; (WebSocket) protocols differed slightly so that the server had to parse each message and re-serialise it in both directions. This processing cost is small for short messages such as execution requests and replies which are typically very short, however, it can become very costly when dealing with larger datasets being sent or retrieved from the front-end, such as large tables, complex mime type rendering. This misalignment of the ZMQ and WebSocket protocol can then become a real bottleneck.&lt;/p&gt;
&lt;p&gt;In Jupyter Server 2, the WebSocket connection supports a new “aligned” protocol, in which messages can simply be copied over to and from ZeroMQ messages, which is supported by JupyterLab 4. (This work was done in PRs &lt;a href="https://github.com/jupyter-server/jupyter_server/pull/657"&gt;#657&lt;/a&gt; (jupyter-server), &lt;a href="https://github.com/jupyter-server/jupyverse/pull/154"&gt;#154&lt;/a&gt; (jupyverse), and &lt;a href="https://github.com/jupyterlab/jupyterlab/pull/11841"&gt;#11841&lt;/a&gt; (JupyterLab)). This new aligned protocol is an opt-in, so that legacy Jupyter front-end are still expected to function with Jupyter Server 2.&lt;/p&gt;
&lt;p&gt;Benchmarks indicate a &lt;strong&gt;large speedup factor&lt;/strong&gt; (at least one order of magnitude, and more for larger messages) in the performance of the Jupyter server when displaying large data sets in Jupyter widgets. However, this is not captured by the JupyterLab benchmark tests which focus on the rendering performances.&lt;/p&gt;
&lt;p&gt;The procol alignment work was done by &lt;strong&gt;David Brochart&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="5-lumino-2"&gt;5. Lumino 2&lt;/h2&gt;
&lt;p&gt;The JupyterLab frontend is built upon the Lumino framework, which provides utilities for building in-browser desktop-like applications. It provides the foundations for such applications, including a uniform component wrapper that handles lifecycle management and efficient propagation of front-end events to an entire application, (e.g., resize events, drag-and-drop, layout calculation). Lumino also provides several high-performance components such as a drag-and-drop dock panel (used as the application shell for JupyterLab) and a best-in-class data grid component.&lt;/p&gt;
&lt;p&gt;JupyterLab 4 includes a major upgrade of the Lumino. The main changes in Lumino 2 include the migration to ES2018, which allowed for the removal of large parts of the codebase prodiving features that are now natively available in JavaScript, such as native iterators, removing polyfills for promises, and special-case logic for idiosyncrasies of legacy browsers like IE. This upgrade is also leading to across-the-board performance improvements in the front-end, although not for the rendering of large documents. Lumino 2 supports background processing of UI components when the application resides in a background browser tab (a feature that may be back-ported to Lumino 1.x as well).&lt;/p&gt;
&lt;p&gt;The Lumino 2 upgrade and integration in JupyterLab was done by &lt;strong&gt;Afshin Darian.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="6-a-faster-lumino-data-grid"&gt;6. A faster Lumino data grid&lt;/h2&gt;
&lt;p&gt;Optimizations to the Lumino data grid widget were also implemented, speeding up the rendering in the case of merged cells (cf. PR &lt;a href="https://github.com/jupyterlab/lumino/pull/394"&gt;#394&lt;/a&gt;). The Lumino datagrid is used in various parts of the JupyterLab UI, such as the table view for CSV files. It is also used extensively in third-party extensions such as the &lt;a href="https://github.com/bloomberg/ipydatagrid"&gt;ipydatagrid&lt;/a&gt; Jupyter widget and the &lt;a href="https://github.com/twosigma/beakerx_tabledisplay"&gt;BeakerX table display&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Lumino data grid optimization was done by &lt;strong&gt;Martin Renou&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="acknowledgement"&gt;Acknowledgement&lt;/h2&gt;
&lt;p&gt;The work by the &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt; team on JupyterLab performance improvements was done in collaboration with &lt;a href="https://www.twosigma.com/"&gt;&lt;strong&gt;Two Sigma&lt;/strong&gt;&lt;/a&gt;. Several of these pull requests required major changes across the JupyterLab codebase. We are very grateful to Two Sigma for supporting the development of the Jupyter project at such a deep level.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2022/accelerating-jupyterlab/images/005-0_P3blJAk0ZNg4obBV.webp" alt="Two-sigma logo" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;We are grateful to &lt;a href="https://twitter.com/juliettetaka?lang=en"&gt;&lt;strong&gt;Juliette Taka&lt;/strong&gt;&lt;/a&gt; for the illustrations.&lt;/p&gt;
&lt;h2 id="about-the-authors"&gt;About the authors&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Frédéric Collonval&lt;/strong&gt;, who led the charge on JupyterLab performance improvements, is a technical director at QuantStack. He is a member of the core JupyterLab core team and authored several JupyterLab extensions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Johan Mabille&lt;/strong&gt; is a technical director at QuantStack, very active in the Jupyter ecosystem. He regularly contributes to JupyterLab, and developed the Xeus framework for creating Jupyter kernels.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;David Brochart&lt;/strong&gt; is a scientific software developer at QuantStack, very active in the Jupyter ecosystem. He is a maintainer of the Jupyter-server project, and the main author of Jupyverse. David also contributes to the geo-science open-source stack built atop Jupyter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Afshin Darian&lt;/strong&gt; is a technical director at QuantStack. He is the co-creator of the JupyterLab project and continues working on the project to this day.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2022/accelerating-jupyterlab/images/006-1_OwFstCVzAGZX3EiQejEWog.webp" alt="Illustration of an astronaut planting a Jupyter flag at the top of a mountain." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="JupyterLab"/></entry><entry><title>nbterm: Jupyter Notebooks in the terminal</title><link href="https://jupyter.org/blog/posts/2021/nbterm-jupyter-notebooks-in-the-terminal/" rel="alternate"/><published>2021-04-26T16:42:00+00:00</published><updated>2021-04-26T16:42:00+00:00</updated><author><name>David Brochart</name></author><id>tag:jupyter.org,2021-04-26:/blog/posts/2021/nbterm-jupyter-notebooks-in-the-terminal/</id><summary type="html">&lt;p&gt;Jupyter notebooks are mostly known for their web-based user interface, such as JupyterLab or the Classic Notebook. They offer a great user experience, allow for rich output rendering, provide interactivity through e.g. widgets, and make possible working with remote kernels.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Jupyter notebooks are mostly known for their web-based user interface, such as JupyterLab or the Classic Notebook. They offer a great user experience, allow for rich output rendering, provide interactivity through e.g. widgets, and make possible working with remote kernels.&lt;/p&gt;
&lt;p&gt;If just like me you tend to never leave the terminal in your workflow, you may be missing a tool to interact with Jupyter notebooks without switching to a web browser. While the &lt;a href="https://github.com/jupyter/jupyter_console"&gt;Jupyter Console&lt;/a&gt; already allows you to interact with a Jupyter kernel in the terminal, it is more comparable to a shell than a notebook.&lt;/p&gt;
&lt;p&gt;The notebook differs from the console in that it’s bound to a document, interleaving rich text and executable code. This makes the code representation more structured, while allowing for interactivity. Well, until recently this wasn’t possible in the terminal. But today, we are proud to announce &lt;strong&gt;nbterm&lt;/strong&gt;!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Side-by-side comparison of the JupyterLab web notebook and nbterm." src="https://jupyter.org/blog/posts/2021/nbterm-jupyter-notebooks-in-the-terminal/images/001-1_jZFC3kPJqxMSUeCGd7PHxw.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Side-by-side comparison of the JupyterLab web notebook and nbterm.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="getting-started-with-nbterm"&gt;Getting started with nbterm&lt;/h2&gt;
&lt;p&gt;Launching a notebook is as easy as entering:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;nbterm&lt;span class="w"&gt; &lt;/span&gt;my_notebook.ipynb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also create a new one if you don’t pass a notebook argument. Almost immediately, you will get a full-screen application that looks like this:&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2021/nbterm-jupyter-notebooks-in-the-terminal/images/002-1_s6BRdEgITBGOR0y1Ec7GSQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;nbterm has other command line options:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nbterm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;--help&lt;/span&gt;
&lt;span class="k"&gt;Usage&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nbterm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OPTIONS&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NOTEBOOK_PATH&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="nl"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NOTEBOOK_PATH&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notebook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;default: &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="nl"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;--no-kernel                     Don&amp;#39;t launch a kernel.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;--run                           Run the notebook.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;--save-path TEXT                Path to save the notebook.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;--version                       Show the version and exit.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;--help                          Show this message and exit.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For example, it is possible to run all cells of a notebook in batch mode, using:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;nbterm&lt;span class="w"&gt; &lt;/span&gt;--run&lt;span class="w"&gt; &lt;/span&gt;my_notebook.ipynb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you didn’t specify a new name with &lt;code&gt;--save-path&lt;/code&gt;, a new notebook named &lt;code&gt;my_notebook_run.ipynb&lt;/code&gt; will be created, with all cell outputs populated. This mode of execution is similar to the &lt;a href="https://github.com/jupyter/nbclient"&gt;nbclient&lt;/a&gt; library, but accessible in the command line.&lt;/p&gt;
&lt;h2 id="embedding-nbterm"&gt;Embedding nbterm&lt;/h2&gt;
&lt;p&gt;It is also possible to use it as a library and embed it in your own application. Besides, all user interactions can be scripted. For instance, you can rearrange the cells and then run all cells like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;nbterm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Notebook&lt;/span&gt;

&lt;span class="n"&gt;nb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Notebook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;my_notebook.ipynb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;nb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cut_cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;nb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paste_cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;nb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="a-lightweight-jupyter-stack"&gt;A lightweight Jupyter stack&lt;/h2&gt;
&lt;p&gt;While you probably can’t tell the difference, nbterm doesn’t reuse the base Jupyter components such as &lt;a href="https://github.com/jupyter/jupyter_client"&gt;jupyter-client&lt;/a&gt; and &lt;a href="https://github.com/jupyter/nbformat"&gt;nbformat&lt;/a&gt; (respectively implementing the Jupyter kernel protocol and the Jupyter Notebook format). The reason is that nbterm was started as a toy project to see how hard it would be to have a minimalist notebook client.&lt;/p&gt;
&lt;p&gt;As a Jupyter contributor, I am mostly involved in the maintenance and modernization of these packages. I wanted to experiment with a new project without the backward-compatibility constraints. This resulted in a rather lean code base, and its simplicity makes it suitable to serve as a reference for other language implementations.&lt;/p&gt;
&lt;h2 id="the-roadmap"&gt;The roadmap&lt;/h2&gt;
&lt;p&gt;While nbterm is obviously more limited than the Notebook, there are several potential improvement directions. Having rich(er) outputs is one of them. The terminal is pretty much limited to showing characters, but people have done great things with ASCII art. An attempt has been made a while ago with an &lt;a href="https://github.com/domitry/matascii"&gt;ASCII back-end for matplotlib&lt;/a&gt;. And although it’s only on macOS for now, some terminals are capable of showing real pictures, and there exists &lt;a href="https://github.com/daleroberts/itermplot"&gt;a Matplotlib back-end for iTerm2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another nice feature would be to add some interactivity. In the Notebook, this is typically achieved using widgets, but they are inherently web-based, so this is not an option. Maybe we could imagine something similar to &lt;a href="https://github.com/jupyter-widgets/ipywidgets"&gt;ipywidgets&lt;/a&gt; for the terminal, and have simple things like sliders, buttons, menus and such. I am keeping an eye on the awesome &lt;a href="https://github.com/willmcgugan/rich"&gt;Rich library&lt;/a&gt;, with which you can already create dashboards, and GUI applications in the future.&lt;/p&gt;
&lt;h2 id="try-it"&gt;Try it!&lt;/h2&gt;
&lt;p&gt;nbterm really has the look and feel of the Notebook you already know and love, with (almost) the same key bindings, so you won’t be lost. As coders, we love the terminal, and libraries like &lt;a href="https://github.com/prompt-toolkit/python-prompt-toolkit"&gt;prompt-toolkit&lt;/a&gt; and &lt;a href="https://github.com/willmcgugan/rich"&gt;Rich&lt;/a&gt; in Python, or &lt;a href="https://github.com/jupyter-xeus/cpp-terminal/"&gt;cpp-terminal&lt;/a&gt; in C++, make it possible to build beautiful applications. You can install nbterm today using your favorite package manager:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;nbterm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;mamba&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;nbterm&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Be sure to update frequently, as it is improving every day. And if you want to contribute, please get in touch on the &lt;a href="https://github.com/davidbrochart/nbterm"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;The development of nbterm is sponsored by &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://twitter.com/davidbrochart"&gt;David Brochart&lt;/a&gt; is a scientific software developer at QuantStack. He is a contributor to several libraries in the Jupyter ecosystem, such as &lt;a href="https://github.com/jupyter/nbclient"&gt;nbclient&lt;/a&gt;, &lt;a href="https://github.com/jupyter/jupyter_client"&gt;jupyter-client&lt;/a&gt;, &lt;a href="https://github.com/jupyter-server/jupyter_server"&gt;jupyter-server&lt;/a&gt;, &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt;, and &lt;a href="https://github.com/jupyter-widgets/ipywidgets"&gt;ipywidgets&lt;/a&gt;.&lt;/p&gt;
</content><category term="Jupyter Notebook"/></entry></feed>