jupyterlite / pyodide-kernel

Python kernel for JupyterLite powered by Pyodide
https://jupyterlite-pyodide-kernel.readthedocs.io/en/latest/_static/
BSD 3-Clause "New" or "Revised" License
46 stars 25 forks source link

ipywidgets does not work in some situations #133

Open MRYingLEE opened 3 months ago

MRYingLEE commented 3 months ago

For the following simple ipywidgets code:

%pip install ipywidgets
# Import IPython widgets for interactive input
from IPython.display import display
import ipywidgets as widgets

# Create a text box for user input
prompt_box = widgets.Textarea(
    value='Enter your prompt here...',
    placeholder='Type something...',
    description='Prompt:',
    disabled=False
)

# Create a button to generate text
button = widgets.Button(description="Generate Text")

# Function to handle button click
def on_button_click(b):
    prompt = prompt_box.value
    generated_text = "generate_text(prompt)"
    print("Prompt:", prompt)
    print("Generated Text:", generated_text)

# Attach the function to the button click event
button.on_click(on_button_click)

# Display the text box and button
display(prompt_box, button)

I works well in other Jupyter environment, such as Colab (https://colab.research.google.com/). image

But the button clicking does not show anything in Jupyterlite, such as here (https://jupyterlite-pyodide-kernel.readthedocs.io/en/latest/_static/lab/index.html).

Please check. Thanks,

MRYingLEE commented 3 months ago

I used kernelspy(https://github.com/jupyterlab-contrib/jupyterlab-kernelspy) to check the messages after the button was clicked, and I saw: image

So it seems the messaging system worked well but for some reason the messages were not processed well.

MartinaMJ commented 3 months ago

Hello. I have been facing this same problem. I noticed the kernel responds to the first few interactions but then stops responding. Did you find a solution or a possible cause?

pierre-haessig commented 2 months ago

My jupyterlite deployment (e.g. https://pierre-haessig.github.io/pierre-notebooks/notebooks/?path=Frequency%20regulation%20nocode.ipynb) is also facing broken widget interaction, despite being up to date with jupyterlite-core==0.4.1 and jupyterlite-pyodide-kernel==0.4.2 just like the jupyterlite demo as per PR https://github.com/jupyterlite/demo/pull/148

Also, the Lorenz attractor demo in jupyter.org's try-jupyter deployment is broken: https://jupyter.org/try-jupyter/lab/index.html?path=notebooks%2FLorenz.ipynb

martenrichter commented 1 week ago

I am also facing the problem, I have tried edge, chrome and firefox all the same. I am integrating Jupyter Lite in my electronic chalk application to provide applets. I am relatively new to the Javascript Jupyter code base (and I can not write Python), but I have some experience debugging stuff (JS or C++), and I am motivated to fix this, as this is the core functionality I want to use. Can someone give me a pointer on where to start? After reading the previous conversation, I think I should probably start inside Pyoid rather than the Jupyter JS side. And may be bisecting pyoide version number?

martenrichter commented 1 week ago

I have installed a Kernel interceptor using

(kernel: Kernel.IKernelConnection) => {
    console.log('Install Kernel interceptor', kernel);
    kernel.anyMessage.connect((sender, args) => {
      console.log('Intercept any message', sender, args);
}

I only see messages as long as the updates work. So, the messages sent to the kernel stop.

martenrichter commented 6 days ago

So far, I see the following: 1.) Just before changing the values does not work anymore, I see an unhandled comm msg at: https://github.com/jupyterlab/jupyterlab/blob/ef7925c4d1b175a2e51bbeab376cafd57a1d0fe0/packages/services/src/kernel/default.ts#L1433 Right after this, the kernel no longer sends messages. 2.) As the last update does not finish, the throttling of widget changes (only one update message being concurrently processed) prevents future updates.

However I did not find a cause so far. (But I did try it with xeus and it worked)

martenrichter commented 6 days ago

I have more insights. The future is actually deleted from this._futures before it is looked for in _handleMessage. So either two messages with the same id arrive or something webpack related went from. I also believe that there are two issues, one future related and the other related to throttling of the widgets.

martenrichter commented 6 days ago

One step further the future related bug is caused by a message on the shell channel with type comm_msg. This causes the future to be resolved. The display_data message, that has the parent message id of the previously arrived comm_msg arrives with some probability after the comm_msg and this causes a the output to be dismissed. Here is the comm_msg:

{
    "msg": {
        "buffers": [],
        "channel": "shell",
        "content": {
            "comm_id": "cfd005729e054b5199fd8bf406bffbc7",
            "data": {
                "method": "update",
                "state": {
                    "value": "Type something...ghg"
                },
                "buffer_paths": []
            }
        },
        "header": {
            "date": "2024-11-17T16:59:00.359Z",
            "msg_id": "3f8617f7-0701-47c9-ab52-f241d0b01c30",
            "msg_type": "comm_msg",
            "session": "9f8aabf4-5b39-4ea8-9b3e-0c7a40493cfa",
            "username": "",
            "version": "5.2"
        },
        "metadata": {},
        "parent_header": {}
    },
    "direction": "send"
}

and this is the display_data message afterwards:

{
    "msg": {
        "channel": "iopub",
        "header": {
            "date": "2024-11-17T16:59:00.378Z",
            "msg_id": "19e625c7-90dd-4476-b2fa-cd064b283ed5",
            "msg_type": "display_data",
            "session": "9f8aabf4-5b39-4ea8-9b3e-0c7a40493cfa",
            "username": "",
            "version": "5.2"
        },
        "parent_header": {
            "date": "2024-11-17T16:59:00.359Z",
            "msg_id": "3f8617f7-0701-47c9-ab52-f241d0b01c30",
            "msg_type": "comm_msg",
            "session": "9f8aabf4-5b39-4ea8-9b3e-0c7a40493cfa",
            "username": "",
            "version": "5.2"
        },
        "metadata": {},
        "content": {
            "data": {
                "text/plain": "'Updated value: Type something...ghg'"
            },
            "metadata": {}
        },
        "buffers": []
    },
    "direction": "recv"
}

So either the send order of the messages is not ensured in the python code, or something shuffles the messages. This can be for example awaits.

martenrichter commented 6 days ago

Another thing there is no transient object and thus no display_id. So if the widget use the display_id for communication, it may be that the id is lost.

martenrichter commented 5 days ago

I have thought about this. Both problems are related. It seems that the two messages are competing for the same future. If the comm_msg arrives first, the display is not updated. If the display_data message arrives first, the control does not send anymore as throttling is activated, and the release message is lost. I wonder if this is the case in all environments and if it is just a timing issue revealed in jupyter lite/pyoide. @jtpio or @martinRenou, what should the message flow be?

martenrichter commented 23 hours ago

In xpython, the shell message for update does not have a parent_header set, so the future is not cleared. This makes the xpython kernel work without problems, and the set parent_header for the shell message seems to cause the error for pyoide. I will try to verify the hypothesis and find the cause.

martenrichter commented 14 hours ago

Ok, I have reinspected xpython, I confused the send with the recv message (also in the post above). But still in pyoide the update and the display message have the same parentHeader and the two message are competing about the future. In xpython, however, they have a different parentHeader and thus parentId and future.

So I have tried to understand what happened during the procession of the two messages. At one point in pyoide get_parent is called: https://github.com/jupyterlite/pyodide-kernel/blob/b70f099252670535db4745b5abab680300dc84eb/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/kernel.py#L29 as you can see it is marked with a todo. If we look into the original implementation of ipykernel: https://github.com/ipython/ipykernel/blob/188f39c509f1056c3784aad6954498f31de955a3/ipykernel/kernelbase.py#L622 we see two different parent headers for control and shell, which would explain why the problem does not occur on normal jupyter. I would guess that this is the cause of the issues with interact. However, it is just a guess.