diff --git a/CHANGELOG.md b/CHANGELOG.md index e0b027d84c..e7c972d2cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/gradio/blocks.py b/gradio/blocks.py index f82dcd0c92..4b2e7b00f8 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -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 diff --git a/test/test_blocks.py b/test/test_blocks.py index 26ccad7f73..03ae152b45 100644 --- a/test/test_blocks.py +++ b/test/test_blocks.py @@ -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", } diff --git a/test/test_examples.py b/test/test_examples.py index 5be426b506..6a06fb320d 100644 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -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):