jupyter / nbclient

A client library for executing notebooks. Formally nbconvert's ExecutePreprocessor
https://nbclient.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
146 stars 56 forks source link

when cell contains ipywidget , execute cell will sometimes blocks #312

Open hjliu0206 opened 1 month ago

hjliu0206 commented 1 month ago

package version

nbformat                  5.10.4
nbclient                     0.10.0

here is the code which can reproduce the bug

import nbformat
from nbclient import NotebookClient
def execute_ipynb(ipynb_file):
    with open(ipynb_file, 'r', encoding='utf-8') as f:
        nb_node = nbformat.read(f, as_version=4)
    client = NotebookClient(nb_node)

    try:
        with client.setup_kernel():
            info_msg = client.wait_for_reply(client.kc.kernel_info())
            for index, cell in enumerate(nb_node.cells):
                client.execute_cell(cell, index)
    except Exception as e:
        print("An error occurred during notebook execution:{}", e)
ipynb_file = 'test.ipynb'
execute_ipynb(ipynb_file)

content of test.ipynb

{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "655816ee-ac2e-44d0-8b1f-4d2686ef0942",
   "metadata": {},
   "outputs": [],
   "source": [
    "import ipywidgets as widgets\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import IPython\n",
    "\n",
    "%matplotlib widget\n",
    "\n",
    "sr = 44100\n",
    "duration = 1.0\n",
    "\n",
    "with plt.ioff():\n",
    "    fig = plt.figure(figsize=(14, 7))\n",
    "\n",
    "@widgets.interact(\n",
    "    freq=widgets.FloatLogSlider(base=np.e, min=np.log(20), max=np.log(20000), step=0.001, value=440),\n",
    "    autoplay=False,\n",
    ")\n",
    "def audio(freq: float, autoplay: bool):\n",
    "    print(f\"Frequency: {freq}\")\n",
    "    \n",
    "    length = int(sr * duration)\n",
    "    t = np.linspace(0, duration, length)\n",
    "    y = 0.1 * np.sin(2 * np.pi * freq * t)\n",
    "\n",
    "    fig.clear() \n",
    "    ax = fig.add_subplot(1, 1, 1)\n",
    "    ax.plot(y)\n",
    "\n",
    "    fig.tight_layout()\n",
    "    IPython.display.display(\n",
    "        fig.canvas,\n",
    "        IPython.display.Audio(y, rate=sr, autoplay=autoplay, normalize=False),\n",
    "    )\n",
    "import time\n",
    "# time.sleep(0.1)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

execute above python code will sometimes blocks

Reason: If the kernel does not pass in the timeout parameter when calling the poll api, and sends a send message to the shell channel at the same time, it will always be blocked.

i have raise an merge request to fix the issue

hjliu0206 commented 1 month ago

here is my fix merge request https://github.com/jupyter/nbclient/pull/311