Repository: tartiflette/tartiflette (python)

Issue:
Exceptions in directives are not handled
## Report a bug

Raised exceptions in directives are not handled and I get a 500 internal server error. I have checked defining an error coercer and the exception is not received there either.

version: 1.2.1
python version: 3.6.8
stack:
```
File "/usr/local/lib/python3.6/site-packages/tartiflette_asgi/_endpoints.py", line 93, in dispatch
     await super().dispatch()
   File "/usr/local/lib/python3.6/site-packages/starlette/endpoints.py", line 30, in dispatch
     response = await handler(request)
   File "/usr/local/lib/python3.6/site-packages/tartiflette_asgi/_endpoints.py", line 54, in post
     return await self._get_response(request, data=data)
   File "/usr/local/lib/python3.6/site-packages/tartiflette_asgi/_endpoints.py", line 71, in _get_response
     operation_name=data.get("operationName"),
   File "/usr/local/lib/python3.6/site-packages/tartiflette/engine.py", line 422, in execute
     context_coercer=context,
   File "/usr/local/lib/python3.6/site-packages/tartiflette/utils/directives.py", line 177, in directive_executor
     **kwargs,
   File "/src/xxxx/xxxx/application/gql/auth.py", line 35, in on_schema_execution
     raise Exception("Error!")
 Exception: Error!
```

Directive looks like:
```python3
@Directive("authDirective")
class AuthDirective:

    async def on_schema_execution(
        self,
        directive_args: Dict[str, Any],
        next_directive: Callable,
        schema: "GraphQLSchema",
        document: "DocumentNode",
        parsing_errors: Optional[List["TartifletteError"]],
        operation_name: Optional[str],
        context: Optional[Dict[str, Any]],
        variables: Optional[Dict[str, Any]],
        initial_value: Optional[Any],
    ):

        raise Exception("Error!")

        results = await next_directive(
            schema,
            document,
            parsing_errors,
            operation_name,
            context,
            variables,
            initial_value,
        )

        return results
```

Pull request description:
ISSUE-453 - Handle errors during schema_directive_execution
fix #453 

* [x] - Fix it.
* [x] - Tests
* [x] - Changelog

Patch:
diff --git a/changelogs/next.md b/changelogs/next.md
index d1d7a33..77f6f98 100644
--- a/changelogs/next.md
+++ b/changelogs/next.md
@@ -8,3 +8,7 @@ schema @nonIntrospectable {
     ...
 }
 ```
+
+## Fixed
+
+- [ISSUE-453](https://github.com/tartiflette/tartiflette/issues/453) - Exceptions in schema directive are now handled
diff --git a/tartiflette/engine.py b/tartiflette/engine.py
index 34f423a..3580eca 100644
--- a/tartiflette/engine.py
+++ b/tartiflette/engine.py
@@ -25,6 +25,7 @@ from tartiflette.types.exceptions.tartiflette import (
     ImproperlyConfigured,
     NonCallable,
     NonCoroutine,
+    TartifletteError,
 )
 from tartiflette.utils.callables import is_valid_coroutine
 from tartiflette.utils.errors import (
@@ -424,16 +425,26 @@ class Engine:
         )
 
         # Goes through potential schema directives and finish in self._perform_query
-        return await self._query_executor(
-            self._schema,
-            document,
-            errors,
-            operation_name,
-            context,
-            variables,
-            initial_value,
-            context_coercer=context,
-        )
+        try:
+            return await self._query_executor(
+                self._schema,
+                document,
+                errors,
+                operation_name,
+                context,
+                variables,
+                initial_value,
+                context_coercer=context,
+            )
+        # pylint: disable=broad-except
+        except Exception as e:
+            if not isinstance(e, TartifletteError):
+                e = TartifletteError(
+                    message=str(e),
+                    path=[self._schema.query_operation_name],
+                    original_error=e,
+                )
+            return await self._build_response(errors=[e])
 
     async def subscribe(
         self,


Test patch:
diff --git a/tests/functional/regressions/test_issue453.py b/tests/functional/regressions/test_issue453.py
new file mode 100644
index 0000000..72319b9
--- /dev/null
+++ b/tests/functional/regressions/test_issue453.py
@@ -0,0 +1,46 @@
+import pytest
+
+from tartiflette import Directive, Resolver, create_engine
+
+_SDL = """
+
+directive @issue453Directive on SCHEMA
+
+type Query {
+    fieldA: Int
+}
+
+schema @issue453Directive {
+    query: Query
+}
+
+"""
+
+
+@pytest.mark.asyncio
+async def test_issue453(random_schema_name):
+    @Directive("issue453Directive", schema_name=random_schema_name)
+    class AuthDirective:
+        async def on_schema_execution(
+            self,
+            directive_args,
+            next_directive,
+            schema,
+            document,
+            parsing_errors,
+            operation_name,
+            context,
+            variables,
+            initial_value,
+        ):
+            raise Exception("Error!")
+
+    @Resolver("Query.fieldA", schema_name=random_schema_name)
+    async def resolver_query_a(pr, args, ctx, info):
+        return 1
+
+    engine = await create_engine(_SDL, schema_name=random_schema_name)
+    assert {
+        "data": None,
+        "errors": [{"locations": [], "message": "Error!", "path": ["Query"]}],
+    } == await engine.execute("query { fieldA }")