Skip to content

General wrongness working with Enums #877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
avivey opened this issue Dec 14, 2018 · 10 comments
Closed

General wrongness working with Enums #877

avivey opened this issue Dec 14, 2018 · 10 comments

Comments

@avivey
Copy link

avivey commented Dec 14, 2018

This is for graphene 2.1.3.

There are a bunch of issues working with enums, here are two that bite me right now:

  1. When specifying default_value, we need to explicitly add .name or .value, else introspection breaks (see commented out code).
  • If specifying .value, the schema generated is wrong (See test 1 below).
  • If specifying .name, the schema is correct, but the argument passed to the function is a string that doesn't match any value (See test 1 and 2).
  1. When working with python-backed enums, the value passed to the resolver doesn't match any sensible value (See test 3, below).

Test code:

#! /usr/bin/env python
import graphene
import json
import enum

class Episode(graphene.Enum):
    NEWHOPE = 4
    EMPIRE = 5
    JEDI = 6

class BackendLengthUnit(enum.Enum):
    METER = 'm'
    FOOT = 'f'

LengthUnit = graphene.Enum.from_enum(BackendLengthUnit)

class Query(graphene.ObjectType):
    # desc = graphene.String(
    #    v=graphene.Argument(Episode, default_value=Episode.NEWHOPE),
    #    description='See ./broken-schema.py')
    desc1 = graphene.String(
        v=graphene.Argument(Episode, default_value=Episode.NEWHOPE.value),
        description='default value in schema is `4`, which is not valid. Also, awkward to write.')
    desc2 = graphene.String(
        v=graphene.Argument(Episode, default_value=Episode.NEWHOPE.name),
        description='Default value is correct in schema doc. awkward to write.')
    pyenum = graphene.String(
        v=graphene.Argument(LengthUnit),
        description='More fun with python-enum backed enum.')

    def resolve_desc1(self, info, v):
        return f'argument: {v!r}'

    def resolve_desc2(self, info, v):
        return f'argument: {v!r}'

    def resolve_pyenum(self, info, v):
        return (
            f'Argument: {v!r}. '
            f'Is GrapheneEnum foot: {v == LengthUnit.FOOT}. '
            f'Is Backend enum foot: {v == BackendLengthUnit.FOOT}. '
        )

schema = graphene.Schema(query=Query)
print('*** Schema:')
print(schema)

def qp(query):
    print(json.dumps(schema.execute(query).data, indent=2))

def qpf(query):
    print(json.dumps(schema.execute(query).to_dict(), indent=2))

print('*** Test 1: use default values:')

qp('{desc1, desc2}')
# Should be:
# {
  # "desc1": "argument: <EnumMeta.NEWHOPE: 4>",  (enum value)
  # "desc2": "argument: <EnumMeta.NEWHOPE: 4>"
# }
# but actually is:
# {
  # "desc1": "argument: 4",         (int value)
  # "desc2": "argument: 'NEWHOPE'"  (string name)
# }


print('*** Test 2: Providing a value:')
qp('{desc1(v: JEDI), desc2(v: JEDI)}')
# Should be:
# {
  # "desc1": "argument: <EnumMeta.JEDI: 6>",  (enum value)
  # "desc2": "argument: <EnumMeta.JEDI: 6>"
# }
# but actually is:
# {
  # "desc1": "argument: 6",
  # "desc2": "argument: 6"
# }


print('*** Test 3: Working with Python Enums:')
qp('{pyenum(v: FOOT)}')
# This is what we get:
# {
  # "pyenum": "Argument: 'f'. Is GrapheneEnum foot: False. Is Backend enum foot: False. "
# }
# In order to be useful, it should be at least one of these things!
@avivey
Copy link
Author

avivey commented Dec 15, 2018

update: never mind, I got the existing syntax to work. I still think the code is overly-complicated and this syntax isn't that useful, but it works.


The more I look at the code trying to resolve it, the more it looks like I'll want to completely break the existing syntax and replace it with something like this:

from enum import Enum
import graphene 

@graphene.enum
class Episode(Enum):
    NEWHOPE = 4
    EMPIRE = 5
    JEDI = 6

LengthUnit = graphene.enum(ExistingPythonEnum)

Which, while obviously much less cool than the existing syntax, it's not much less readable and makes the Graphene source much more readable.

Thoughts?

@MichaelAquilina
Copy link

@avivey what was your solution for fixing introspection with default values?

Both default_value=MyEnum.Foo.value and default_value=MyEnum.Foo.name dont seem to work correctly for me

@avivey
Copy link
Author

avivey commented Jan 24, 2019

@avivey what was your solution for fixing introspection with default values?

I didn't have one.
Considering the code complexity, my other issues with the project, and my personal hunch that "when an argument is an Enum, the default value should be implicitly obvious", I didn't spend any time on this.
Instead, I don't define a default value and specify it in the resolver directly.

@MichaelAquilina
Copy link

That's basically the approach I've gone for too - it's just not ideal for clarity in some situations :/

@daimeng
Copy link

daimeng commented Jun 21, 2019

update: never mind, I got the existing syntax to work. I still think the code is overly-complicated and this syntax isn't that useful, but it works.

Could you clarify what you did?

I'm using graphene.Enum.from_enum()
How do I get an enum argument from a query? I still get the value (str or int).

@avivey
Copy link
Author

avivey commented Jun 21, 2019

@daimeng see #879.

@stale
Copy link

stale bot commented Aug 20, 2019

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.

@stale stale bot added the wontfix label Aug 20, 2019
@barseghyanartur
Copy link

This works for me:

from enum import Enum
import graphene


class NoValue(Enum):

    def __repr__(self):
        return '<%s.%s>' % (self.__class__.__name__, self.name)

class Color(NoValue):
    RED = 'stop'
    GREEN = 'go'
    BLUE = 'too fast!'

Now you can:

print(Color.GREEN)
>>> <Color.GREEN>

print(Color.GREEN.value)
>>> 'go'

In graphene:

@graphene.Enum.from_enum
class ColorOptions(NoValue):

    RED = 'stop'
    GREEN = 'go'
    BLUE = 'too fast!'

@stale stale bot removed the wontfix label Aug 22, 2019
@stale
Copy link

stale bot commented Nov 20, 2019

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.

@stale stale bot added the wontfix label Nov 20, 2019
@stale stale bot closed this as completed Dec 4, 2019
@santosdevco
Copy link

How do I get an enum argument from a query? I still get the value (str or int).
Ca you solved it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants