Repository: gradio-app/gradio

Issue:
Setting value of CheckboxGroup to an integer causes an infinite loading loop.
### Describe the bug

Hello,

Setting `value`  of a `ChecboxGroup` to an integer prevents the demo from loading.

That's all!

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Reproduction

Minimal code to reproduce

```
import gradio as gr

def dummy(values):
    return "OK"

demo = gr.Interface(
    dummy, [gr.CheckboxGroup(["checkbox 1", "checkbox 2", "checkbox 3"], value=0)], "text"
)

if __name__ == "__main__":
    demo.launch()
```

### Screenshot

![loading](https://user-images.githubusercontent.com/588431/198682860-e9d09b93-56b2-40a9-bddb-5e2536855a96.png)


### Logs

```shell
There is no crash, so I am ignoring this one.
```


### System Info

```shell
Gradio: `3.7`
Windows: Version 10.0.19044.2130
Happens on both Firefox and Chrome
```


### Severity

annoying


Discussion:
Hi @taesiri ! 

Thanks for filing.

The problem is that `value` has to be a list of strings and each string has to be a a valid possible choice. I think this should fix your problem:

```python
import gradio as gr

def dummy(values):
    return "OK"

demo = gr.Interface(
    dummy, [gr.CheckboxGroup(["checkbox 1", "checkbox 2", "checkbox 3"], value=["checkbox 1"])], "text"
)

if __name__ == "__main__":
    demo.launch()
The front-end crashes with 
```
Uncaught (in promise) TypeError: t[0].includes is not a function
    at Object.c (CheckboxGroup.svelte:40:20)
    at Object.c (CheckboxGroup.svelte:32:4)
    at Nt (index.f3976d0d.js:4:5230)
    at Object.c (CheckboxGroup.svelte:35:22)
    at Object.c (Block.svelte:44:8)
    at Object.c (Block.svelte:44:8)
    at Nt (index.f3976d0d.js:4:5230)
    at Object.c (CheckboxGroup.svelte:24:58)
    at Nt (index.f3976d0d.js:4:5230)
    at Object.c (index.f3976d0d.js:34:30855)
```

But I think a more helpful error should be raised prior to the demo launching.
Thank you for your help @freddyaboulton 

My expectation is that the demo should load fine without crashing or looping. Keep in mind that this will not happen if the `value` is set to a string, which is **not** among the options.

Resolving patch:
diff --git a/gradio/components.py b/gradio/components.py
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -899,7 +899,7 @@ def __init__(
         self,
         choices: Optional[List[str]] = None,
         *,
-        value: List[str] | Callable = None,
+        value: List[str] | str | Callable = None,
         type: str = "value",
         label: Optional[str] = None,
         every: float | None = None,
@@ -952,7 +952,7 @@ def get_config(self):
 
     @staticmethod
     def update(
-        value: Optional[List[str]] = _Keywords.NO_VALUE,
+        value: Optional[List[str] | str] = _Keywords.NO_VALUE,
         choices: Optional[List[str]] = None,
         label: Optional[str] = None,
         show_label: Optional[bool] = None,
@@ -991,15 +991,19 @@ def preprocess(self, x: List[str]) -> List[str] | List[int]:
                 + ". Please choose from: 'value', 'index'."
             )
 
-    def postprocess(self, y: List[str] | None) -> List[str]:
+    def postprocess(self, y: List[str] | str | None) -> List[str]:
         """
         Any postprocessing needed to be performed on function output.
         Parameters:
-            y: List of selected choices
+            y: List of selected choices. If a single choice is selected, it can be passed in as a string
         Returns:
             List of selected choices
         """
-        return [] if y is None else y
+        if y is None:
+            return []
+        if not isinstance(y, list):
+            y = [y]
+        return y
 
     def set_interpret_parameters(self):
         """
@@ -4834,6 +4838,7 @@ def __init__(
         samples: List[List[Any]] = None,
         headers: Optional[List[str]] = None,
         type: str = "values",
+        samples_per_page: int = 10,
         visible: bool = True,
         elem_id: Optional[str] = None,
         **kwargs,
@@ -4844,6 +4849,7 @@ def __init__(
             samples: a nested list of samples. Each sublist within the outer list represents a data sample, and each element within the sublist represents an value for each component
             headers: Column headers in the Dataset widget, should be the same len as components. If not provided, inferred from component labels
             type: 'values' if clicking on a sample should pass the value of the sample, or "index" if it should pass the index of the sample
+            samples_per_page: how many examples to show per page.
             visible: If False, component will be hidden.
             elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
         """
@@ -4861,6 +4867,7 @@ def __init__(
             self.headers = []
         else:
             self.headers = [c.label or "" for c in self.components]
+        self.samples_per_page = samples_per_page
 
     def get_config(self):
         return {
@@ -4869,6 +4876,7 @@ def get_config(self):
             "samples": self.samples,
             "type": self.type,
             "label": self.label,
+            "samples_per_page": self.samples_per_page,
             **Component.get_config(self),
         }
 
diff --git a/gradio/examples.py b/gradio/examples.py
--- a/gradio/examples.py
+++ b/gradio/examples.py
@@ -97,7 +97,7 @@ def __init__(
             outputs: optionally, provide the component or list of components corresponding to the output of the examples. Required if `cache` is True.
             fn: optionally, provide the function to run to generate the outputs corresponding to the examples. Required if `cache` is True.
             cache_examples: if True, caches examples for fast runtime. If True, then `fn` and `outputs` need to be provided
-            examples_per_page: how many examples to show per page (this parameter currently has no effect)
+            examples_per_page: how many examples to show per page.
             label: the label to use for the examples component (by default, "Examples")
             elem_id: an optional string that is assigned as the id of this component in the HTML DOM.
             run_on_click: if cache_examples is False, clicking on an example does not run the function when an example is clicked. Set this to True to run the function when an example is clicked. Has no effect if cache_examples is True.
@@ -182,7 +182,6 @@ def __init__(
         self.outputs = outputs
         self.fn = fn
         self.cache_examples = cache_examples
-        self.examples_per_page = examples_per_page
         self._api_mode = _api_mode
         self.preprocess = preprocess
         self.postprocess = postprocess
@@ -218,6 +217,7 @@ def __init__(
                 samples=non_none_examples,
                 type="index",
                 label=label,
+                samples_per_page=examples_per_page,
                 elem_id=elem_id,
             )
 


Test patch:
diff --git a/test/test_components.py b/test/test_components.py
--- a/test/test_components.py
+++ b/test/test_components.py
@@ -493,6 +493,10 @@ def test_component_functions(self):
         with pytest.raises(ValueError):
             gr.CheckboxGroup(["a"], type="unknown")
 
+        cbox = gr.CheckboxGroup(choices=["a", "b"], value="c")
+        assert cbox.get_config()["value"] == ["c"]
+        assert cbox.postprocess("a") == ["a"]
+
     def test_in_interface(self):
         """
         Interface, process
diff --git a/test/test_examples.py b/test/test_examples.py
--- a/test/test_examples.py
+++ b/test/test_examples.py
@@ -45,6 +45,10 @@ def test_handle_directory_with_log_file(self):
         for sample in examples.dataset.samples:
             assert os.path.isabs(sample[0])
 
+    def test_examples_per_page(self):
+        examples = gr.Examples(["hello", "hi"], gr.Textbox(), examples_per_page=2)
+        assert examples.dataset.get_config()["samples_per_page"] == 2
+
     @pytest.mark.asyncio
     async def test_no_preprocessing(self):
         with gr.Blocks():
diff --git a/test/test_interfaces.py b/test/test_interfaces.py
--- a/test/test_interfaces.py
+++ b/test/test_interfaces.py
@@ -132,10 +132,13 @@ def test_interface_browser(self, mock_browser):
 
     def test_examples_list(self):
         examples = ["test1", "test2"]
-        interface = Interface(lambda x: x, "textbox", "label", examples=examples)
+        interface = Interface(
+            lambda x: x, "textbox", "label", examples=examples, examples_per_page=2
+        )
         interface.launch(prevent_thread_lock=True)
         assert len(interface.examples_handler.examples) == 2
         assert len(interface.examples_handler.examples[0]) == 1
+        assert interface.examples_handler.dataset.get_config()["samples_per_page"] == 2
         interface.close()
 
     @mock.patch("IPython.display.display")