question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

A new way to write ObjectType with python3's annotation

See original GitHub issue

This issue is a feature discussion.

For now, the way to write a ObjectType is as below:

class Hero(ObjectType):
    name = Field(List(String), only_first_name=Boolean())
    age = Int()

    def resolve_name(self, info, only_first_name=False):
        if only_first_name:
            return [self._first_name]
        return [self._first_name, self._last_name]

    def resolve_age(self, info):
        return self._age

I define name twice (Hero.name and Hero.resolve_name) because I have to define types of arguments and return value. This will cause some reading and writing problems.

Since python3, annotation feature bring a native way to describe types. By using annotation, we can rewrite this class with a clearer way:

class Heroine(AnnotationObjectType):

    def name(self, info, only_first_name: Boolean() = False) -> List(String):
        if only_first_name:
            return [self._first_name]
        return [self._first_name, self._last_name]

    def age(self, info) -> Int():
        return self._age


print(Heroine.name.__annotations__)
# {'only_first_name': <graphene.types.scalars.Boolean object at 0x104cb8550>, 'return': <graphene.types.structures.List object at 0x105742f28>}

AnnotationObjectType shouldn’t be difficult to write if we somehow transform it into ObjectType. But I think a looooot of cases should be tested before being released.

Of cause even if we write an AnnotationObjectType class, ObjectType class will not be dropped since python2.7 doesn’t support annotation.

I would like to hear you guys’ comments and suggestions before doing this.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:29
  • Comments:28 (7 by maintainers)

github_iconTop GitHub Comments

27reactions
syrusakbarycommented, Jun 5, 2018

@ocavue thanks for opening the discussion.

The new resolution syntax on Graphene 2 root, info, **options was took specially with typing in mind, so it could be very easy to annotate the resolver arguments in the future.

Personally, I do really like the way NamedTuple works in the typing package for defining the attributes for the named tuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple In Python 3.7 onwards, the @dataclass attribute will let the user create a data class very easily: https://www.python.org/dev/peps/pep-0557/

Inspired in all this I think the next version of Graphene (Graphene 3.0) could also allow developers to type their schemas like the following: (while maintaining 100% compatibility with the previous syntax)

@ObjectType
class Person:
    id: str
    name: str
    last_name: str
    def full_name(self, info) -> str:
        return f'{self.name} {self.last_name}'

persons_by_id = {
  '1': Person(id='1', name='Alice'),
  '2': Person(id='2', name='Bob')
}

@ObjectType
class Query:
   def get_person(self, info, id: str) -> Person:
       '''Get a person by their id'''
       return persons_by_id.get(id)

There are some reasons on why I think this is a very powerful syntax:

  • The decorator syntax permits to extend the normal data types, and will make easier to reason about self in resolvers (currently, on Graphene 2, self is referring to the root object, automatically marking the resolver as a @staticmethod… and sometimes this might be confusing)
  • It permits a fully typed and testable schema, end to end
  • The layering needed between native Python types and GraphQL types will be minimal

However, there are some cons of this approach:

  • field types will be required by default. As an optional type is always a super set of the normal type Optional[T] = Union[T, None] *, Optional types will need to be explicitly defined and being required will be the default.
  • If the ObjectType have other attributes annotated (that we don’t want to expose to GraphQL) they will be exposed automatically. We can solve this in various ways:
    • do not include attributes starting _ (private) to GraphQL
    • Add a explicit way of skipping attributes, such as:
@ObjectType(skip: ['password'])
class Person:
   # ...
   password: str
  • We will need to rethink about connections and how are resolved, so we can do things like:
@Connection(node=People)
class ConnectionPeople:
    pass

@ObjectType
class Query:
    def all_people(self, info, first: int = 10) -> ConnectionPeople:
        return ConnectionPeople.get_from_iterable(...)
  • Will be challenging to keep the previous syntax 100% compatible (but still possible, it will be just a bit harder and might complicate the logic for a bit).

What are your thoughts?

6reactions
thomascobbcommented, Mar 16, 2020

Looks like someone already thought of combining Pydantic and Graphene… https://github.com/upsidetravel/graphene-pydantic

Read more comments on GitHub >

github_iconTop Results From Across the Web

typing — Support for type hints — Python 3.11.1 documentation
For a simplified introduction to type hints, see PEP 483. The function below takes and returns a string and is annotated as follows:...
Read more >
Kinds of types - mypy 0.991 documentation
Callable types (and lambdas)#. You can pass around function objects and bound methods in statically typed code. The type of a function that...
Read more >
Type Annotation in Python | Towards Data Science
Type annotations have had a convoluted history with Python - but have recently experienced a Renaissance. We'll cover how to effectively use ...
Read more >
Python Metaclasses - Real Python
In Python 3, all classes are new-style classes. Thus, in Python 3 it is reasonable to refer to an object's type and its...
Read more >
Using GraphQL with Python – A Complete Guide
route ('/') def hello(): return 'My First API !!' Restart the server and make sure everything is working as usual. Creating a model....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found