Graphene-django: Traceback is printed on exceptions raised from resolve_field methods

Created on 6 Aug 2019  路  5Comments  路  Source: graphql-python/graphene-django

Hi,

I am trying to implement an easy permission system on retrieving fields. So for each field, I implement such a method:

def resolve_field(self, info, **kwargs):
    if not has_permission():
        raise PermissionError("Access Denied!")
    return self.field

Everything works fine, the user gets a nicely formatted errors filed back to the frontend. But when I look to my server console, I see this:

Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Traceback (most recent call last):
...some irrelevant lines...
graphql.error.located_error.GraphQLLocatedError: Access Denied!

[06/Aug/2019 21:19:16] "POST /graphql/ HTTP/1.1" 200 427

No exception is thrown, my exception is perfectly caught in the system. But something somewhere translated the exception into GraphQLLocatedError and prints the traceback, which is super annoying, since I can't manage to find a way to get rid of it. Is that even possible?

Or, is my idea of implementing permissions (with raising an exception) wrong?

Thanks!

wontfix

Most helpful comment

@tlinhart I am not sure if I don't accidentally silence something I don't want to, but it is definitely a good workaround. This one line of code

import logging
logging.getLogger("graphql.execution.utils").setLevel(logging.CRITICAL)

does it. Thanks a lot!

All 5 comments

I have the same problem in my tests. I've put it on the back burner for now. I was thinking maybe better to handle that validation in the serializer instead.

I'm doing something like this now:

# exceptions.py

from rest_framework.exceptions import ErrorDetail
from graphene_django.types import ErrorType
from django.utils.translation import gettext as _


class MutationError(Exception):

    def __init__(self, message, field='non_field_errors'):
        self.message = message
        self.field = field

    def __str__(self):
        return self.message

    @property
    def error_dict(self):
        return {self.field: [ErrorDetail(string=self.message)]}

    @property
    def errors(self):
        return [
            ErrorType(field=key, messages=value)
            for key, value in self.error_dict.items()
        ]


class PermissionError(MutationError):
    pass


class BaseSerializerMutation(SerializerMutation):

    class Meta:
        abstract = True

    @classmethod
    def mutate_and_get_payload(cls, root, info, **input):
        try:
            input = SomeAdvancedPermissionCheckerService().execute()
        except PermissionError as e:
            return cls(errors=e.errors)

Exceptions raised in your resolvers get caught and wrapped in GraphQLLocatedError, look in the code. If you look at the report_error method, you'll see that the exception together with traceback is logged using the graphql.execution.utils logger. Thus, you should be able to suppress the tracebacks in the log by silencing this logger.

@tlinhart I am not sure if I don't accidentally silence something I don't want to, but it is definitely a good workaround. This one line of code

import logging
logging.getLogger("graphql.execution.utils").setLevel(logging.CRITICAL)

does it. Thanks a lot!

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eyalch picture eyalch  路  3Comments

x9sheikh picture x9sheikh  路  4Comments

mraak picture mraak  路  3Comments

nickhudkins picture nickhudkins  路  3Comments

MythicManiac picture MythicManiac  路  3Comments