<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Eric Rawn</title><link href="https://jupyter.org/medium-archive/pelican/" rel="alternate"/><link href="https://jupyter.org/medium-archive/pelican/feeds/author-eric-rawn.atom.xml" rel="self"/><id>https://jupyter.org/medium-archive/pelican/</id><updated>2026-09-22T00:37:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>Notebooks-On-Demand (Nod): A JupyterLab Extension for a Notebook Anywhere</title><link href="https://jupyter.org/medium-archive/pelican/posts/2026/notebooks-on-demand-nod-a-jupyterlab-extension-for-a/" rel="alternate"/><published>2026-09-22T00:37:00+00:00</published><updated>2026-09-22T00:37:00+00:00</updated><author><name>Eric Rawn</name></author><id>tag:jupyter.org,2026-09-22:/medium-archive/pelican/posts/2026/notebooks-on-demand-nod-a-jupyterlab-extension-for-a/</id><summary type="html">&lt;p&gt;Nod: Notebooks-on-Demand is an open-source JupyterLab extension that lets you jump into a Jupyter notebook from anywhere in a Python…&lt;/p&gt;
</summary><content type="html">&lt;figure&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/xgsp8h8bvfI" title="notebooks on demand demo" width="560" height="315" style="aspect-ratio: 560 / 315" loading="lazy" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;
&lt;figcaption&gt;
&lt;p&gt;A tour of Notebooks-On-Demand&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href="https://github.com/erawn/nod"&gt;&lt;em&gt;Nod: Notebooks-on-Demand&lt;/em&gt;&lt;/a&gt; is an open-source JupyterLab extension that lets you jump into a Jupyter notebook from anywhere in a Python codebase. Just call &lt;strong&gt;notebook()&lt;/strong&gt; from anywhere in your &lt;strong&gt;.py&lt;/strong&gt; file, run it with &lt;strong&gt;nod&lt;/strong&gt;, and a notebook opens at that point in the program with all your variables already loaded into the notebook state. When you’re done making edits, send them back to your original Python file with a click.&lt;/p&gt;
&lt;p&gt;Here’s what it looks like in practice. Say you have this Python program:&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;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;nodpy&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;,&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;:
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;notebook&lt;/span&gt;&lt;span class="ss"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;#&lt;span class="nv"&gt;our&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;pause&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def d(a):
  b = 2
  f(a,b)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;d(4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All you need to do is execute your program with &lt;strong&gt;nod&lt;/strong&gt; at the start:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;nod&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;myfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and a new JupyterLab session will open which looks like this:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Left: The Nod Control Panel shows the call stack for our notebook() call, Center: The notebook created from the current Nod Session, Right: The JupyterLab Debugger shows the current variables in our kernel" src="https://jupyter.org/medium-archive/pelican/posts/2026/notebooks-on-demand-nod-a-jupyterlab-extension-for-a/images/001-1_aepH_Tg-Y9filKEEllMe4g.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Left: The Nod Control Panel shows the call stack for our notebook() call, Center: The notebook created from the current Nod Session, Right: The JupyterLab Debugger shows the current variables in our kernel&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You’ll notice a couple of things. First, on the right-hand pane, you’ll see that the current notebook state matches the variables available in the Python program at the place &lt;strong&gt;notebook()&lt;/strong&gt; was called. Second, the body of the current function &lt;strong&gt;f&lt;/strong&gt; is editable, and the surrounding code is displayed for context.&lt;/p&gt;
&lt;p&gt;From here, you can use your on-demand notebook to write and debug the function &lt;strong&gt;f&lt;/strong&gt;. When you’re done, you can send your new code back to the original source file with a button at the top of the Nod Panel on the left (see the &lt;a href="https://github.com/erawn/nod"&gt;repository&lt;/a&gt; for detailed instructions). Whatever you wrote in your on-demand notebook will replace the current body of &lt;strong&gt;f&lt;/strong&gt; in the source file. The notebook cells will be converted to newlines, and markdown cells are converted to comments (thanks to &lt;a href="https://github.com/jupytext/jupytext"&gt;Jupytext&lt;/a&gt;!). We can also re-run our program from the beginning with these new changes in our source file through the Nod panel.&lt;/p&gt;
&lt;p&gt;Here’s a few scenarios that Nod might be helpful:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Scenario 1: You’re working in a large Python project and code is spread over countless files, classes, and function calls. You begin writing a new function, but the arguments are complex and questions abound: &lt;em&gt;What exactly does that class method return&lt;/em&gt;? &lt;em&gt;When I join these two tables, what does the output look like? How do I even construct the inputs to this function?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;You think: &lt;em&gt;This would be so much easier if I could just write this function in a notebook!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Instead of manually copying out code snippets, writing data structures to files and reading them into a notebook, and perhaps getting something to work eventually,&lt;/em&gt; you can just call &lt;strong&gt;notebook(),&lt;/strong&gt; &lt;em&gt;and a Jupyter notebook appears with just the body of your function and the function’s arguments already in your notebook state.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Scenario 2: &lt;em&gt;You realize there’s a bug in a specific function when called with specific arguments. You could use a&lt;/em&gt; &lt;em&gt;&lt;strong&gt;breakpoint()&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;to inspect the program, but you don’t just want to look at the arguments, you want to see if different ways of changing the arguments remove the bug when you run the function code. Instead, you call&lt;/em&gt; &lt;em&gt;&lt;strong&gt;notebook()&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;when the function is invoked with the buggy arguments, opening a Jupyter notebook on the function definition with those arguments loaded in the notebook state. You can modify the arguments and rerun your code in the notebook to test different outcomes and implement your fix. When you’re done, you can send the changes back to your source file and restart (opening a notebook-on-demand for your new program), verifying that your program is now running as expected.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Scenario 3: You use an AI system to generate some code, but there’s a crucial part you want to really understand. Just call &lt;strong&gt;notebook()&lt;/strong&gt; at that part, and now you have the full functionality of a notebook to walk line-by-line through the code, printing and visualizing the output at each step or experimenting with alternative decisions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nod’s on-demand notebooks are &lt;em&gt;&lt;strong&gt;designed to be disposable&lt;/strong&gt;&lt;/em&gt;. After you’ve sent your changes back to your source file, all the information to recreate the notebook is already in your Python file, so you can safely quit the JupyterLab session and continue programming in your text editor of choice. Your code can live in your text editor and Python source files, but Nod makes a quick trip to a notebook easy.&lt;/p&gt;
&lt;p&gt;While I’ve found this workflow by itself to be really helpful in my own programming, Nod can do two other things that are pretty neat:&lt;/p&gt;
&lt;p&gt;First, you don’t just have to stay in the function body where you called &lt;strong&gt;notebook()&lt;/strong&gt;; you can also navigate up and down the call stack with the Nod panel on the left. For example, you can select the function &lt;strong&gt;d&lt;/strong&gt; to see how &lt;strong&gt;f&lt;/strong&gt; was called:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The Nod Panel on the Left navigates down the callstack, replacing the current notebook and the state to match." src="https://jupyter.org/medium-archive/pelican/posts/2026/notebooks-on-demand-nod-a-jupyterlab-extension-for-a/images/002-1_MB1vHj5UbqOnMG8FxjXLeQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Nod Panel on the Left navigates down the callstack, replacing the current notebook and the state to match.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Both our notebook and the current variables update to our new position in our program (&lt;strong&gt;x&lt;/strong&gt; isn’t in our debugger pane on the right anymore, since it’s not available within function &lt;strong&gt;d&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;Second, you can use &lt;strong&gt;nodLog()&lt;/strong&gt; to collect a bunch of values from your program to poke and prod in an on-demand notebook. Take a look at the following file:&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;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;nodpy&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodLog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;:
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;:
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;notebook&lt;/span&gt;&lt;span class="ss"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;:
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;nodLog&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;f(2, [1, 2, 3])
f(3, {&amp;quot;s&amp;quot;: 1})
f(4, 1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running it will pop open a JupyterLab interface where, on the right, you’ll have access to a “Nod Log” pane, which lets you select assignments to &lt;strong&gt;a&lt;/strong&gt; and &lt;strong&gt;b&lt;/strong&gt; for the on-demand notebook session among the values you saved with &lt;strong&gt;nodLog():&lt;/strong&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="nodLog() captures variables while your program runs and makes them available in the on-demand notebook session to experiment with." src="https://jupyter.org/medium-archive/pelican/posts/2026/notebooks-on-demand-nod-a-jupyterlab-extension-for-a/images/003-1_6GAT730KwNVY8NJDM2dOLw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;nodLog() captures variables while your program runs and makes them available in the on-demand notebook session to experiment with.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When working with Python objects which can vary in unpredictable ways (like the results of a JSON API call or some other heavily-nested Python dictionary), I find &lt;strong&gt;nodLog()&lt;/strong&gt; to be helpful in gauging the possible values I need to handle when writing a new function. It lets me inspect and switch between different inputs to my function easily.&lt;/p&gt;
&lt;p&gt;Nod is free and open-source, so &lt;a href="https://github.com/erawn/nod"&gt;check it out here&lt;/a&gt; if you’re interested. Nod also works with JupyterHub with a slightly different workflow (see the repository for a guide!). My goal with Nod is to add to the situations where Jupyter Notebooks are a useful tool. If you have ideas about more ways Nod can be helpful or feedback to make it better, please leave an issue on the Github page and we can discuss!&lt;/p&gt;
</content><category term="debugging"/></entry></feed>