Fix bugs with gr.update (#2157)

* Use void to reset to original state

* Cosmetic fixes to comments

* Use keyword

* Set default value to no value

* Fix test

* Make private

* Improve comment on _Keywords.NO_VALUE

Improve comment

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Use NO_VALUE in `test_blocks`

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Update test/test_blocks.py

Use NO_VALUE in tests

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
Freddy Boulton 2022-09-06 17:08:38 -04:00 committed by GitHub
parent 3464673064
commit 6c1bfbb42c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 39 deletions

View File

@ -18,7 +18,7 @@ from demos.kitchen_sink_random.constants import (
components = [
gr.Textbox(value=lambda: datetime.now(), label="Current Time"),
gr.Number(value=lambda: random.random(), label="Ranom Percentage"),
gr.Number(value=lambda: random.random(), label="Random Percentage"),
gr.Slider(minimum=-1, maximum=1, randomize=True, label="Slider with randomize"),
gr.Slider(
minimum=0,
@ -88,11 +88,23 @@ with gr.Blocks() as demo:
for component in components:
component.render()
reset = gr.Button(value="Reset")
hide = gr.Button(value="Hide")
reveal = gr.Button(value="Reveal")
reset.click(
lambda: [c.update(value=None) for c in components],
inputs=[],
outputs=components,
)
hide.click(
lambda: [c.update(visible=False) for c in components],
inputs=[],
outputs=components
)
reveal.click(
lambda: [c.update(visible=True) for c in components],
inputs=[],
outputs=components
)
if __name__ == "__main__":

View File

@ -7,7 +7,6 @@ import os
import pkgutil
import random
import sys
import tempfile
import time
import warnings
import webbrowser
@ -35,7 +34,6 @@ from gradio.documentation import (
document_component_api,
set_documentation_group,
)
from gradio.exceptions import Error
from gradio.utils import component_or_layout_class, delete_none
set_documentation_group("blocks")
@ -669,6 +667,13 @@ class Blocks(BlockContext):
prediction_value = block.__class__.update(
**prediction_value
)
# If the prediction is the default (NO_VALUE) enum then the user did
# not specify a value for the 'value' key and we can get rid of it
if (
prediction_value.get("value")
== components._Keywords.NO_VALUE
):
prediction_value.pop("value")
prediction_value = delete_none(prediction_value)
if "value" in prediction_value:
prediction_value["value"] = block.postprocess(

View File

@ -15,6 +15,7 @@ import tempfile
import uuid
import warnings
from copy import deepcopy
from enum import Enum
from pathlib import Path
from types import ModuleType
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
@ -59,6 +60,10 @@ from gradio.utils import component_or_layout_class
set_documentation_group("component")
class _Keywords(Enum):
NO_VALUE = "NO_VALUE" # Used as a sentinel to determine if nothing is provided as a argument for `value` in `Component.update()`
class Component(Block):
"""
A base class for defining the methods that all gradio components should have.
@ -286,7 +291,7 @@ class Textbox(Changeable, Submittable, IOComponent, SimpleSerializable, FormComp
@staticmethod
def update(
value: Optional[str] = None,
value: Optional[str] = _Keywords.NO_VALUE,
lines: Optional[int] = None,
max_lines: Optional[int] = None,
placeholder: Optional[str] = None,
@ -465,7 +470,7 @@ class Number(Changeable, Submittable, IOComponent, SimpleSerializable, FormCompo
@staticmethod
def update(
value: Optional[float] = None,
value: Optional[float] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
interactive: Optional[bool] = None,
@ -637,7 +642,7 @@ class Slider(Changeable, IOComponent, SimpleSerializable, FormComponent):
@staticmethod
def update(
value: Optional[float] = None,
value: Optional[float] = _Keywords.NO_VALUE,
minimum: Optional[float] = None,
maximum: Optional[float] = None,
step: Optional[float] = None,
@ -763,7 +768,7 @@ class Checkbox(Changeable, IOComponent, SimpleSerializable, FormComponent):
@staticmethod
def update(
value: Optional[bool] = None,
value: Optional[bool] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
interactive: Optional[bool] = None,
@ -861,7 +866,7 @@ class CheckboxGroup(Changeable, IOComponent, SimpleSerializable, FormComponent):
@staticmethod
def update(
value: Optional[List[str]] = None,
value: Optional[List[str]] = _Keywords.NO_VALUE,
choices: Optional[List[str]] = None,
label: Optional[str] = None,
show_label: Optional[bool] = None,
@ -1024,7 +1029,7 @@ class Radio(Changeable, IOComponent, SimpleSerializable, FormComponent):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
choices: Optional[List[str]] = None,
label: Optional[str] = None,
show_label: Optional[bool] = None,
@ -1257,7 +1262,7 @@ class Image(Editable, Clearable, Changeable, Streamable, IOComponent, ImgSeriali
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
interactive: Optional[bool] = None,
@ -1581,7 +1586,7 @@ class Video(Changeable, Clearable, Playable, IOComponent, FileSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
source: Optional[str] = None,
label: Optional[str] = None,
show_label: Optional[bool] = None,
@ -1768,7 +1773,7 @@ class Audio(Changeable, Clearable, Playable, Streamable, IOComponent, FileSerial
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
source: Optional[str] = None,
label: Optional[str] = None,
show_label: Optional[bool] = None,
@ -2046,7 +2051,7 @@ class File(Changeable, Clearable, IOComponent, FileSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
interactive: Optional[bool] = None,
@ -2282,7 +2287,7 @@ class Dataframe(Changeable, IOComponent, JSONSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
max_rows: Optional[int] = None,
max_cols: Optional[str] = None,
label: Optional[str] = None,
@ -2494,7 +2499,7 @@ class Timeseries(Changeable, IOComponent, JSONSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
colors: Optional[List[str]] = None,
label: Optional[str] = None,
show_label: Optional[bool] = None,
@ -2644,7 +2649,7 @@ class Button(Clickable, IOComponent, SimpleSerializable):
@staticmethod
def update(
value: Optional[str] = None,
value: Optional[str] = _Keywords.NO_VALUE,
variant: Optional[str] = None,
visible: Optional[bool] = None,
):
@ -2732,7 +2737,7 @@ class ColorPicker(Changeable, Submittable, IOComponent, SimpleSerializable):
@staticmethod
def update(
value: Optional[str] = None,
value: Optional[str] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -2867,7 +2872,7 @@ class Label(Changeable, IOComponent, JSONSerializable):
@staticmethod
def update(
value: Optional[Dict[str, float] | str | float] = None,
value: Optional[Dict[str, float] | str | float] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -2957,7 +2962,9 @@ class HighlightedText(Changeable, IOComponent, JSONSerializable):
@staticmethod
def update(
value: Optional[List[Tuple[str, str | float | None]] | Dict] = None,
value: Optional[
List[Tuple[str, str | float | None]] | Dict
] = _Keywords.NO_VALUE,
color_map: Optional[Dict[str, str]] = None,
show_legend: Optional[bool] = None,
label: Optional[str] = None,
@ -3086,7 +3093,7 @@ class JSON(Changeable, IOComponent, JSONSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -3171,7 +3178,7 @@ class HTML(Changeable, IOComponent, SimpleSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -3228,7 +3235,7 @@ class Gallery(IOComponent):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -3361,7 +3368,7 @@ class Carousel(IOComponent, Changeable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -3452,7 +3459,7 @@ class Chatbot(Changeable, IOComponent, JSONSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
color_map: Optional[Tuple[str, str]] = None,
label: Optional[str] = None,
show_label: Optional[bool] = None,
@ -3551,7 +3558,7 @@ class Model3D(Changeable, Editable, Clearable, IOComponent, FileSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -3668,7 +3675,7 @@ class Plot(Changeable, Clearable, IOComponent, JSONSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
label: Optional[str] = None,
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
@ -3756,7 +3763,7 @@ class Markdown(IOComponent, Changeable, SimpleSerializable):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
visible: Optional[bool] = None,
):
updated_config = {
@ -3832,7 +3839,7 @@ class Dataset(Clickable, Component):
@staticmethod
def update(
samples: Optional[Any] = None,
samples: Optional[Any] = _Keywords.NO_VALUE,
visible: Optional[bool] = None,
label: Optional[str] = None,
):
@ -3905,7 +3912,7 @@ class Interpretation(Component):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
visible: Optional[bool] = None,
):
return {
@ -3949,7 +3956,7 @@ class StatusTracker(Component):
@staticmethod
def update(
value: Optional[Any] = None,
value: Optional[Any] = _Keywords.NO_VALUE,
visible: Optional[bool] = None,
):
return {

View File

@ -46,12 +46,12 @@ XRAY_CONFIG = {
"image_mode": "RGB",
"source": "upload",
"tool": "editor",
"value": None,
"streaming": False,
"mirror_webcam": True,
"show_label": True,
"name": "image",
"visible": True,
"value": None,
"style": {},
},
},
@ -59,10 +59,10 @@ XRAY_CONFIG = {
"id": 29,
"type": "json",
"props": {
"value": None,
"show_label": True,
"name": "json",
"visible": True,
"value": None,
"style": {},
},
},
@ -94,24 +94,24 @@ XRAY_CONFIG = {
"image_mode": "RGB",
"source": "upload",
"tool": "editor",
"value": None,
"streaming": False,
"mirror_webcam": True,
"show_label": True,
"name": "image",
"visible": True,
"style": {},
"value": None,
},
},
{
"id": 34,
"type": "json",
"props": {
"value": None,
"show_label": True,
"name": "json",
"visible": True,
"style": {},
"value": None,
},
},
{
@ -279,6 +279,7 @@ XRAY_CONFIG_DIFF_IDS = {
"type": "image",
"props": {
"image_mode": "RGB",
"value": None,
"source": "upload",
"tool": "editor",
"streaming": False,
@ -286,7 +287,6 @@ XRAY_CONFIG_DIFF_IDS = {
"show_label": True,
"name": "image",
"visible": True,
"value": None,
"style": {},
},
},
@ -295,10 +295,10 @@ XRAY_CONFIG_DIFF_IDS = {
"type": "json",
"props": {
"show_label": True,
"value": None,
"name": "json",
"visible": True,
"style": {},
"value": None,
},
},
{
@ -349,8 +349,8 @@ XRAY_CONFIG_DIFF_IDS = {
"show_label": True,
"name": "json",
"visible": True,
"value": None,
"style": {},
"value": None,
},
},
{
@ -525,7 +525,6 @@ XRAY_CONFIG_WITH_MISTAKE = {
"id": 7,
"type": "json",
"props": {
"value": None,
"name": "json",
"style": {},
},
@ -572,7 +571,6 @@ XRAY_CONFIG_WITH_MISTAKE = {
"id": 12,
"type": "json",
"props": {
"value": None,
"name": "json",
"style": {},
},

View File

@ -13,6 +13,7 @@ def copy_all_demos(source_dir: str, dest_dir: str):
"blocks_layout",
"blocks_mask",
"blocks_multiple_event_triggers",
"blocks_update",
"calculator",
"fake_gan",
"gender_sentence_default_interpretation",

View File

@ -266,5 +266,20 @@ def test_blocks_do_not_filter_none_values_from_updates(io_components):
)
def test_blocks_does_not_replace_keyword_literal():
with gr.Blocks() as demo:
text = gr.Textbox()
btn = gr.Button(value="Reset")
btn.click(
lambda: gr.update(value="NO_VALUE"),
inputs=[],
outputs=text,
)
output = demo.postprocess_data(0, gr.update(value="NO_VALUE"), state=None)
assert output[0]["value"] == "NO_VALUE"
if __name__ == "__main__":
unittest.main()