Repository: gradio-app/gradio
Pull request: Miscellaneous warnings and errors 
Just a few improvements to our warnings and errors:

* Raises a warning in the `File` component or `UploadButton` component if someone passes in a `file_types` parameter along with `file_count=="dir"` (closes: #3077) 
* Raises a `gr.Error` instead of a regular Python error when you use `gr.Interface.load()` to load a model and there's an error querying the HF API to make it more easily readable (closes: #3194), looks like this:

<img width="689" alt="image" src="https://user-images.githubusercontent.com/1778297/218831897-2b9ef6d9-28d8-45d2-a81e-375f9627ef3d.png">

* Improves error messages when returning incorrect number of values from an event handler (closes: #3082)

Problem statement:
Filter out files based on file_types when choosing the `directory` option
- [x] I have searched to see if a similar issue already exists.

Currently `gr.File(file_count="multiple", file_types=["image"])` checks if the file is an image before uploading. I was hoping for a similar behavior for `gr.File(file_count="directory", file_types=["image"])` where using the `file_types` option with `directory` should selective upload files with matching types. 

With the current implementation, mentioning `file_types` with `file_count=directory` doesn't have effect.

Improve error messages when returning incorrect number of values from an event handler
- [x] I have searched to see if a similar issue already exists.


**Is your feature request related to a problem? Please describe.**  
I have a lot of event handlers with a large number of values returned. If something goes wrong, like if I send the wrong number of return values, I get this error

```
Traceback (most recent call last):
  File "C:\build\stable-diffusion-webui\venv\lib\site-packages\gradio\routes.py", line 337, in run_predict
    output = await app.get_blocks().process_api(
  File "C:\build\stable-diffusion-webui\venv\lib\site-packages\gradio\blocks.py", line 1018, in process_api
    data = self.postprocess_data(fn_index, result["prediction"], state)
  File "C:\build\stable-diffusion-webui\venv\lib\site-packages\gradio\blocks.py", line 935, in postprocess_data
    if predictions[i] is components._Keywords.FINISHED_ITERATING:
IndexError: tuple index out of range
```

Now from trial and error I know the issue stems from a code mistake in an event handler, but I have no idea where it is because the traceback isn't placed within my code. There isn't any context or other things to help me locate the error, just the (also unclear) implication that there's a faulty event handler somewhere

**Describe the solution you'd like**  
It would at least be helpful to know the function name and the number of arguments it expects. Maybe printing out what the arguments are with `pprint` would be useful too.

**Additional context**  
N/A

Hints from issue discussion:
To be sure, `gr.File(file_count="multiple", file_types=["image"])` works as expected though, right? It only let's you upload images.
> To be sure, `gr.File(file_count="multiple", file_types=["image"])` works as expected though, right? It only let's you upload images.

Yes, it works as expected.
It feels quite niche to me to support filtering file types when a directory is uploaded. Perhaps we should just have a warning displayed if someone uses `file_types` with file_count="directory". @dawoodkhan82 @freddyaboulton thoughts?

Commit message: formatting

Patch to review:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 534ceb6561..463ba67cd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,8 @@ No changes to highlight.
 
 ## Full Changelog:
 * Fix demos page css and add close demos button by [@aliabd](https://github.com/aliabd) in [PR 3151](https://github.com/gradio-app/gradio/pull/3151)
+* Better warnings and error messages (e.g. when there is a mismatch between the number
+of output components and values returned by a function) by [@abidlabs](https://github.com/abidlabs) in [PR 3194](https://github.com/gradio-app/gradio/pull/3194) 
 
 ## Contributors Shoutout:
 No changes to highlight.
diff --git a/gradio/blocks.py b/gradio/blocks.py
index 4aae0f3aea..1986e7c766 100644
--- a/gradio/blocks.py
+++ b/gradio/blocks.py
@@ -334,13 +334,12 @@ def __init__(
         self.total_runtime = 0
         self.total_runs = 0
         self.inputs_as_dict = inputs_as_dict
+        self.name = getattr(fn, "__name__", "fn") if fn is not None else None
 
     def __str__(self):
         return str(
             {
-                "fn": getattr(self.fn, "__name__", "fn")
-                if self.fn is not None
-                else None,
+                "fn": self.name,
                 "preprocess": self.preprocess,
                 "postprocess": self.postprocess,
             }
@@ -934,9 +933,14 @@ def postprocess_data(
 
         output = []
         for i, output_id in enumerate(dependency["outputs"]):
-            if predictions[i] is components._Keywords.FINISHED_ITERATING:
-                output.append(None)
-                continue
+            try:
+                if predictions[i] is components._Keywords.FINISHED_ITERATING:
+                    output.append(None)
+                    continue
+            except (IndexError, KeyError):
+                raise ValueError(
+                    f"Number of output components does not match number of values returned from from function {block_fn.name}"
+                )
             block = self.blocks[output_id]
             if getattr(block, "stateful", False):
                 if not utils.is_update(predictions[i]):
@@ -957,6 +961,7 @@ def postprocess_data(
                     ), f"{block.__class__} Component with id {output_id} not a valid output component."
                     prediction_value = block.postprocess(prediction_value)
                 output.append(prediction_value)
+
         return output
 
     async def process_api(
diff --git a/gradio/components.py b/gradio/components.py
index f4df02a7a4..c3ff6e7e38 100644
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -2273,6 +2273,10 @@ def __init__(
             warnings.warn(
                 "The `bytes` type is deprecated and may not work as expected. Please use `binary` instead."
             )
+        if file_count == "directory" and file_types is not None:
+            warnings.warn(
+                "The `file_types` parameter is ignored when `file_count` is 'directory'."
+            )
         self.type = type
         self.test_input = None
         TempFileManager.__init__(self)
@@ -2992,6 +2996,10 @@ def __init__(
         """
         self.type = type
         self.file_count = file_count
+        if file_count == "directory" and file_types is not None:
+            warnings.warn(
+                "The `file_types` parameter is ignored when `file_count` is 'directory'."
+            )
         if file_types is not None and not isinstance(file_types, list):
             raise ValueError(
                 f"Parameter file_types must be a list. Received {file_types.__class__.__name__}"
diff --git a/gradio/external.py b/gradio/external.py
index 97618dba5d..8e66864709 100644
--- a/gradio/external.py
+++ b/gradio/external.py
@@ -15,7 +15,7 @@
 import gradio
 from gradio import components, utils
 from gradio.context import Context
-from gradio.exceptions import TooManyRequestsError
+from gradio.exceptions import Error, TooManyRequestsError
 from gradio.external_utils import (
     cols_to_rows,
     encode_to_base64,
@@ -328,7 +328,7 @@ def query_huggingface_api(*params):
                 errors = f", Error: {errors_json.get('error')}"
             if errors_json.get("warnings"):
                 warns = f", Warnings: {errors_json.get('warnings')}"
-            raise ValueError(
+            raise Error(
                 f"Could not complete request to HuggingFace API, Status Code: {response.status_code}"
                 + errors
                 + warns
diff --git a/test/test_blocks.py b/test/test_blocks.py
index 0ea12e916a..b20d761a71 100644
--- a/test/test_blocks.py
+++ b/test/test_blocks.py
@@ -414,6 +414,8 @@ def test_get_load_events(self, io_components):
                 components.append(component(value=lambda: None, every=1))
         assert [comp.load_event for comp in components] == demo.dependencies
 
+
+class TestBlocksPostprocessing:
     def test_blocks_do_not_filter_none_values_from_updates(self, io_components):
         io_components = [
             c()
@@ -531,6 +533,15 @@ def generic_update():
             }
             assert output["data"][1] == {"__type__": "update", "mode": "dynamic"}
 
+    def test_error_raised_if_num_outputs_mismatch(self):
+        with gr.Blocks() as demo:
+            textbox1 = gr.Textbox()
+            textbox2 = gr.Textbox()
+            button = gr.Button()
+            button.click(lambda x: x, textbox1, [textbox1, textbox2])
+        with pytest.raises(ValueError):
+            demo.postprocess_data(fn_index=0, predictions=["test"], state={})
+
 
 class TestCallFunction:
     @pytest.mark.asyncio


Code review comments:
diff_hunk: @@ -531,6 +533,15 @@ def generic_update():
             }
             assert output["data"][1] == {"__type__": "update", "mode": "dynamic"}
 
+    def test_error_raised_if_num_outputs_mismatch(self):
+        with gr.Blocks() as demo:
+            textbox1 = gr.Textbox()
+            textbox2 = gr.Textbox()
+            button = gr.Button()
+            button.click(lambda x: x, textbox1, [textbox1, textbox2])
+        with pytest.raises(ValueError):
path: test/test_blocks.py
text: @user1:
nit: I would `match=` against the expected error message here but maybe I'm just too paranoid 😂