Fix bug with gr.update and interactive=True (#2639)

* Fix update interactivity

* Lint

* CHANGELOG

* Fix

* Undo interactive=True

* Do not call update twice

* Add unit test

* Revert change

* Lint
This commit is contained in:
Freddy Boulton 2022-11-15 17:53:35 +01:00 committed by GitHub
parent ebb65eb9ee
commit e6336d6882
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 11 deletions

View File

@ -6,6 +6,7 @@ No changes to highlight.
## Bug Fixes:
* Updated the minimum FastApi used in tests to version 0.87 [@freddyaboulton](https://github.com/freddyaboulton) in [PR 2647](https://github.com/gradio-app/gradio/pull/2647)
* Fixed bug where interfaces with examples could not be loaded with `gr.Interface.load` by [@freddyaboulton](https://github.com/freddyaboulton) [PR 2640](https://github.com/gradio-app/gradio/pull/2640)
* Fixed bug where the `interactive` property of a component could not be updated by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 2639](https://github.com/gradio-app/gradio/pull/2639)
## Documentation Changes:
No changes to highlight.

View File

@ -421,10 +421,11 @@ def postprocess_update_dict(block: Block, update_dict: Dict, postprocess: bool =
update_dict: The original update dictionary
postprocess: Whether to postprocess the "value" key of the update dictionary.
"""
prediction_value = block.get_specific_update(update_dict)
if prediction_value.get("value") is components._Keywords.NO_VALUE:
prediction_value.pop("value")
prediction_value = delete_none(prediction_value, skip_value=True)
if update_dict.get("__type__", "") == "generic_update":
update_dict = block.get_specific_update(update_dict)
if update_dict.get("value") is components._Keywords.NO_VALUE:
update_dict.pop("value")
prediction_value = delete_none(update_dict, skip_value=True)
if "value" in prediction_value and postprocess:
prediction_value["value"] = block.postprocess(prediction_value["value"])
return prediction_value

View File

@ -391,6 +391,35 @@ class TestComponentsInBlocks:
"value": gr.media_data.BASE64_IMAGE,
}
@pytest.mark.asyncio
async def test_blocks_update_interactive(
self,
):
def specific_update():
return [
gr.Image.update(interactive=True),
gr.Textbox.update(interactive=True),
]
def generic_update():
return [gr.update(interactive=True), gr.update(interactive=True)]
with gr.Blocks() as demo:
run = gr.Button(value="Make interactive")
image = gr.Image()
textbox = gr.Text()
run.click(specific_update, None, [image, textbox])
run.click(generic_update, None, [image, textbox])
for fn_index in range(2):
output = await demo.process_api(fn_index, [])
assert output["data"][0] == {
"interactive": True,
"__type__": "update",
"mode": "dynamic",
}
assert output["data"][1] == {"__type__": "update", "mode": "dynamic"}
class TestCallFunction:
@pytest.mark.asyncio
@ -677,7 +706,7 @@ class TestSpecificUpdate:
def test_with_update(self):
specific_update = gr.Textbox.get_specific_update(
{"lines": 4, "__type__": "update"}
{"lines": 4, "__type__": "update", "interactive": False}
)
assert specific_update == {
"lines": 4,
@ -688,19 +717,41 @@ class TestSpecificUpdate:
"visible": None,
"value": gr.components._Keywords.NO_VALUE,
"__type__": "update",
"mode": "static",
}
specific_update = gr.Textbox.get_specific_update(
{"lines": 4, "__type__": "update", "interactive": True}
)
assert specific_update == {
"lines": 4,
"max_lines": None,
"placeholder": None,
"label": None,
"show_label": None,
"visible": None,
"value": gr.components._Keywords.NO_VALUE,
"__type__": "update",
"mode": "dynamic",
}
def test_with_generic_update(self):
specific_update = gr.Video.get_specific_update(
{"visible": True, "value": "test.mp4", "__type__": "generic_update"}
{
"visible": True,
"value": "test.mp4",
"__type__": "generic_update",
"interactive": True,
}
)
assert specific_update == {
"source": None,
"label": None,
"show_label": None,
"interactive": None,
"visible": True,
"value": "test.mp4",
"mode": "dynamic",
"interactive": True,
"__type__": "update",
}

View File

@ -151,7 +151,10 @@ class TestProcessExamples:
cache_examples=True,
)
prediction = await io.examples_handler.load_from_cache(1)
assert prediction[0] == {"visible": False, "__type__": "update"}
assert prediction[0] == {
"visible": False,
"__type__": "update",
}
@pytest.mark.asyncio
async def test_caching_with_mix_update(self):
@ -163,7 +166,11 @@ class TestProcessExamples:
cache_examples=True,
)
prediction = await io.examples_handler.load_from_cache(1)
assert prediction[0] == {"lines": 4, "value": "hello", "__type__": "update"}
assert prediction[0] == {
"lines": 4,
"value": "hello",
"__type__": "update",
}
@pytest.mark.asyncio
async def test_caching_with_dict(self):
@ -171,15 +178,18 @@ class TestProcessExamples:
out = gr.Label()
io = gr.Interface(
lambda _: {text: gr.update(lines=4), out: "lion"},
lambda _: {text: gr.update(lines=4, interactive=False), out: "lion"},
"textbox",
[text, out],
examples=["abc"],
cache_examples=True,
)
prediction = await io.examples_handler.load_from_cache(0)
assert prediction == [{"lines": 4, "__type__": "update"}, {"label": "lion"}]
assert not any(d["trigger"] == "fake_event" for d in io.config["dependencies"])
assert prediction == [
{"lines": 4, "__type__": "update", "mode": "static"},
{"label": "lion"},
]
def test_raise_helpful_error_message_if_providing_partial_examples(self, tmp_path):
def foo(a, b):