mirror of
https://github.com/graphql-python/graphene.git
synced 2025-09-21 19:32:33 +03:00
Merge branch 'master' into issue_863
This commit is contained in:
commit
4344d8f442
|
@ -1,24 +1,28 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: git://github.com/pre-commit/pre-commit-hooks
|
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v1.3.0
|
rev: v2.1.0
|
||||||
hooks:
|
hooks:
|
||||||
|
- id: check-merge-conflict
|
||||||
- id: check-json
|
- id: check-json
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: debug-statements
|
- id: debug-statements
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
exclude: ^docs/.*$
|
exclude: ^docs/.*$
|
||||||
- id: trailing-whitespace
|
|
||||||
exclude: README.md
|
|
||||||
- id: pretty-format-json
|
- id: pretty-format-json
|
||||||
args:
|
args:
|
||||||
- --autofix
|
- --autofix
|
||||||
- id: flake8
|
- id: trailing-whitespace
|
||||||
|
exclude: README.md
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v1.4.0
|
rev: v1.12.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
- repo: https://github.com/ambv/black
|
- repo: https://github.com/ambv/black
|
||||||
rev: 18.6b4
|
rev: 18.9b0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
language_version: python3
|
language_version: python3
|
||||||
|
- repo: https://github.com/PyCQA/flake8
|
||||||
|
rev: 3.7.7
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
|
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/ @syrusakbary @ekampf @dan98765 @projectcheshire
|
|
@ -17,7 +17,7 @@ developer has to write to use them.
|
||||||
|
|
||||||
**New Features!**
|
**New Features!**
|
||||||
* [`InputObjectType`](#inputobjecttype)
|
* [`InputObjectType`](#inputobjecttype)
|
||||||
* [`Meta as Class arguments`](#meta-ass-class-arguments) (_only available for Python 3_)
|
* [`Meta as Class arguments`](#meta-as-class-arguments) (_only available for Python 3_)
|
||||||
|
|
||||||
|
|
||||||
> The type metaclasses are now deleted as they are no longer necessary. If your code was depending
|
> The type metaclasses are now deleted as they are no longer necessary. If your code was depending
|
||||||
|
@ -276,7 +276,7 @@ If you are using Middelwares, you need to some adjustments:
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class MyGrapheneMiddleware(object):
|
class MyGrapheneMiddleware(object):
|
||||||
def resolve(self, next_mw, root, args, context, info):
|
def resolve(self, next_mw, root, args, context, info):
|
||||||
|
|
||||||
## Middleware code
|
## Middleware code
|
||||||
|
@ -287,7 +287,7 @@ class MyGrapheneMiddleware(object):
|
||||||
With 2.0:
|
With 2.0:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class MyGrapheneMiddleware(object):
|
class MyGrapheneMiddleware(object):
|
||||||
def resolve(self, next_mw, root, info, **args):
|
def resolve(self, next_mw, root, info, **args):
|
||||||
context = info.context
|
context = info.context
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ This example model defines a Person, with a first and a last name:
|
||||||
last_name = graphene.String()
|
last_name = graphene.String()
|
||||||
full_name = graphene.String()
|
full_name = graphene.String()
|
||||||
|
|
||||||
def resolve_full_name(self, info):
|
def resolve_full_name(root, info):
|
||||||
return '{} {}'.format(self.first_name, self.last_name)
|
return '{} {}'.format(root.first_name, root.last_name)
|
||||||
|
|
||||||
**first\_name** and **last\_name** are fields of the ObjectType. Each
|
**first\_name** and **last\_name** are fields of the ObjectType. Each
|
||||||
field is specified as a class attribute, and each attribute maps to a
|
field is specified as a class attribute, and each attribute maps to a
|
||||||
|
@ -56,23 +56,148 @@ NOTE: The resolvers on an ``ObjectType`` are always treated as ``staticmethod``\
|
||||||
so the first argument to the resolver method ``self`` (or ``root``) need
|
so the first argument to the resolver method ``self`` (or ``root``) need
|
||||||
not be an actual instance of the ``ObjectType``.
|
not be an actual instance of the ``ObjectType``.
|
||||||
|
|
||||||
|
If an explicit resolver is not defined on the ``ObjectType`` then Graphene will
|
||||||
|
attempt to use a property with the same name on the object that is passed to the
|
||||||
|
``ObjectType``.
|
||||||
|
|
||||||
Quick example
|
.. code:: python
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This example model defines a ``Query`` type, which has a reverse field
|
import graphene
|
||||||
that reverses the given ``word`` argument using the ``resolve_reverse``
|
|
||||||
method in the class.
|
class Person(graphene.ObjectType):
|
||||||
|
first_name = graphene.String()
|
||||||
|
last_name = graphene.String()
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
me = graphene.Field(Person)
|
||||||
|
|
||||||
|
def resolve_me(_, info):
|
||||||
|
# returns an object that represents a Person
|
||||||
|
return get_human(name='Luke Skywalker')
|
||||||
|
|
||||||
|
If you are passing a dict instead of an object to your ``ObjectType`` you can
|
||||||
|
change the default resolver in the ``Meta`` class like this:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
import graphene
|
||||||
|
from graphene.types.resolver import dict_resolver
|
||||||
|
|
||||||
|
class Person(graphene.ObjectType):
|
||||||
|
class Meta:
|
||||||
|
default_resolver = dict_resolver
|
||||||
|
|
||||||
|
first_name = graphene.String()
|
||||||
|
last_name = graphene.String()
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
me = graphene.Field(Person)
|
||||||
|
|
||||||
|
def resolve_me(_, info):
|
||||||
|
return {
|
||||||
|
"first_name": "Luke",
|
||||||
|
"last_name": "Skywalker",
|
||||||
|
}
|
||||||
|
|
||||||
|
Or you can change the default resolver globally by calling ``set_default_resolver``
|
||||||
|
before executing a query.
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
import graphene
|
||||||
|
from graphene.types.resolver import dict_resolver, set_default_resolver
|
||||||
|
|
||||||
|
set_default_resolver(dict_resolver)
|
||||||
|
|
||||||
|
schema = graphene.Schema(query=Query)
|
||||||
|
result = schema.execute('''
|
||||||
|
query {
|
||||||
|
me {
|
||||||
|
firstName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
Resolvers with arguments
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Any arguments that a field defines gets passed to the resolver function as
|
||||||
|
kwargs. For example:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
class Query(graphene.ObjectType):
|
||||||
reverse = graphene.String(word=graphene.String())
|
human_by_name = graphene.Field(Human, name=graphene.String(required=True))
|
||||||
|
|
||||||
|
def resolve_human_by_name(_, info, name):
|
||||||
|
return get_human(name=name)
|
||||||
|
|
||||||
|
You can then execute the following query:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
query {
|
||||||
|
humanByName(name: "Luke Skywalker") {
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTE: if you define an argument for a field that is not required (and in a query
|
||||||
|
execution it is not provided as an argument) it will not be passed to the
|
||||||
|
resolver function at all. This is so that the developer can differenciate
|
||||||
|
between a ``undefined`` value for an argument and an explicit ``null`` value.
|
||||||
|
|
||||||
|
For example, given this schema:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
import graphene
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
hello = graphene.String(required=True, name=graphene.String())
|
||||||
|
|
||||||
|
def resolve_hello(_, info, name):
|
||||||
|
return name if name else 'World'
|
||||||
|
|
||||||
|
And this query:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
query {
|
||||||
|
hello
|
||||||
|
}
|
||||||
|
|
||||||
|
An error will be thrown:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
TypeError: resolve_hello() missing 1 required positional argument: 'name'
|
||||||
|
|
||||||
|
You can fix this error in 2 ways. Either by combining all keyword arguments
|
||||||
|
into a dict:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
hello = graphene.String(required=True, name=graphene.String())
|
||||||
|
|
||||||
|
def resolve_hello(_, info, **args):
|
||||||
|
return args.get('name', 'World')
|
||||||
|
|
||||||
|
Or by setting a default value for the keyword argument:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
hello = graphene.String(required=True, name=graphene.String())
|
||||||
|
|
||||||
|
def resolve_hello(_, info, name='World'):
|
||||||
|
return name
|
||||||
|
|
||||||
def resolve_reverse(self, info, word):
|
|
||||||
return word[::-1]
|
|
||||||
|
|
||||||
Resolvers outside the class
|
Resolvers outside the class
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -83,11 +208,13 @@ A field can use a custom resolver from outside the class:
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
|
|
||||||
def reverse(root, info, word):
|
def resolve_full_name(person, info):
|
||||||
return word[::-1]
|
return '{} {}'.format(person.first_name, person.last_name)
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
class Person(graphene.ObjectType):
|
||||||
reverse = graphene.String(word=graphene.String(), resolver=reverse)
|
first_name = graphene.String()
|
||||||
|
last_name = graphene.String()
|
||||||
|
full_name = graphene.String(resolver=resolve_full_name)
|
||||||
|
|
||||||
|
|
||||||
Instances as data containers
|
Instances as data containers
|
||||||
|
|
|
@ -707,7 +707,10 @@ class Signature(object):
|
||||||
break
|
break
|
||||||
elif param.name in kwargs:
|
elif param.name in kwargs:
|
||||||
if param.kind == _POSITIONAL_ONLY:
|
if param.kind == _POSITIONAL_ONLY:
|
||||||
msg = "{arg!r} parameter is positional only, " "but was passed as a keyword"
|
msg = (
|
||||||
|
"{arg!r} parameter is positional only, "
|
||||||
|
"but was passed as a keyword"
|
||||||
|
)
|
||||||
msg = msg.format(arg=param.name)
|
msg = msg.format(arg=param.name)
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
parameters_ex = (param,)
|
parameters_ex = (param,)
|
||||||
|
|
|
@ -75,9 +75,7 @@ def to_arguments(args, extra_args=None):
|
||||||
arg_name = default_name or arg.name
|
arg_name = default_name or arg.name
|
||||||
assert (
|
assert (
|
||||||
arg_name not in arguments
|
arg_name not in arguments
|
||||||
), 'More than one Argument have same name "{}".'.format(
|
), 'More than one Argument have same name "{}".'.format(arg_name)
|
||||||
arg_name
|
|
||||||
)
|
|
||||||
arguments[arg_name] = arg
|
arguments[arg_name] = arg
|
||||||
|
|
||||||
return arguments
|
return arguments
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
from ..utils.subclass_with_meta import SubclassWithMeta
|
from ..utils.subclass_with_meta import SubclassWithMeta
|
||||||
from ..utils.trim_docstring import trim_docstring
|
from ..utils.trim_docstring import trim_docstring
|
||||||
|
import six
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
|
|
||||||
class BaseOptions(object):
|
class BaseOptions(object):
|
||||||
|
|
|
@ -4,6 +4,9 @@ from graphql.language.ast import BooleanValue, FloatValue, IntValue, StringValue
|
||||||
from .base import BaseOptions, BaseType
|
from .base import BaseOptions, BaseType
|
||||||
from .unmountedtype import UnmountedType
|
from .unmountedtype import UnmountedType
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class ScalarOptions(BaseOptions):
|
class ScalarOptions(BaseOptions):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from ..argument import Argument
|
from ..argument import Argument
|
||||||
from ..enum import Enum
|
from ..enum import Enum
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from ..argument import Argument
|
from ..argument import Argument
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
from ..inputfield import InputField
|
from ..inputfield import InputField
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from ..json import JSONString
|
from ..json import JSONString
|
||||||
from ..objecttype import ObjectType
|
from ..objecttype import ObjectType
|
||||||
from ..schema import Schema
|
from ..schema import Schema
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
from ..scalars import String
|
from ..scalars import String
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from ..resolver import (
|
from ..resolver import (
|
||||||
attr_resolver,
|
attr_resolver,
|
||||||
dict_resolver,
|
dict_resolver,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from ..scalars import Scalar
|
from ..scalars import Scalar
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,4 +18,4 @@ def to_snake_case(name):
|
||||||
|
|
||||||
|
|
||||||
def to_const(string):
|
def to_const(string):
|
||||||
return re.sub("[\W|^]+", "_", string).upper()
|
return re.sub(r"[\W|^]+", "_", string).upper() # noqa
|
||||||
|
|
5
setup.py
5
setup.py
|
@ -1,4 +1,5 @@
|
||||||
import ast
|
import ast
|
||||||
|
import codecs
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -61,7 +62,9 @@ setup(
|
||||||
name="graphene",
|
name="graphene",
|
||||||
version=version,
|
version=version,
|
||||||
description="GraphQL Framework for Python",
|
description="GraphQL Framework for Python",
|
||||||
long_description=open("README.rst").read(),
|
long_description=codecs.open(
|
||||||
|
"README.rst", "r", encoding="ascii", errors="replace"
|
||||||
|
).read(),
|
||||||
url="https://github.com/graphql-python/graphene",
|
url="https://github.com/graphql-python/graphene",
|
||||||
author="Syrus Akbary",
|
author="Syrus Akbary",
|
||||||
author_email="me@syrusakbary.com",
|
author_email="me@syrusakbary.com",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user