Fix updating choices in gr.Dropdown and updates related to other components (#6309)

* postprocess fix

* add changeset

* add changeset

* blocks

* formatting

* demo

* add changeset

* Add code

* reduce

* trigger ci

* Update gradio/blocks.py

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: freddyaboulton <alfonsoboulton@gmail.com>
This commit is contained in:
Abubakar Abid 2023-11-07 04:16:29 -08:00 committed by GitHub
parent 3cdeabc684
commit c561287812
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 16 deletions

View File

@ -0,0 +1,5 @@
---
"gradio": patch
---
fix:Fix updating choices in `gr.Dropdown` and updates related to other components

View File

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: blocks_essay"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "\n", "def change_textbox(choice):\n", " if choice == \"short\":\n", " return gr.Textbox(lines=2, visible=True), gr.Button(interactive=True)\n", " elif choice == \"long\":\n", " return gr.Textbox(lines=8, visible=True, value=\"Lorem ipsum dolor sit amet\"), gr.Button(interactive=True)\n", " else:\n", " return gr.Textbox(visible=False), gr.Button(interactive=False)\n", "\n", "\n", "with gr.Blocks() as demo:\n", " radio = gr.Radio(\n", " [\"short\", \"long\", \"none\"], label=\"What kind of essay would you like to write?\"\n", " )\n", " text = gr.Textbox(lines=2, interactive=True, show_copy_button=True)\n", "\n", " with gr.Row():\n", " num = gr.Number(minimum=0, maximum=100, label=\"input\")\n", " out = gr.Number(label=\"output\")\n", " minimum_slider = gr.Slider(0, 100, 0, label=\"min\")\n", " maximum_slider = gr.Slider(0, 100, 100, label=\"max\")\n", " submit_btn = gr.Button(\"Submit\", variant=\"primary\")\n", "\n", " def reset_bounds(minimum, maximum):\n", " return gr.Number(minimum=minimum, maximum=maximum)\n", "\n", " radio.change(fn=change_textbox, inputs=radio, outputs=[text, submit_btn])\n", " gr.on(\n", " [minimum_slider.change, maximum_slider.change],\n", " reset_bounds,\n", " [minimum_slider, maximum_slider],\n", " outputs=num,\n", " )\n", " num.submit(lambda x: x, num, out)\n", "\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} {"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: blocks_essay"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "countries_cities_dict = {\n", " \"USA\": [\"New York\", \"Los Angeles\", \"Chicago\"],\n", " \"Canada\": [\"Toronto\", \"Montreal\", \"Vancouver\"],\n", " \"Pakistan\": [\"Karachi\", \"Lahore\", \"Islamabad\"],\n", "}\n", "\n", "\n", "def change_textbox(choice):\n", " if choice == \"short\":\n", " return gr.Textbox(lines=2, visible=True), gr.Button(interactive=True)\n", " elif choice == \"long\":\n", " return gr.Textbox(lines=8, visible=True, value=\"Lorem ipsum dolor sit amet\"), gr.Button(interactive=True)\n", " else:\n", " return gr.Textbox(visible=False), gr.Button(interactive=False)\n", "\n", "\n", "with gr.Blocks() as demo:\n", " radio = gr.Radio(\n", " [\"short\", \"long\", \"none\"], label=\"What kind of essay would you like to write?\"\n", " )\n", " text = gr.Textbox(lines=2, interactive=True, show_copy_button=True)\n", "\n", " with gr.Row():\n", " num = gr.Number(minimum=0, maximum=100, label=\"input\")\n", " out = gr.Number(label=\"output\")\n", " minimum_slider = gr.Slider(0, 100, 0, label=\"min\")\n", " maximum_slider = gr.Slider(0, 100, 100, label=\"max\")\n", " submit_btn = gr.Button(\"Submit\", variant=\"primary\")\n", "\n", " with gr.Row():\n", " country = gr.Dropdown(list(countries_cities_dict.keys()), label=\"Country\")\n", " cities = gr.Dropdown([], label=\"Cities\")\n", " \n", " @country.change(inputs=country, outputs=cities)\n", " def update_cities(country):\n", " cities = list(countries_cities_dict[country])\n", " return gr.Dropdown(choices=cities, value=cities[0], interactive=True)\n", "\n", " def reset_bounds(minimum, maximum):\n", " return gr.Number(minimum=minimum, maximum=maximum)\n", "\n", " radio.change(fn=change_textbox, inputs=radio, outputs=[text, submit_btn])\n", " gr.on(\n", " [minimum_slider.change, maximum_slider.change],\n", " reset_bounds,\n", " [minimum_slider, maximum_slider],\n", " outputs=num,\n", " )\n", " num.submit(lambda x: x, num, out)\n", "\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -1,5 +1,11 @@
import gradio as gr import gradio as gr
countries_cities_dict = {
"USA": ["New York", "Los Angeles", "Chicago"],
"Canada": ["Toronto", "Montreal", "Vancouver"],
"Pakistan": ["Karachi", "Lahore", "Islamabad"],
}
def change_textbox(choice): def change_textbox(choice):
if choice == "short": if choice == "short":
@ -23,6 +29,15 @@ with gr.Blocks() as demo:
maximum_slider = gr.Slider(0, 100, 100, label="max") maximum_slider = gr.Slider(0, 100, 100, label="max")
submit_btn = gr.Button("Submit", variant="primary") submit_btn = gr.Button("Submit", variant="primary")
with gr.Row():
country = gr.Dropdown(list(countries_cities_dict.keys()), label="Country")
cities = gr.Dropdown([], label="Cities")
@country.change(inputs=country, outputs=cities)
def update_cities(country):
cities = list(countries_cities_dict[country])
return gr.Dropdown(choices=cities, value=cities[0], interactive=True)
def reset_bounds(minimum, maximum): def reset_bounds(minimum, maximum):
return gr.Number(minimum=minimum, maximum=maximum) return gr.Number(minimum=minimum, maximum=maximum)

View File

@ -382,13 +382,15 @@ def postprocess_update_dict(
update_dict: The original update dictionary update_dict: The original update dictionary
postprocess: Whether to postprocess the "value" key of the update dictionary. postprocess: Whether to postprocess the "value" key of the update dictionary.
""" """
update_dict = {k: update_dict[k] for k in update_dict if hasattr(block, k)} value = update_dict.pop("value", components._Keywords.NO_VALUE)
if update_dict.get("value") is components._Keywords.NO_VALUE: update_dict = {k: getattr(block, k) for k in update_dict if hasattr(block, k)}
update_dict.pop("value") if value is not components._Keywords.NO_VALUE:
elif "value" in update_dict and postprocess: if postprocess:
update_dict["value"] = block.postprocess(update_dict["value"]) update_dict["value"] = block.postprocess(value)
if isinstance(update_dict["value"], (GradioModel, GradioRootModel)): if isinstance(update_dict["value"], (GradioModel, GradioRootModel)):
update_dict["value"] = update_dict["value"].model_dump() update_dict["value"] = update_dict["value"].model_dump()
else:
update_dict["value"] = value
update_dict["__type__"] = "update" update_dict["__type__"] = "update"
return update_dict return update_dict
@ -1360,14 +1362,14 @@ Received outputs:
prediction_value["__type__"] = "update" prediction_value["__type__"] = "update"
if utils.is_update(prediction_value): if utils.is_update(prediction_value):
if output_id in state: if output_id in state:
args = state[output_id].constructor_args.copy() kwargs = state[output_id].constructor_args.copy()
else: else:
args = self.blocks[output_id].constructor_args.copy() kwargs = self.blocks[output_id].constructor_args.copy()
args.update(prediction_value) kwargs.update(prediction_value)
args.pop("value", None) kwargs.pop("value", None)
args.pop("__type__") kwargs.pop("__type__")
args["render"] = False kwargs["render"] = False
state[output_id] = self.blocks[output_id].__class__(**args) state[output_id] = self.blocks[output_id].__class__(**kwargs)
prediction_value = postprocess_update_dict( prediction_value = postprocess_update_dict(
block=state[output_id], block=state[output_id],

View File

@ -50,3 +50,16 @@ test("updates backend correctly", async ({ page }) => {
await num.press("Enter"); await num.press("Enter");
await expect(output).toHaveValue("25"); await expect(output).toHaveValue("25");
}); });
test("updates dropdown choices correctly", async ({ page }) => {
const country = await page.getByLabel("Country").first();
const city = await page.getByLabel("Cities").first();
await country.fill("Canada");
await country.press("Enter");
await expect(city).toHaveValue("Toronto");
await country.fill("Pakistan");
await country.press("Enter");
await expect(city).toHaveValue("Karachi");
});

View File

@ -1,4 +1,4 @@
lockfileVersion: '6.0' lockfileVersion: '6.1'
settings: settings:
autoInstallPeers: true autoInstallPeers: true

View File

@ -1543,3 +1543,35 @@ def test_deprecation_warning_emitted_when_concurrency_count_set():
gr.Interface(lambda x: x, gr.Textbox(), gr.Textbox()).queue( gr.Interface(lambda x: x, gr.Textbox(), gr.Textbox()).queue(
concurrency_count=12 concurrency_count=12
) )
def test_postprocess_update_dict():
block = gr.Textbox()
update_dict = {"value": 2.0, "visible": True, "invalid_arg": "hello"}
assert gr.blocks.postprocess_update_dict(block, update_dict, True) == {
"__type__": "update",
"value": "2.0",
"visible": True,
}
block = gr.Textbox(lines=10)
update_dict = {"value": 2.0, "lines": 10}
assert gr.blocks.postprocess_update_dict(block, update_dict, False) == {
"__type__": "update",
"value": 2.0,
"lines": 10,
}
block = gr.Dropdown(choices=["New Country A", "New Country B"])
update_dict = {
"value": "New Country A",
"choices": ["New Country A", "New Country B"],
}
assert gr.blocks.postprocess_update_dict(block, update_dict, False) == {
"__type__": "update",
"value": "New Country A",
"choices": [
("New Country A", "New Country A"),
("New Country B", "New Country B"),
],
}