gradio-app / gradio

Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
http://www.gradio.app
Apache License 2.0
32.43k stars 2.43k forks source link

change event fires infinitely for some components when updated once (Markdown, HTML) #9103

Open JohnDuncanScott opened 1 month ago

JohnDuncanScott commented 1 month ago

Describe the bug

Markdown - run the code below and see that the print statement carries on infinitely. I expected the change event handler to fire, I would check the input value, replace the callable with a new value and then the early return statement would mean the component wouldn't update. This is so I can react to an update once and then never again. I have tried returning the same value instead of return (which I thought would just mean no update) and the infinite trigger still happens. I have also tried setting triggerMode to "once".

Textbox - replace Markdown with Textbox in the sample code and note that the change event handler never fires

Have you searched existing issues? πŸ”Ž

Reproduction

import gradio as gr

with gr.Blocks() as model_test_harness:

    def update_value() -> str:
        return "Foo!"

    # Create components
    with gr.Row(variant="panel"):
        markdown = gr.Markdown(value=update_value)

    # Define handlers
    def markdown_change_handler(value: str):
        print("Changed!")

        if value == "Bar!":
            return

        return {markdown: gr.Markdown(value="Bar!")}

    # Register handlers
    markdown.change(  # type: ignore
        markdown_change_handler, inputs=[markdown], outputs=[markdown]
    )

model_test_harness.launch()  # type: ignore

Screenshot

No response

Logs

No response

System Info

gradio==4.36.1

Severity

Blocking usage of gradio

JohnDuncanScott commented 1 month ago

Expanding on this: Have upgraded to 4.41.0 and the issue still persists.

To simplify this further and show there is a definite bug with Markdown and change handler, use this code:

import gradio as gr

with gr.Blocks() as model_test_harness:
    # Create components
    with gr.Row(variant="panel"):
        markdown = gr.Markdown("Markdown value")
        button = gr.Button("Press me")

    # Define handlers
    def markdown_change_handler(value: str):
        print("Changed!")

    def button_click_handler():
        print("Changing markdown component")
        return "New Markdown value"

    # Register handlers
    markdown.change(  # type: ignore
        markdown_change_handler, inputs=[markdown], outputs=[]
    )
    button.click(button_click_handler, inputs=[], outputs=[markdown])

model_test_harness.launch()  # type: ignore

Press the button to trigger a single update and note that the change handler then fires infinitely afterwards. This essentially makes the Markdown component unusable at the moment.

JohnDuncanScott commented 1 month ago

Sorry for all the edits, this bug is really strange. I don't have time to go through this exhaustively but both Markdown and HTML components have the same infinite loop issue. If you use Textbox, the change handler fires only once as expected. I don't know how widespread this issue is, but it seems a major bug both from performance and usability.

If I were to guess without knowing the code base at all, I suspect Textbox is just storing the value as is, whereas the other 2 are transforming it in some way (due to the different internal rendering) so the engine thinks a change is happening even though the text is just being reformatted internally (maybe, total guess :) ).