Repository: vitalik/django-ninja

Issue:
[BUG] Declaring path param as any kind of instance crashes (at `signature.details.is_collection_type`)
Declaring a path with parameters from e.g. `typing.NewType` raises a `TypingError` from `issubclass`. See relevant traceback below.

```
  File "/app/venv/lib/python3.10/site-packages/ninja/router.py", line 238, in decorator
    self.add_api_operation(
  File "/app/venv/lib/python3.10/site-packages/ninja/router.py", line 285, in add_api_operation
    path_view.add_operation(
  File "/app/venv/lib/python3.10/site-packages/ninja/operation.py", line 289, in add_operation
    operation = OperationClass(
  File "/app/venv/lib/python3.10/site-packages/ninja/operation.py", line 240, in __init__
    super().__init__(*args, **kwargs)
  File "/app/venv/lib/python3.10/site-packages/ninja/operation.py", line 65, in __init__
    self.signature = ViewSignature(self.path, self.view_func)
  File "/app/venv/lib/python3.10/site-packages/ninja/signature/details.py", line 57, in __init__
    func_param = self._get_param_type(name, arg)
  File "/app/venv/lib/python3.10/site-packages/ninja/signature/details.py", line 202, in _get_param_type
    is_collection = is_collection_type(annotation)
  File "/app/venv/lib/python3.10/site-packages/ninja/signature/details.py", line 255, in is_collection_type
    return issubclass(annotation, types)
  File "/usr/local/lib/python3.10/typing.py", line 1157, in __subclasscheck__
    return issubclass(cls, self.__origin__)
TypeError: issubclass() arg 1 must be a class
```

For background, I'm trying to use a custom Django path converter. Which feeds a complex type (based on `pydantic.BaseModel`) that has attributes typed via `typing.NewType`.

In any case, here's a minimal repro:

```python
from typing import NewType

from django.http import HttpRequest, HttpResponse
from ninja import NinjaAPI

Custom = NewType("Custom", str)
api = NinjaAPI()

@api.get("/endpoint/{custom:value}/")
def endpoint(request: HttpRequest, value: Custom) -> HttpResponse:
    ...
```

**Versions:**
 - Python version: `3.10.4`
 - Django version: `4.0.4`
 - Django-Ninja version: `0.17.0`


Discussion:

Patch:
diff --git a/ninja/signature/details.py b/ninja/signature/details.py
index 8ef6d56..3e91f8d 100644
--- a/ninja/signature/details.py
+++ b/ninja/signature/details.py
@@ -252,7 +252,11 @@ def is_collection_type(annotation: Any) -> bool:
     origin = get_collection_origin(annotation)
     types = (List, list, set, tuple)
     if origin is None:
-        return issubclass(annotation, types)
+        return (
+            isinstance(annotation, types)
+            if not isinstance(annotation, type)
+            else issubclass(annotation, types)
+        )
     else:
         return origin in types  # TODO: I guess we should handle only list
 


Test patch:
diff --git a/tests/test_signature_details.py b/tests/test_signature_details.py
new file mode 100644
index 0000000..c48f84f
--- /dev/null
+++ b/tests/test_signature_details.py
@@ -0,0 +1,48 @@
+import typing
+from sys import version_info
+
+import pytest
+
+from ninja.signature.details import is_collection_type
+
+
+@pytest.mark.parametrize(
+    ("annotation", "expected"),
+    [
+        pytest.param(typing.List, True, id="true_for_typing_List"),
+        pytest.param(list, True, id="true_for_native_list"),
+        pytest.param(typing.Set, True, id="true_for_typing_Set"),
+        pytest.param(set, True, id="true_for_native_set"),
+        pytest.param(typing.Tuple, True, id="true_for_typing_Tuple"),
+        pytest.param(tuple, True, id="true_for_native_tuple"),
+        pytest.param(
+            type("Custom", (), {}),
+            False,
+            id="false_for_custom_type_without_typing_origin",
+        ),
+        pytest.param(
+            object(), False, id="false_for_custom_instance_without_typing_origin"
+        ),
+        pytest.param(
+            typing.NewType("SomethingNew", str),
+            False,
+            id="false_for_instance_without_typing_origin",
+        ),
+        # Can't mark with `pytest.mark.skipif` since we'd attempt to instantiate the
+        # parameterized value/type(e.g. `list[int]`). Which only works with Python >= 3.9)
+        *(
+            (
+                pytest.param(list[int], True, id="true_for_parameterized_native_list"),
+                pytest.param(set[int], True, id="true_for_parameterized_native_set"),
+                pytest.param(
+                    tuple[int], True, id="true_for_parameterized_native_tuple"
+                ),
+            )
+            # TODO: Remove conditional once support for <=3.8 is dropped
+            if version_info >= (3, 9)
+            else ()
+        ),
+    ],
+)
+def test_is_collection_type_returns(annotation: typing.Any, expected: bool):
+    assert is_collection_type(annotation) is expected