mirror of
https://github.com/gradio-app/gradio.git
synced 2024-12-27 02:30:17 +08:00
Merge pull request #603 from gradio-app/Optional-Input-Clarification
Optional-Input-Clarification
This commit is contained in:
commit
fd13591f20
@ -73,6 +73,9 @@ Do not forget the format the backend before pushing.
|
||||
```
|
||||
bash scripts/format_backend.sh
|
||||
```
|
||||
```
|
||||
bash scripts/format_frontend.sh
|
||||
```
|
||||
You can run the circleci checks locally as well.
|
||||
```
|
||||
bash scripts/run_circleci.sh
|
||||
|
@ -1,6 +1,6 @@
|
||||
import os
|
||||
import shutil
|
||||
from typing import Any, Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from gradio import processing_utils
|
||||
|
||||
@ -49,7 +49,7 @@ class Component:
|
||||
|
||||
def save_flagged_file(
|
||||
self, dir: str, label: str, data: Any, encryption_key: bool
|
||||
) -> str:
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Saved flagged data (e.g. image or audio) as a file and returns filepath
|
||||
"""
|
||||
|
@ -30,11 +30,14 @@ class InputComponent(Component):
|
||||
Input Component. All input components subclass this.
|
||||
"""
|
||||
|
||||
def __init__(self, label: str, requires_permissions: bool = False):
|
||||
def __init__(
|
||||
self, label: str, requires_permissions: bool = False, optional: bool = False
|
||||
):
|
||||
"""
|
||||
Constructs an input component.
|
||||
"""
|
||||
self.set_interpret_parameters()
|
||||
self.optional = optional
|
||||
super().__init__(label, requires_permissions)
|
||||
|
||||
def preprocess(self, x: Any) -> Any:
|
||||
@ -97,6 +100,12 @@ class InputComponent(Component):
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"optional": self.optional,
|
||||
**super().get_template_context(),
|
||||
}
|
||||
|
||||
|
||||
class Textbox(InputComponent):
|
||||
"""
|
||||
@ -248,16 +257,22 @@ class Number(InputComponent):
|
||||
Demos: tax_calculator, titanic_survival
|
||||
"""
|
||||
|
||||
def __init__(self, default: Optional[float] = None, label: Optional[str] = None):
|
||||
def __init__(
|
||||
self,
|
||||
default: Optional[float] = None,
|
||||
label: Optional[str] = None,
|
||||
optional: bool = False,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
default (float): default value.
|
||||
label (str): component name in interface.
|
||||
optional (bool): If True, the interface can be submitted with no value for this component.
|
||||
"""
|
||||
self.default = default
|
||||
self.test_input = default if default is not None else 1
|
||||
self.interpret_by_tokens = False
|
||||
super().__init__(label)
|
||||
super().__init__(label, optional=optional)
|
||||
|
||||
def get_template_context(self):
|
||||
return {"default": self.default, **super().get_template_context()}
|
||||
@ -268,13 +283,15 @@ class Number(InputComponent):
|
||||
"number": {},
|
||||
}
|
||||
|
||||
def preprocess(self, x: Number) -> float:
|
||||
def preprocess(self, x: Optional[Number]) -> Optional[float]:
|
||||
"""
|
||||
Parameters:
|
||||
x (number): numeric input
|
||||
x (string): numeric input as a string
|
||||
Returns:
|
||||
(float): number representing function input
|
||||
"""
|
||||
if self.optional and x is None:
|
||||
return None
|
||||
return float(x)
|
||||
|
||||
def preprocess_example(self, x: float) -> float:
|
||||
@ -445,7 +462,7 @@ class Checkbox(InputComponent):
|
||||
"checkbox": {},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: bool) -> bool:
|
||||
"""
|
||||
Parameters:
|
||||
x (bool): boolean input
|
||||
@ -519,7 +536,7 @@ class CheckboxGroup(InputComponent):
|
||||
**super().get_template_context(),
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: List[str]) -> List[str] | List[int]:
|
||||
"""
|
||||
Parameters:
|
||||
x (List[str]): list of selected choices
|
||||
@ -590,7 +607,7 @@ class Radio(InputComponent):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
choices: List(str),
|
||||
choices: List[str],
|
||||
type: str = "value",
|
||||
default: Optional[str] = None,
|
||||
label: Optional[str] = None,
|
||||
@ -616,7 +633,7 @@ class Radio(InputComponent):
|
||||
**super().get_template_context(),
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: str) -> str | int:
|
||||
"""
|
||||
Parameters:
|
||||
x (str): selected choice
|
||||
@ -692,7 +709,7 @@ class Dropdown(InputComponent):
|
||||
**super().get_template_context(),
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: str) -> str | int:
|
||||
"""
|
||||
Parameters:
|
||||
x (str): selected choice
|
||||
@ -768,11 +785,10 @@ class Image(InputComponent):
|
||||
requires_permissions = source == "webcam"
|
||||
self.tool = tool
|
||||
self.type = type
|
||||
self.optional = optional
|
||||
self.invert_colors = invert_colors
|
||||
self.test_input = test_data.BASE64_IMAGE
|
||||
self.interpret_by_tokens = True
|
||||
super().__init__(label, requires_permissions)
|
||||
super().__init__(label, requires_permissions, optional=optional)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
@ -797,12 +813,12 @@ class Image(InputComponent):
|
||||
**super().get_template_context(),
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: Optional[str]) -> np.array | PIL.Image | str | None:
|
||||
"""
|
||||
Parameters:
|
||||
x (str): base64 url data
|
||||
Returns:
|
||||
(Union[numpy.array, PIL.Image, file-object]): image in requested format
|
||||
(Union[numpy.array, PIL.Image, filepath]): image in requested format
|
||||
"""
|
||||
if x is None:
|
||||
return x
|
||||
@ -993,8 +1009,7 @@ class Video(InputComponent):
|
||||
"""
|
||||
self.type = type
|
||||
self.source = source
|
||||
self.optional = optional
|
||||
super().__init__(label)
|
||||
super().__init__(label, optional=optional)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
@ -1012,7 +1027,7 @@ class Video(InputComponent):
|
||||
def preprocess_example(self, x):
|
||||
return {"name": x, "data": None, "is_example": True}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: Dict[str, str] | None) -> str | None:
|
||||
"""
|
||||
Parameters:
|
||||
x (Dict[name: str, data: str]): JSON object with filename as 'name' property and base64 data as 'data' property
|
||||
@ -1081,10 +1096,9 @@ class Audio(InputComponent):
|
||||
self.source = source
|
||||
requires_permissions = source == "microphone"
|
||||
self.type = type
|
||||
self.optional = optional
|
||||
self.test_input = test_data.BASE64_AUDIO
|
||||
self.interpret_by_tokens = True
|
||||
super().__init__(label, requires_permissions)
|
||||
super().__init__(label, requires_permissions, optional=optional)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
@ -1104,12 +1118,12 @@ class Audio(InputComponent):
|
||||
def preprocess_example(self, x):
|
||||
return {"name": x, "data": None, "is_example": True}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: Dict[str, str] | None) -> Tuple[int, np.array] | str | None:
|
||||
"""
|
||||
Parameters:
|
||||
x (Dict[name: str, data: str]): JSON object with filename as 'name' property and base64 data as 'data' property
|
||||
Returns:
|
||||
(Union[Tuple[int, numpy.array], file-object, numpy.array]): audio in requested format
|
||||
(Union[Tuple[int, numpy.array], str, numpy.array]): audio in requested format
|
||||
"""
|
||||
if x is None:
|
||||
return x
|
||||
@ -1295,8 +1309,7 @@ class File(InputComponent):
|
||||
self.file_count = file_count
|
||||
self.type = type
|
||||
self.test_input = None
|
||||
self.optional = optional
|
||||
super().__init__(label)
|
||||
super().__init__(label, optional=optional)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
@ -1315,7 +1328,7 @@ class File(InputComponent):
|
||||
def preprocess_example(self, x):
|
||||
return {"name": x, "data": None, "is_example": True}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: List[Dict[str, str]] | None):
|
||||
"""
|
||||
Parameters:
|
||||
x (List[Dict[name: str, data: str]]): List of JSON objects with filename as 'name' property and base64 data as 'data' property
|
||||
@ -1445,7 +1458,7 @@ class Dataframe(InputComponent):
|
||||
"list": {"type": "array", "col_count": 1},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: List[List[str | Number | bool]]):
|
||||
"""
|
||||
Parameters:
|
||||
x (List[List[Union[str, number, bool]]]): 2D array of str, numeric, or bool data
|
||||
@ -1508,8 +1521,7 @@ class Timeseries(InputComponent):
|
||||
if isinstance(y, str):
|
||||
y = [y]
|
||||
self.y = y
|
||||
self.optional = optional
|
||||
super().__init__(label)
|
||||
super().__init__(label, optional=optional)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
@ -1528,7 +1540,7 @@ class Timeseries(InputComponent):
|
||||
def preprocess_example(self, x):
|
||||
return {"name": x, "is_example": True}
|
||||
|
||||
def preprocess(self, x):
|
||||
def preprocess(self, x: Dict | None) -> pd.DataFrame | None:
|
||||
"""
|
||||
Parameters:
|
||||
x (Dict[data: List[List[Union[str, number, bool]]], headers: List[str], range: List[number]]): Dict with keys 'data': 2D array of str, numeric, or bool data, 'headers': list of strings for header names, 'range': optional two element list designating start of end of subrange.
|
||||
|
@ -45,10 +45,10 @@
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script>
|
||||
<title>Gradio</title>
|
||||
<script type="module" crossorigin src="/assets/index.8bfab7fc.js"></script>
|
||||
<link rel="modulepreload" href="/assets/vendor.086ddd97.js">
|
||||
<script type="module" crossorigin src="/assets/index.5b58beaf.js"></script>
|
||||
<link rel="modulepreload" href="/assets/vendor.3cba5ef5.js">
|
||||
<link rel="stylesheet" href="/assets/vendor.327fceeb.css">
|
||||
<link rel="stylesheet" href="/assets/index.b7106307.css">
|
||||
<link rel="stylesheet" href="/assets/index.de6ac9a0.css">
|
||||
</head>
|
||||
|
||||
<body style="height: 100%; margin: 0; padding: 0">
|
||||
|
@ -3,7 +3,7 @@ if [ -z "$(ls | grep CONTRIBUTING.md)" ]; then
|
||||
echo "Please run the script from repo directory"
|
||||
exit -1
|
||||
else
|
||||
echo "Installing formatting with black and isort, also checking for standards with flake8"
|
||||
echo "Formatting backend and tests with black and isort, also checking for standards with flake8"
|
||||
python -m black gradio test
|
||||
python -m isort --profile=black gradio test
|
||||
python -m flake8 --ignore=E731,E501,E722,W503,E126,F401,E203 gradio test
|
||||
|
10
scripts/format_frontend.sh
Normal file
10
scripts/format_frontend.sh
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$(ls | grep CONTRIBUTING.md)" ]; then
|
||||
echo "Please run the script from repo directory"
|
||||
exit -1
|
||||
else
|
||||
echo "Formatting frontend with prettier, also type checking with TypeScript"
|
||||
cd ui
|
||||
pnpm format:write
|
||||
pnpm ts:check
|
||||
fi
|
@ -123,8 +123,9 @@ class TestTextbox(unittest.TestCase):
|
||||
|
||||
class TestNumber(unittest.TestCase):
|
||||
def test_as_component(self):
|
||||
numeric_input = gr.inputs.Number()
|
||||
numeric_input = gr.inputs.Number(optional=True)
|
||||
self.assertEqual(numeric_input.preprocess(3), 3.0)
|
||||
self.assertEqual(numeric_input.preprocess(None), None)
|
||||
self.assertEqual(numeric_input.preprocess_example(3), 3)
|
||||
self.assertEqual(numeric_input.serialize(3, True), 3)
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
@ -143,6 +144,10 @@ class TestNumber(unittest.TestCase):
|
||||
numeric_input.get_interpretation_neighbors(1),
|
||||
([0.97, 0.98, 0.99, 1.01, 1.02, 1.03], {}),
|
||||
)
|
||||
self.assertEqual(
|
||||
numeric_input.get_template_context(),
|
||||
{"default": None, "optional": True, "name": "number", "label": None},
|
||||
)
|
||||
|
||||
def test_in_interface(self):
|
||||
iface = gr.Interface(lambda x: x**2, "number", "textbox")
|
||||
@ -204,6 +209,7 @@ class TestSlider(unittest.TestCase):
|
||||
"step": 1,
|
||||
"default": 15,
|
||||
"name": "slider",
|
||||
"optional": False,
|
||||
"label": "Slide Your Input",
|
||||
},
|
||||
)
|
||||
@ -262,7 +268,12 @@ class TestCheckbox(unittest.TestCase):
|
||||
bool_input = gr.inputs.Checkbox(default=True, label="Check Your Input")
|
||||
self.assertEqual(
|
||||
bool_input.get_template_context(),
|
||||
{"default": True, "name": "checkbox", "label": "Check Your Input"},
|
||||
{
|
||||
"default": True,
|
||||
"name": "checkbox",
|
||||
"optional": False,
|
||||
"label": "Check Your Input",
|
||||
},
|
||||
)
|
||||
|
||||
def test_in_interface(self):
|
||||
@ -301,6 +312,7 @@ class TestCheckboxGroup(unittest.TestCase):
|
||||
{
|
||||
"choices": ["a", "b", "c"],
|
||||
"default": ["a", "c"],
|
||||
"optional": False,
|
||||
"name": "checkboxgroup",
|
||||
"label": "Check Your Inputs",
|
||||
},
|
||||
@ -349,6 +361,7 @@ class TestRadio(unittest.TestCase):
|
||||
"default": "a",
|
||||
"name": "radio",
|
||||
"label": "Pick Your One Input",
|
||||
"optional": False,
|
||||
},
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
@ -393,6 +406,7 @@ class TestDropdown(unittest.TestCase):
|
||||
"default": "a",
|
||||
"name": "dropdown",
|
||||
"label": "Drop Your Input",
|
||||
"optional": False,
|
||||
},
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
@ -651,6 +665,7 @@ class TestDataframe(unittest.TestCase):
|
||||
"default": [[None, None, None], [None, None, None], [None, None, None]],
|
||||
"name": "dataframe",
|
||||
"label": "Dataframe Input",
|
||||
"optional": False,
|
||||
},
|
||||
)
|
||||
dataframe_input = gr.inputs.Dataframe()
|
||||
|
@ -200,7 +200,10 @@
|
||||
{#each input_components as input_component, i}
|
||||
{#if input_component.name !== "state"}
|
||||
<div class="component" key={i}>
|
||||
<div class="panel-header mb-1.5">{input_component.label}</div>
|
||||
<div class="panel-header mb-1.5">
|
||||
{input_component.label}{#if input_component.optional}
|
||||
<em>(optional)</em>{/if}
|
||||
</div>
|
||||
<svelte:component
|
||||
this={input_component_map[input_component.name][
|
||||
interpret_mode ? "interpretation" : "component"
|
||||
|
Loading…
Reference in New Issue
Block a user