Repository: gradio-app/gradio
Pull request: Fix bug where functions with type hints caused errors
# Description

Fixes #3514

End to end test
```python
import gradio as gr
from typing import Literal

def calculator(num1,
               operation: Literal["add", "subtract", "multiply", "divide"],
               num2):
    if operation == "add":
        return num1 + num2
    elif operation == "subtract":
        return num1 - num2
    elif operation == "multiply":
        return num1 * num2
    elif operation == "divide":
        if num2 == 0:
            raise gr.Error("Cannot divide by zero!")
        return num1 / num2

demo = gr.Interface(
    calculator,
    [
        "number", 
        gr.Radio(["add", "subtract", "multiply", "divide"]),
        "number"
    ],
    "number",
    examples=[
        [5, "add", 3],
        [4, "divide", 2],
        [-4, "multiply", 2.5],
        [0, "subtract", 1.2],
    ],
    title="Toy Calculator",
    description="Here's a sample toy calculator. Allows you to calculate things like $2+2=4$",
)
if __name__ == "__main__":
    demo.launch()
```


# Checklist:

- [ ] I have performed a self-review of my own code
- [ ] I have added a short summary of my change to the CHANGELOG.md
- [ ] My code follows the style guidelines of this project
- [ ] I have commented my code in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes


# A note about the CHANGELOG

Hello 👋 and thank you for contributing to Gradio!

All pull requests must update the change log located in CHANGELOG.md, unless the pull request is labeled with the "no-changelog-update" label.

Please add a brief summary of the change to the Upcoming Release > Full Changelog section of the CHANGELOG.md file and include
a link to the PR (formatted in markdown) and a link to your github profile (if you like). For example, "* Added a cool new feature by `[@myusername](link-to-your-github-profile)` in `[PR 11111](https://github.com/gradio-app/gradio/pull/11111)`".

If you would like to elaborate on your change further, feel free to include a longer explanation in the other sections.
If you would like an image/gif/video showcasing your feature, it may be best to edit the CHANGELOG file using the 
GitHub web UI since that lets you upload files directly via drag-and-drop.

Problem statement:
"issubclass() arg 1 must be a class" error appears when using input parameters with Optional type hint
### Describe the bug

I'm getting an error `issubclass() arg 1 must be a class` when using Optional typing in the target function. The problem started appearing with version `3.21.0`

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Reproduction

Steps to reproduce:
1. Install gradio `3.21.0`
2. Target function with the following type hints:
```
def transcribe(
        language: Optional[str] = 'auto',
):
```
3. Run gradio application

AR:
```
  File ".../app.py", line 67, in <module>
    allow_flagging='never',
  File ".../.venv/lib/python3.7/site-packages/gradio/interface.py", line 463, in __init__
    self.attach_submit_events(submit_btn, stop_btn)
  File ".../.venv/lib/python3.7/site-packages/gradio/interface.py", line 624, in attach_submit_events
    postprocess=not (self.api_mode),
  File ".../.venv/lib/python3.7/site-packages/gradio/events.py", line 147, in __call__
    trigger_only_on_success=self.trigger_only_on_success,
  File ".../.venv/lib/python3.7/site-packages/gradio/blocks.py", line 190, in set_event_trigger
    check_function_inputs_match(fn, inputs, inputs_as_dict)
  File ".../.venv/lib/python3.7/site-packages/gradio/utils.py", line 943, in check_function_inputs_match
    if not is_special_typed_parameter(name):
  File ".../.venv/lib/python3.7/site-packages/gradio/utils.py", line 932, in is_special_typed_parameter
    is_event_data = issubclass(parameter_types.get(name, int), EventData)
TypeError: issubclass() arg 1 must be a class
```

The problem is that the first parameter for `issubclass` is `typing.Union[str, NoneType]`

### Screenshot

_No response_

### Logs

_Stacktrace is attached in the reproduction steps._


### System Info

```shell
Gradio 3.21.0, Python 3.7
```


### Severity

serious, but I can work around it by removing annotations

Hints from issue discussion:
@hnsywangxin , I didn't try to downgrade the version. The simpler way was to remove type annotations. 
> @hnsywangxin , I didn't try to downgrade the version. The simpler way was to remove type annotations.

Same, removing the annotations solved.
I'm having the same problem. It is a more generic problem than you described.

For me it happens with the following code:

```
def from_raw(raw_pdf: Union[str, bytes]) -> str:
...

pdf_file.upload(from_raw, inputs=[file], outputs=[text])
```

It seems like gradio events can not handle type annotations with the `fn` arguments.

This should be fixed.
This issue has not been resolved. On Gradio 3.28 it still occurs, and removing the type annotations still seems to be the only resolution. 
annoying
thanks for your advice, removing type annotations fixed it for me too
Will take a look and get a fix out this week!

Commit message: CHANGELOG

Patch to review:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ec16b9e74..10df28d35c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ No changes to highlight.
 
 ## Bug Fixes:
 
-No changes to highlight.
+- Fixed bug where type hints in functions caused the event handler to crash by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 4068](https://github.com/gradio-app/gradio/pull/4068)
 
 ## Documentation Changes:
 
diff --git a/gradio/utils.py b/gradio/utils.py
index cc35d1ce35..29d87c4000 100644
--- a/gradio/utils.py
+++ b/gradio/utils.py
@@ -41,12 +41,12 @@
 import matplotlib
 import requests
 from markdown_it import MarkdownIt
-from pygments import highlight
-from pygments.formatters import HtmlFormatter
-from pygments.lexers import get_lexer_by_name
 from mdit_py_plugins.dollarmath.index import dollarmath_plugin
 from mdit_py_plugins.footnote.index import footnote_plugin
 from pydantic import BaseModel, parse_obj_as
+from pygments import highlight
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import get_lexer_by_name
 
 import gradio
 from gradio.context import Context
@@ -845,22 +845,24 @@ def get_type_hints(fn):
     return {}
 
 
+def is_special_typed_parameter(name, parameter_types):
+    from gradio.helpers import EventData
+    from gradio.routes import Request
+
+    """Checks if parameter has a type hint designating it as a gr.Request or gr.EventData"""
+    is_request = parameter_types.get(name, "") == Request
+    # use int in the fall-back as that will always be false
+    hint = parameter_types.get(name, int)
+    is_event_data = inspect.isclass(hint) and issubclass(hint, EventData)
+    return is_request or is_event_data
+
+
 def check_function_inputs_match(fn: Callable, inputs: List, inputs_as_dict: bool):
     """
     Checks if the input component set matches the function
     Returns: None if valid, a string error message if mismatch
     """
 
-    def is_special_typed_parameter(name):
-        from gradio.helpers import EventData
-        from gradio.routes import Request
-
-        """Checks if parameter has a type hint designating it as a gr.Request or gr.EventData"""
-        is_request = parameter_types.get(name, "") == Request
-        # use int in the fall-back as that will always be false
-        is_event_data = issubclass(parameter_types.get(name, int), EventData)
-        return is_request or is_event_data
-
     signature = inspect.signature(fn)
     parameter_types = get_type_hints(fn)
     min_args = 0
@@ -869,7 +871,7 @@ def is_special_typed_parameter(name):
     for name, param in signature.parameters.items():
         has_default = param.default != param.empty
         if param.kind in [param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD]:
-            if not is_special_typed_parameter(name):
+            if not is_special_typed_parameter(name, parameter_types):
                 if not has_default:
                     min_args += 1
                 max_args += 1
diff --git a/test/test_utils.py b/test/test_utils.py
index 984b0f6773..21612311a0 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -5,6 +5,7 @@
 import sys
 import unittest.mock as mock
 import warnings
+from typing import List
 from unittest.mock import MagicMock
 
 import pytest
@@ -14,7 +15,7 @@
 from pydantic import BaseModel
 from typing_extensions import Literal
 
-from gradio import EventData
+from gradio import EventData, Request
 from gradio.context import Context
 from gradio.test_data.blocks_configs import (
     XRAY_CONFIG,
@@ -34,6 +35,7 @@
     get_local_ip_address,
     get_type_hints,
     ipython_check,
+    is_special_typed_parameter,
     kaggle_check,
     launch_analytics,
     readme_to_html,
@@ -633,6 +635,16 @@ class GenericObject:
 
         assert len(get_type_hints(GenericObject())) == 0
 
+    def test_is_special_typed_parameter(self):
+        def func(a: List[str], b: Literal["a", "b"], c, d: Request):
+            pass
+
+        hints = get_type_hints(func)
+        assert not is_special_typed_parameter("a", hints)
+        assert not is_special_typed_parameter("a", hints)
+        assert not is_special_typed_parameter("a", hints)
+        assert not is_special_typed_parameter("a", hints)
+
 
 class TestCheckFunctionInputsMatch:
     def test_check_function_inputs_match(self):


Code review comments:
diff_hunk: @@ -633,6 +635,16 @@ class GenericObject:
 
         assert len(get_type_hints(GenericObject())) == 0
 
+    def test_is_special_typed_parameter(self):
+        def func(a: List[str], b: Literal["a", "b"], c, d: Request):
+            pass
+
+        hints = get_type_hints(func)
+        assert not is_special_typed_parameter("a", hints)
+        assert not is_special_typed_parameter("a", hints)
+        assert not is_special_typed_parameter("a", hints)
+        assert not is_special_typed_parameter("a", hints)
path: test/test_utils.py
text: @user1:
I believe this was intended? 
```suggestion
        assert not is_special_typed_parameter("a", hints)
        assert not is_special_typed_parameter("b", hints)
        assert not is_special_typed_parameter("c", hints)
        assert is_special_typed_parameter("d", hints)
```

@author:
Lol yes 🙈