Repository: mirumee/ariadne

Issue:
Error using the Apollo Tracing extension
Hi folks, amazing job on the library

Yesterday I enabled the ApolloTracingExtension but got some errors with mutations that receive an argument named "parent".

`TypeError: resolve() got multiple values for argument 'parent'`

Looking at how it's implemented points to this line:
https://github.com/mirumee/ariadne/blob/master/ariadne/contrib/tracing/apollotracing.py#L38


Discussion:
Looks like extension will explode if its enabled on GraphQL schema with fields taking `parent` as argument, and we should change this arg to `parent_` on our side, like we are already doing for `next_`.


Update: this also happens with the resolve method in custom extensions.

Seems like it is not ideal to use parent as an input arg at all. Lots of graphql-related functions rely on the exact names "parent" and "info"

if this isn't implementable, i believe documenting a warning would be helpful
This is an interesting issue actually. :D

Basically convention for GraphQL resolvers is to name first argument either `obj` or `parent`. With Ariadne we went for the latter which causes naming conflict for you. Renaming first arg to `obj` in default resolvers should resolve it from what I see?

I can see cases for having an arg named "parent", but nothing comes to mind for `obj`.

Resolving patch:
diff --git a/ariadne/contrib/tracing/apollotracing.py b/ariadne/contrib/tracing/apollotracing.py
--- a/ariadne/contrib/tracing/apollotracing.py
+++ b/ariadne/contrib/tracing/apollotracing.py
@@ -36,10 +36,10 @@ def request_started(self, context: ContextValue):
         self.start_timestamp = perf_counter_ns()
 
     async def resolve(
-        self, next_: Resolver, parent: Any, info: GraphQLResolveInfo, **kwargs
+        self, next_: Resolver, obj: Any, info: GraphQLResolveInfo, **kwargs
     ):
         if not should_trace(info, self.trace_default_resolver):
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             if isawaitable(result):
                 result = await result
             return result
@@ -54,7 +54,7 @@ async def resolve(
         }
         self.resolvers.append(record)
         try:
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             if isawaitable(result):
                 result = await result
             return result
@@ -91,10 +91,10 @@ def format(self, context: ContextValue):
 
 class ApolloTracingExtensionSync(ApolloTracingExtension):
     def resolve(
-        self, next_: Resolver, parent: Any, info: GraphQLResolveInfo, **kwargs
+        self, next_: Resolver, obj: Any, info: GraphQLResolveInfo, **kwargs
     ):  # pylint: disable=invalid-overridden-method
         if not should_trace(info):
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             return result
 
         start_timestamp = perf_counter_ns()
@@ -107,7 +107,7 @@ def resolve(
         }
         self.resolvers.append(record)
         try:
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             return result
         finally:
             end_timestamp = perf_counter_ns()
diff --git a/ariadne/contrib/tracing/opentracing.py b/ariadne/contrib/tracing/opentracing.py
--- a/ariadne/contrib/tracing/opentracing.py
+++ b/ariadne/contrib/tracing/opentracing.py
@@ -33,10 +33,10 @@ def request_finished(self, context: ContextValue):
         self._root_scope.close()
 
     async def resolve(
-        self, next_: Resolver, parent: Any, info: GraphQLResolveInfo, **kwargs
+        self, next_: Resolver, obj: Any, info: GraphQLResolveInfo, **kwargs
     ):
         if not should_trace(info):
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             if isawaitable(result):
                 result = await result
             return result
@@ -56,7 +56,7 @@ async def resolve(
                 for kwarg, value in filtered_kwargs.items():
                     span.set_tag(f"graphql.param.{kwarg}", value)
 
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             if isawaitable(result):
                 result = await result
             return result
@@ -74,10 +74,10 @@ def filter_resolver_args(
 
 class OpenTracingExtensionSync(OpenTracingExtension):
     def resolve(
-        self, next_: Resolver, parent: Any, info: GraphQLResolveInfo, **kwargs
+        self, next_: Resolver, obj: Any, info: GraphQLResolveInfo, **kwargs
     ):  # pylint: disable=invalid-overridden-method
         if not should_trace(info):
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             return result
 
         with self._tracer.start_active_span(info.field_name) as scope:
@@ -95,7 +95,7 @@ def resolve(
                 for kwarg, value in filtered_kwargs.items():
                     span.set_tag(f"graphql.param.{kwarg}", value)
 
-            result = next_(parent, info, **kwargs)
+            result = next_(obj, info, **kwargs)
             return result
 
 
diff --git a/ariadne/types.py b/ariadne/types.py
--- a/ariadne/types.py
+++ b/ariadne/types.py
@@ -55,9 +55,9 @@ def request_finished(self, context: ContextValue):
         pass  # pragma: no cover
 
     async def resolve(
-        self, next_: Resolver, parent: Any, info: GraphQLResolveInfo, **kwargs
+        self, next_: Resolver, obj: Any, info: GraphQLResolveInfo, **kwargs
     ):
-        result = next_(parent, info, **kwargs)
+        result = next_(obj, info, **kwargs)
         if isawaitable(result):
             result = await result
         return result
@@ -71,9 +71,9 @@ def format(self, context: ContextValue) -> Optional[dict]:
 
 class ExtensionSync(Extension):
     def resolve(
-        self, next_: Resolver, parent: Any, info: GraphQLResolveInfo, **kwargs
+        self, next_: Resolver, obj: Any, info: GraphQLResolveInfo, **kwargs
     ):  # pylint: disable=invalid-overridden-method
-        return next_(parent, info, **kwargs)
+        return next_(obj, info, **kwargs)
 
 
 @runtime_checkable


Test patch:
diff --git a/tests/asgi/test_configuration.py b/tests/asgi/test_configuration.py
--- a/tests/asgi/test_configuration.py
+++ b/tests/asgi/test_configuration.py
@@ -289,8 +289,8 @@ def test_error_formatter_is_called_with_debug_disabled(schema):
 
 
 class CustomExtension(Extension):
-    async def resolve(self, next_, parent, info, **kwargs):
-        return next_(parent, info, **kwargs).lower()
+    async def resolve(self, next_, obj, info, **kwargs):
+        return next_(obj, info, **kwargs).lower()
 
 
 def test_extension_from_option_are_passed_to_query_executor(schema):
diff --git a/tests/asgi/test_query_execution.py b/tests/asgi/test_query_execution.py
--- a/tests/asgi/test_query_execution.py
+++ b/tests/asgi/test_query_execution.py
@@ -167,8 +167,8 @@ def test_query_is_executed_for_multipart_request_with_large_file_with_tracing(
 
 
 class CustomExtension(Extension):
-    async def resolve(self, next_, parent, info, **kwargs):
-        value = next_(parent, info, **kwargs)
+    async def resolve(self, next_, obj, info, **kwargs):
+        value = next_(obj, info, **kwargs)
         return f"={value}="
 
 
diff --git a/tests/wsgi/test_configuration.py b/tests/wsgi/test_configuration.py
--- a/tests/wsgi/test_configuration.py
+++ b/tests/wsgi/test_configuration.py
@@ -150,8 +150,8 @@ def test_error_formatter_is_called_with_debug_disabled_flag(schema):
 
 
 class CustomExtension(ExtensionSync):
-    def resolve(self, next_, parent, info, **kwargs):
-        return next_(parent, info, **kwargs).lower()
+    def resolve(self, next_, obj, info, **kwargs):
+        return next_(obj, info, **kwargs).lower()
 
 
 def test_extension_from_option_are_passed_to_query_executor(schema):
diff --git a/tests/wsgi/test_query_execution.py b/tests/wsgi/test_query_execution.py
--- a/tests/wsgi/test_query_execution.py
+++ b/tests/wsgi/test_query_execution.py
@@ -186,8 +186,8 @@ def test_query_is_executed_for_multipart_form_request_with_file(
 
 
 class CustomExtension(ExtensionSync):
-    def resolve(self, next_, parent, info, **kwargs):
-        value = next_(parent, info, **kwargs)
+    def resolve(self, next_, obj, info, **kwargs):
+        value = next_(obj, info, **kwargs)
         return f"={value}="