Repository: strawberry-graphql/strawberry

Issue:
Subclassing `Info`
Subclassing `strawberry.types.Info` raises a warning when using it in resolvers.
`"Argument name-based matching of 'info' is deprecated and will be removed in v1.0."`
But it gets injected into resolvers without any problems, in my use case i only change `Info.context` to provide my custom `Context` class and original `Info` gets injected: `strawberry.types.info.Info`

## System Information

 - Strawberry version: 0.126.2

## Additional Context

Repository with bug reproduction: https://gitlab.com/ThirVondukr/strawberry-info-warning
Strawberry code where warning originates from: https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/types/fields/resolver.py#L126

Subclassing `Info`
Subclassing `strawberry.types.Info` raises a warning when using it in resolvers.
`"Argument name-based matching of 'info' is deprecated and will be removed in v1.0."`
But it gets injected into resolvers without any problems, in my use case i only change `Info.context` to provide my custom `Context` class and original `Info` gets injected: `strawberry.types.info.Info`

## System Information

 - Strawberry version: 0.126.2

## Additional Context

Repository with bug reproduction: https://gitlab.com/ThirVondukr/strawberry-info-warning
Strawberry code where warning originates from: https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/types/fields/resolver.py#L126



Discussion:
Thanks for the bug report @ThirVondukr , this is byproduct of what I introduced in #1713. I'm having a look now to be able to support sub-classed reserved types 😊
Thanks for the bug report @ThirVondukr , this is byproduct of what I introduced in #1713. I'm having a look now to be able to support sub-classed reserved types 😊

Resolving patch:
diff --git a/strawberry/types/fields/resolver.py b/strawberry/types/fields/resolver.py
--- a/strawberry/types/fields/resolver.py
+++ b/strawberry/types/fields/resolver.py
@@ -20,6 +20,7 @@
     TypeVar,
     Union,
     _eval_type,
+    cast,
 )
 
 from typing_extensions import Annotated, Protocol, get_args, get_origin
@@ -129,12 +130,17 @@ def find(
             return None
 
     def is_reserved_type(self, other: Type) -> bool:
-        if get_origin(other) is Annotated:
+        origin = cast(type, get_origin(other)) or other
+        if origin is Annotated:
             # Handle annotated arguments such as Private[str] and DirectiveValue[str]
             return any(isinstance(argument, self.type) for argument in get_args(other))
         else:
             # Handle both concrete and generic types (i.e Info, and Info[Any, Any])
-            return other is self.type or get_origin(other) is self.type
+            return (
+                issubclass(origin, self.type)
+                if isinstance(origin, type)
+                else origin is self.type
+            )
 
 
 SELF_PARAMSPEC = ReservedNameBoundParameter("self")


Test patch:
diff --git a/tests/types/test_argument_types.py b/tests/types/test_argument_types.py
--- a/tests/types/test_argument_types.py
+++ b/tests/types/test_argument_types.py
@@ -1,7 +1,11 @@
+import warnings
 from enum import Enum
-from typing import List, Optional, TypeVar
+from typing import Any, List, Optional, TypeVar
+
+import pytest
 
 import strawberry
+from strawberry.types.info import Info
 
 
 def test_enum():
@@ -93,3 +97,52 @@ def set_value(value: T) -> bool:
 
     argument = set_value.arguments[0]
     assert argument.type == T
+
+
+ContextType = TypeVar("ContextType")
+RootValueType = TypeVar("RootValueType")
+
+
+class CustomInfo(Info[ContextType, RootValueType]):
+    """Subclassed Info type used to test dependency injection."""
+
+
+@pytest.mark.parametrize(
+    "annotation",
+    [CustomInfo, CustomInfo[Any, Any], Info, Info[Any, Any]],
+)
+def test_custom_info(annotation):
+    """Test to ensure that subclassed Info does not raise warning."""
+    with warnings.catch_warnings():
+        warnings.filterwarnings("error")
+
+        def get_info(info) -> bool:
+            _ = info
+            return True
+
+        get_info.__annotations__["info"] = annotation
+        get_info_field = strawberry.field(get_info)
+
+        assert not get_info_field.arguments  # Should have no arguments matched
+
+        info_parameter = get_info_field.base_resolver.info_parameter
+        assert info_parameter is not None
+        assert info_parameter.name == "info"
+
+
+def test_custom_info_negative():
+    """Test to ensure deprecation warning is emitted."""
+    with pytest.warns(
+        DeprecationWarning, match=r"Argument name-based matching of 'info'"
+    ):
+
+        @strawberry.field
+        def get_info(info) -> bool:
+            _ = info
+            return True
+
+        assert not get_info.arguments  # Should have no arguments matched
+
+        info_parameter = get_info.base_resolver.info_parameter
+        assert info_parameter is not None
+        assert info_parameter.name == "info"