mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-23 23:20:47 +03:00
Manually merged in master.
This commit is contained in:
commit
3d6486a899
|
@ -22,7 +22,7 @@ before_install:
|
||||||
install:
|
install:
|
||||||
- |
|
- |
|
||||||
if [ "$TEST_TYPE" = build ]; then
|
if [ "$TEST_TYPE" = build ]; then
|
||||||
pip install pytest pytest-cov pytest-benchmark coveralls six
|
pip install pytest pytest-cov pytest-benchmark coveralls six pytz iso8601
|
||||||
pip install -e .
|
pip install -e .
|
||||||
python setup.py develop
|
python setup.py develop
|
||||||
elif [ "$TEST_TYPE" = lint ]; then
|
elif [ "$TEST_TYPE" = lint ]; then
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
You are in the `next` unreleased version of Graphene (`1.0.dev`).
|
Please read [UPGRADE-v1.0.md](/UPGRADE-v1.0.md) to learn how to upgrade to Graphene `1.0`.
|
||||||
Please read [UPGRADE-v1.0.md](/UPGRADE-v1.0.md) to learn how to upgrade.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -32,7 +31,7 @@ Graphene has multiple integrations with different frameworks:
|
||||||
For instaling graphene, just run this command in your shell
|
For instaling graphene, just run this command in your shell
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install "graphene>=1.0.dev"
|
pip install "graphene>=1.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
## 1.0 Upgrade Guide
|
## 1.0 Upgrade Guide
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
You are in the ``next`` unreleased version of Graphene (``1.0.dev``).
|
Please read `UPGRADE-v1.0.md`_ to learn how to upgrade to Graphene ``1.0``.
|
||||||
Please read `UPGRADE-v1.0.md`_ to learn how to upgrade.
|
|
||||||
|
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ For instaling graphene, just run this command in your shell
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
pip install "graphene>=1.0.dev"
|
pip install "graphene>=1.0"
|
||||||
|
|
||||||
1.0 Upgrade Guide
|
1.0 Upgrade Guide
|
||||||
-----------------
|
-----------------
|
||||||
|
|
15
docs/conf.py
15
docs/conf.py
|
@ -73,7 +73,7 @@ author = u'Syrus Akbary'
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = u'1.0'
|
version = u'1.0'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = u'1.0.dev'
|
release = u'1.0'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -133,9 +133,14 @@ todo_include_todos = True
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
#
|
#
|
||||||
html_theme = 'alabaster'
|
# html_theme = 'alabaster'
|
||||||
if on_rtd:
|
# if on_rtd:
|
||||||
html_theme = 'sphinx_rtd_theme'
|
# html_theme = 'sphinx_rtd_theme'
|
||||||
|
import sphinx_graphene_theme
|
||||||
|
|
||||||
|
html_theme = "sphinx_graphene_theme"
|
||||||
|
|
||||||
|
html_theme_path = [sphinx_graphene_theme.get_html_theme_path()]
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
|
@ -149,7 +154,7 @@ if on_rtd:
|
||||||
# The name for this set of Sphinx documents.
|
# The name for this set of Sphinx documents.
|
||||||
# "<project> v<release> documentation" by default.
|
# "<project> v<release> documentation" by default.
|
||||||
#
|
#
|
||||||
# html_title = u'Graphene v1.0.dev'
|
# html_title = u'Graphene v1.0'
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
#
|
#
|
||||||
|
|
|
@ -14,7 +14,7 @@ Project setup
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
pip install graphene>=1.0
|
pip install "graphene>=1.0"
|
||||||
|
|
||||||
Creating a basic Schema
|
Creating a basic Schema
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
2
docs/requirements.txt
Normal file
2
docs/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Docs template
|
||||||
|
https://github.com/graphql-python/graphene-python.org/archive/docs.zip
|
|
@ -32,7 +32,7 @@ plus the ones defined in ``UserFields``.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
.. code:: graphql
|
.. code::
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
name: String
|
name: String
|
||||||
|
|
|
@ -10,4 +10,5 @@ Types Reference
|
||||||
interfaces
|
interfaces
|
||||||
abstracttypes
|
abstracttypes
|
||||||
objecttypes
|
objecttypes
|
||||||
|
schema
|
||||||
mutations
|
mutations
|
||||||
|
|
|
@ -5,6 +5,7 @@ An Interface contains the essential fields that will be implemented among
|
||||||
multiple ObjectTypes.
|
multiple ObjectTypes.
|
||||||
|
|
||||||
The basics:
|
The basics:
|
||||||
|
|
||||||
- Each Interface is a Python class that inherits from ``graphene.Interface``.
|
- Each Interface is a Python class that inherits from ``graphene.Interface``.
|
||||||
- Each attribute of the Interface represents a GraphQL field.
|
- Each attribute of the Interface represents a GraphQL field.
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ time.
|
||||||
|
|
||||||
The above types would have the following representation in a schema:
|
The above types would have the following representation in a schema:
|
||||||
|
|
||||||
.. code:: graphql
|
.. code::
|
||||||
|
|
||||||
interface Character {
|
interface Character {
|
||||||
name: String
|
name: String
|
||||||
|
|
|
@ -53,7 +53,7 @@ Executing the Mutation
|
||||||
|
|
||||||
Then, if we query (``schema.execute(query_str)``) the following:
|
Then, if we query (``schema.execute(query_str)``) the following:
|
||||||
|
|
||||||
.. code:: graphql
|
.. code::
|
||||||
|
|
||||||
mutation myFirstMutation {
|
mutation myFirstMutation {
|
||||||
createPerson(name:"Peter") {
|
createPerson(name:"Peter") {
|
||||||
|
|
|
@ -8,7 +8,7 @@ querying.
|
||||||
The basics:
|
The basics:
|
||||||
|
|
||||||
- Each ObjectType is a Python class that inherits
|
- Each ObjectType is a Python class that inherits
|
||||||
``graphene.ObjectType`` or inherits an implemented `Interface`_.
|
``graphene.ObjectType``.
|
||||||
- Each attribute of the ObjectType represents a ``Field``.
|
- Each attribute of the ObjectType represents a ``Field``.
|
||||||
|
|
||||||
Quick example
|
Quick example
|
||||||
|
@ -36,7 +36,7 @@ Field.
|
||||||
The above ``Person`` ObjectType would have the following representation
|
The above ``Person`` ObjectType would have the following representation
|
||||||
in a schema:
|
in a schema:
|
||||||
|
|
||||||
.. code:: graphql
|
.. code::
|
||||||
|
|
||||||
type Person {
|
type Person {
|
||||||
firstName: String
|
firstName: String
|
||||||
|
@ -55,6 +55,11 @@ otherwise, the ``resolve_{field_name}`` within the ``ObjectType``.
|
||||||
By default a resolver will take the ``args``, ``context`` and ``info``
|
By default a resolver will take the ``args``, ``context`` and ``info``
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
|
NOTE: The class resolvers in a ``ObjectType`` are treated as ``staticmethod``s
|
||||||
|
always, so the first argument in the resolver: ``self`` (or ``root``) doesn't
|
||||||
|
need to be an actual instance of the ``ObjectType``.
|
||||||
|
|
||||||
|
|
||||||
Quick example
|
Quick example
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ Scalars
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Graphene define the following base Scalar Types:
|
Graphene define the following base Scalar Types:
|
||||||
|
|
||||||
- ``graphene.String``
|
- ``graphene.String``
|
||||||
- ``graphene.Int``
|
- ``graphene.Int``
|
||||||
- ``graphene.Float``
|
- ``graphene.Float``
|
||||||
|
@ -9,6 +10,7 @@ Graphene define the following base Scalar Types:
|
||||||
- ``graphene.ID``
|
- ``graphene.ID``
|
||||||
|
|
||||||
Graphene also provides custom scalars for Dates and JSON:
|
Graphene also provides custom scalars for Dates and JSON:
|
||||||
|
|
||||||
- ``graphene.types.datetime.DateTime``
|
- ``graphene.types.datetime.DateTime``
|
||||||
- ``graphene.types.json.JSONString``
|
- ``graphene.types.json.JSONString``
|
||||||
|
|
||||||
|
|
81
docs/types/schema.rst
Normal file
81
docs/types/schema.rst
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
Schema
|
||||||
|
======
|
||||||
|
|
||||||
|
A Schema is created by supplying the root types of each type of operation, query and mutation (optional).
|
||||||
|
A schema definition is then supplied to the validator and executor.
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
my_schema = Schema(
|
||||||
|
query=MyRootQuery,
|
||||||
|
mutation=MyRootMutation,
|
||||||
|
)
|
||||||
|
|
||||||
|
Types
|
||||||
|
-----
|
||||||
|
|
||||||
|
There are some cases where the schema could not access all the types that we plan to have.
|
||||||
|
For example, when a field returns an ``Interface``, the schema doesn't know any of the
|
||||||
|
implementations.
|
||||||
|
|
||||||
|
In this case, we would need to use the ``types`` argument when creating the Schema.
|
||||||
|
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
my_schema = Schema(
|
||||||
|
query=MyRootQuery,
|
||||||
|
types=[SomeExtraObjectType, ]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Querying
|
||||||
|
--------
|
||||||
|
|
||||||
|
If you need to query a schema, you can directly call the ``execute`` method on it.
|
||||||
|
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
my_schema.execute('{ lastName }')
|
||||||
|
|
||||||
|
|
||||||
|
Auto CamelCase field names
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
By default all field and argument names (that are not
|
||||||
|
explicitly set with the ``name`` arg) will be converted from
|
||||||
|
`snake_case` to `camelCase` (`as the API is usually being consumed by a js/mobile client`)
|
||||||
|
|
||||||
|
So, for example if we have the following ObjectType
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
class Person(graphene.ObjectType):
|
||||||
|
last_name = graphene.String()
|
||||||
|
other_name = graphene.String(name='_other_Name')
|
||||||
|
|
||||||
|
Then the ``last_name`` field name is converted to ``lastName``.
|
||||||
|
|
||||||
|
In the case we don't want to apply any transformation, we can specify
|
||||||
|
the field name with the ``name`` argument. So ``other_name`` field name
|
||||||
|
would be converted to ``_other_Name`` (without any other transformation).
|
||||||
|
|
||||||
|
So, you would need to query with:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
{
|
||||||
|
lastName
|
||||||
|
_other_Name
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
If you want to disable this behavior, you set use the ``auto_camelcase`` argument
|
||||||
|
to ``False`` when you create the Schema.
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
my_schema = Schema(
|
||||||
|
query=MyRootQuery,
|
||||||
|
auto_camelcase=False,
|
||||||
|
)
|
|
@ -10,7 +10,7 @@ except NameError:
|
||||||
__SETUP__ = False
|
__SETUP__ = False
|
||||||
|
|
||||||
|
|
||||||
VERSION = (1, 0, 0, 'alpha', 0)
|
VERSION = (1, 0, 2, 'final', 0)
|
||||||
|
|
||||||
__version__ = get_version(VERSION)
|
__version__ = get_version(VERSION)
|
||||||
|
|
||||||
|
|
|
@ -95,13 +95,13 @@ class Connection(six.with_metaclass(ConnectionMeta, ObjectType)):
|
||||||
class IterableConnectionField(Field):
|
class IterableConnectionField(Field):
|
||||||
|
|
||||||
def __init__(self, type, *args, **kwargs):
|
def __init__(self, type, *args, **kwargs):
|
||||||
|
kwargs.setdefault('before', String())
|
||||||
|
kwargs.setdefault('after', String())
|
||||||
|
kwargs.setdefault('first', Int())
|
||||||
|
kwargs.setdefault('last', Int())
|
||||||
super(IterableConnectionField, self).__init__(
|
super(IterableConnectionField, self).__init__(
|
||||||
type,
|
type,
|
||||||
*args,
|
*args,
|
||||||
before=String(),
|
|
||||||
after=String(),
|
|
||||||
first=Int(),
|
|
||||||
last=Int(),
|
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,21 +117,25 @@ class IterableConnectionField(Field):
|
||||||
).format(str(self), connection_type)
|
).format(str(self), connection_type)
|
||||||
return connection_type
|
return connection_type
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def connection_resolver(resolver, connection, root, args, context, info):
|
def connection_resolver(cls, resolver, connection, root, args, context, info):
|
||||||
iterable = resolver(root, args, context, info)
|
resolved = resolver(root, args, context, info)
|
||||||
assert isinstance(iterable, Iterable), (
|
|
||||||
'Resolved value from the connection field have to be iterable. '
|
if isinstance(resolved, connection):
|
||||||
|
return resolved
|
||||||
|
|
||||||
|
assert isinstance(resolved, Iterable), (
|
||||||
|
'Resolved value from the connection field have to be iterable or instance of {}. '
|
||||||
'Received "{}"'
|
'Received "{}"'
|
||||||
).format(iterable)
|
).format(connection, resolved)
|
||||||
connection = connection_from_list(
|
connection = connection_from_list(
|
||||||
iterable,
|
resolved,
|
||||||
args,
|
args,
|
||||||
connection_type=connection,
|
connection_type=connection,
|
||||||
edge_type=connection.Edge,
|
edge_type=connection.Edge,
|
||||||
pageinfo_type=PageInfo
|
pageinfo_type=PageInfo
|
||||||
)
|
)
|
||||||
connection.iterable = iterable
|
connection.iterable = resolved
|
||||||
return connection
|
return connection
|
||||||
|
|
||||||
def get_resolver(self, parent_resolver):
|
def get_resolver(self, parent_resolver):
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
from ...types import AbstractType, Field, List, NonNull, ObjectType, String
|
from ...types import AbstractType, Field, List, NonNull, ObjectType, String, Argument, Int
|
||||||
from ..connection import Connection, PageInfo
|
from ..connection import Connection, PageInfo, ConnectionField
|
||||||
from ..node import Node
|
from ..node import Node
|
||||||
|
|
||||||
|
|
||||||
|
@ -109,3 +109,32 @@ def test_pageinfo():
|
||||||
assert PageInfo._meta.name == 'PageInfo'
|
assert PageInfo._meta.name == 'PageInfo'
|
||||||
fields = PageInfo._meta.fields
|
fields = PageInfo._meta.fields
|
||||||
assert list(fields.keys()) == ['has_next_page', 'has_previous_page', 'start_cursor', 'end_cursor']
|
assert list(fields.keys()) == ['has_next_page', 'has_previous_page', 'start_cursor', 'end_cursor']
|
||||||
|
|
||||||
|
|
||||||
|
def test_connectionfield():
|
||||||
|
class MyObjectConnection(Connection):
|
||||||
|
class Meta:
|
||||||
|
node = MyObject
|
||||||
|
|
||||||
|
field = ConnectionField(MyObjectConnection)
|
||||||
|
assert field.args == {
|
||||||
|
'before': Argument(String),
|
||||||
|
'after': Argument(String),
|
||||||
|
'first': Argument(Int),
|
||||||
|
'last': Argument(Int),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_connectionfield_custom_args():
|
||||||
|
class MyObjectConnection(Connection):
|
||||||
|
class Meta:
|
||||||
|
node = MyObject
|
||||||
|
|
||||||
|
field = ConnectionField(MyObjectConnection, before=String(required=True), extra=String())
|
||||||
|
assert field.args == {
|
||||||
|
'before': Argument(NonNull(String)),
|
||||||
|
'after': Argument(String),
|
||||||
|
'first': Argument(Int),
|
||||||
|
'last': Argument(Int),
|
||||||
|
'extra': Argument(String),
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||||
from graphql_relay.utils import base64
|
from graphql_relay.utils import base64
|
||||||
|
|
||||||
from ...types import ObjectType, Schema, String
|
from ...types import ObjectType, Schema, String
|
||||||
from ..connection import ConnectionField
|
from ..connection import ConnectionField, PageInfo
|
||||||
from ..node import Node
|
from ..node import Node
|
||||||
|
|
||||||
letter_chars = ['A', 'B', 'C', 'D', 'E']
|
letter_chars = ['A', 'B', 'C', 'D', 'E']
|
||||||
|
@ -19,11 +19,26 @@ class Letter(ObjectType):
|
||||||
|
|
||||||
class Query(ObjectType):
|
class Query(ObjectType):
|
||||||
letters = ConnectionField(Letter)
|
letters = ConnectionField(Letter)
|
||||||
|
connection_letters = ConnectionField(Letter)
|
||||||
|
|
||||||
|
node = Node.Field()
|
||||||
|
|
||||||
def resolve_letters(self, args, context, info):
|
def resolve_letters(self, args, context, info):
|
||||||
return list(letters.values())
|
return list(letters.values())
|
||||||
|
|
||||||
node = Node.Field()
|
def resolve_connection_letters(self, args, context, info):
|
||||||
|
return Letter.Connection(
|
||||||
|
page_info=PageInfo(
|
||||||
|
has_next_page=True,
|
||||||
|
has_previous_page=False
|
||||||
|
),
|
||||||
|
edges=[
|
||||||
|
Letter.Connection.Edge(
|
||||||
|
node=Letter(id=0, letter='A'),
|
||||||
|
cursor='a-cursor'
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
schema = Schema(Query)
|
schema = Schema(Query)
|
||||||
|
@ -176,3 +191,40 @@ def test_returns_all_elements_if_cursors_are_on_the_outside():
|
||||||
|
|
||||||
def test_returns_no_elements_if_cursors_cross():
|
def test_returns_no_elements_if_cursors_cross():
|
||||||
check('before: "{}" after: "{}"'.format(base64('arrayconnection:%s' % 2), base64('arrayconnection:%s' % 4)), '')
|
check('before: "{}" after: "{}"'.format(base64('arrayconnection:%s' % 2), base64('arrayconnection:%s' % 4)), '')
|
||||||
|
|
||||||
|
|
||||||
|
def test_connection_type_nodes():
|
||||||
|
result = schema.execute('''
|
||||||
|
{
|
||||||
|
connectionLetters {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
letter
|
||||||
|
}
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasPreviousPage
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
'connectionLetters': {
|
||||||
|
'edges': [{
|
||||||
|
'node': {
|
||||||
|
'id': 'TGV0dGVyOjA=',
|
||||||
|
'letter': 'A',
|
||||||
|
},
|
||||||
|
'cursor': 'a-cursor',
|
||||||
|
}],
|
||||||
|
'pageInfo': {
|
||||||
|
'hasPreviousPage': False,
|
||||||
|
'hasNextPage': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -84,6 +84,9 @@ def test_mutation():
|
||||||
assert isinstance(field.args['input'], Argument)
|
assert isinstance(field.args['input'], Argument)
|
||||||
assert isinstance(field.args['input'].type, NonNull)
|
assert isinstance(field.args['input'].type, NonNull)
|
||||||
assert field.args['input'].type.of_type == SaySomething.Input
|
assert field.args['input'].type.of_type == SaySomething.Input
|
||||||
|
assert isinstance(fields['client_mutation_id'], Field)
|
||||||
|
assert fields['client_mutation_id'].name == 'clientMutationId'
|
||||||
|
assert fields['client_mutation_id'].type == String
|
||||||
|
|
||||||
|
|
||||||
def test_mutation_input():
|
def test_mutation_input():
|
||||||
|
@ -132,7 +135,7 @@ def test_node_query():
|
||||||
|
|
||||||
def test_edge_query():
|
def test_edge_query():
|
||||||
executed = schema.execute(
|
executed = schema.execute(
|
||||||
'mutation a { other(input: {clientMutationId:"1"}) { myNodeEdge { cursor node { name }} } }'
|
'mutation a { other(input: {clientMutationId:"1"}) { clientMutationId, myNodeEdge { cursor node { name }} } }'
|
||||||
)
|
)
|
||||||
assert not executed.errors
|
assert not executed.errors
|
||||||
assert dict(executed.data) == {'other': {'myNodeEdge': {'cursor': '1', 'node': {'name': 'name'}}}}
|
assert dict(executed.data) == {'other': {'clientMutationId': '1', 'myNodeEdge': {'cursor': '1', 'node': {'name': 'name'}}}}
|
||||||
|
|
|
@ -18,6 +18,14 @@ class Argument(OrderedType):
|
||||||
self.default_value = default_value
|
self.default_value = default_value
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Argument) and (
|
||||||
|
self.name == other.name,
|
||||||
|
self.type == other.type,
|
||||||
|
self.default_value == other.default_value,
|
||||||
|
self.description == other.description
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_arguments(args, extra_args):
|
def to_arguments(args, extra_args):
|
||||||
from .unmountedtype import UnmountedType
|
from .unmountedtype import UnmountedType
|
||||||
|
|
|
@ -36,4 +36,4 @@ class DateTime(Scalar):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_value(value):
|
def parse_value(value):
|
||||||
return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
|
return iso8601.parse_date(value)
|
||||||
|
|
|
@ -8,6 +8,9 @@ from .structures import NonNull
|
||||||
from .unmountedtype import UnmountedType
|
from .unmountedtype import UnmountedType
|
||||||
|
|
||||||
|
|
||||||
|
base_type = type
|
||||||
|
|
||||||
|
|
||||||
def source_resolver(source, root, args, context, info):
|
def source_resolver(source, root, args, context, info):
|
||||||
resolved = getattr(root, source, None)
|
resolved = getattr(root, source, None)
|
||||||
if inspect.isfunction(resolved):
|
if inspect.isfunction(resolved):
|
||||||
|
@ -19,7 +22,8 @@ class Field(OrderedType):
|
||||||
|
|
||||||
def __init__(self, type, args=None, resolver=None, source=None,
|
def __init__(self, type, args=None, resolver=None, source=None,
|
||||||
deprecation_reason=None, name=None, description=None,
|
deprecation_reason=None, name=None, description=None,
|
||||||
required=False, _creation_counter=None, **extra_args):
|
required=False, _creation_counter=None, default_value=None,
|
||||||
|
**extra_args):
|
||||||
super(Field, self).__init__(_creation_counter=_creation_counter)
|
super(Field, self).__init__(_creation_counter=_creation_counter)
|
||||||
assert not args or isinstance(args, Mapping), (
|
assert not args or isinstance(args, Mapping), (
|
||||||
'Arguments in a field have to be a mapping, received "{}".'
|
'Arguments in a field have to be a mapping, received "{}".'
|
||||||
|
@ -27,6 +31,9 @@ class Field(OrderedType):
|
||||||
assert not (source and resolver), (
|
assert not (source and resolver), (
|
||||||
'A Field cannot have a source and a resolver in at the same time.'
|
'A Field cannot have a source and a resolver in at the same time.'
|
||||||
)
|
)
|
||||||
|
assert not callable(default_value), (
|
||||||
|
'The default value can not be a function but received "{}".'
|
||||||
|
).format(base_type(default_value))
|
||||||
|
|
||||||
if required:
|
if required:
|
||||||
type = NonNull(type)
|
type = NonNull(type)
|
||||||
|
@ -49,6 +56,7 @@ class Field(OrderedType):
|
||||||
self.resolver = resolver
|
self.resolver = resolver
|
||||||
self.deprecation_reason = deprecation_reason
|
self.deprecation_reason = deprecation_reason
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.default_value = default_value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
|
|
|
@ -48,7 +48,11 @@ class Interface(six.with_metaclass(InterfaceMeta)):
|
||||||
when the field is resolved.
|
when the field is resolved.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
resolve_type = None
|
@classmethod
|
||||||
|
def resolve_type(cls, instance, context, info):
|
||||||
|
from .objecttype import ObjectType
|
||||||
|
if isinstance(instance, ObjectType):
|
||||||
|
return type(instance)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
raise Exception("An Interface cannot be intitialized")
|
raise Exception("An Interface cannot be intitialized")
|
||||||
|
|
|
@ -3,6 +3,7 @@ from functools import partial
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ..utils.is_base_type import is_base_type
|
from ..utils.is_base_type import is_base_type
|
||||||
|
from ..utils.get_unbound_function import get_unbound_function
|
||||||
from ..utils.props import props
|
from ..utils.props import props
|
||||||
from .field import Field
|
from .field import Field
|
||||||
from .objecttype import ObjectType, ObjectTypeMeta
|
from .objecttype import ObjectType, ObjectTypeMeta
|
||||||
|
@ -22,6 +23,7 @@ class MutationMeta(ObjectTypeMeta):
|
||||||
field_args = props(input_class) if input_class else {}
|
field_args = props(input_class) if input_class else {}
|
||||||
resolver = getattr(cls, 'mutate', None)
|
resolver = getattr(cls, 'mutate', None)
|
||||||
assert resolver, 'All mutations must define a mutate method in it'
|
assert resolver, 'All mutations must define a mutate method in it'
|
||||||
|
resolver = get_unbound_function(resolver)
|
||||||
cls.Field = partial(Field, cls, args=field_args, resolver=resolver)
|
cls.Field = partial(Field, cls, args=field_args, resolver=resolver)
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
|
@ -63,10 +63,7 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
||||||
have a name, but most importantly describe their fields.
|
have a name, but most importantly describe their fields.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@classmethod
|
is_type_of = None
|
||||||
def is_type_of(cls, root, context, info):
|
|
||||||
if isinstance(root, cls):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# ObjectType acting as container
|
# ObjectType acting as container
|
||||||
|
|
|
@ -27,6 +27,13 @@ class List(Structure):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '[{}]'.format(self.of_type)
|
return '[{}]'.format(self.of_type)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, List) and (
|
||||||
|
self.of_type == other.of_type and
|
||||||
|
self.args == other.args and
|
||||||
|
self.kwargs == other.kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class NonNull(Structure):
|
class NonNull(Structure):
|
||||||
'''
|
'''
|
||||||
|
@ -49,3 +56,10 @@ class NonNull(Structure):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}!'.format(self.of_type)
|
return '{}!'.format(self.of_type)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, NonNull) and (
|
||||||
|
self.of_type == other.of_type and
|
||||||
|
self.args == other.args and
|
||||||
|
self.kwargs == other.kwargs
|
||||||
|
)
|
||||||
|
|
26
graphene/types/tests/test_argument.py
Normal file
26
graphene/types/tests/test_argument.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ..argument import Argument
|
||||||
|
from ..structures import NonNull
|
||||||
|
from ..scalars import String
|
||||||
|
|
||||||
|
|
||||||
|
def test_argument():
|
||||||
|
arg = Argument(String, default_value='a', description='desc', name='b')
|
||||||
|
assert arg.type == String
|
||||||
|
assert arg.default_value == 'a'
|
||||||
|
assert arg.description == 'desc'
|
||||||
|
assert arg.name == 'b'
|
||||||
|
|
||||||
|
|
||||||
|
def test_argument_comparasion():
|
||||||
|
arg1 = Argument(String, name='Hey', description='Desc', default_value='default')
|
||||||
|
arg2 = Argument(String, name='Hey', description='Desc', default_value='default')
|
||||||
|
|
||||||
|
assert arg1 == arg2
|
||||||
|
assert arg1 != String()
|
||||||
|
|
||||||
|
|
||||||
|
def test_argument_required():
|
||||||
|
arg = Argument(String, required=True)
|
||||||
|
assert arg.type == NonNull(String)
|
41
graphene/types/tests/test_datetime.py
Normal file
41
graphene/types/tests/test_datetime.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import datetime
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
from ..datetime import DateTime
|
||||||
|
from ..objecttype import ObjectType
|
||||||
|
from ..schema import Schema
|
||||||
|
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
datetime = DateTime(_in=DateTime(name='in'))
|
||||||
|
|
||||||
|
def resolve_datetime(self, args, context, info):
|
||||||
|
_in = args.get('in')
|
||||||
|
return _in
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_datetime_query():
|
||||||
|
now = datetime.datetime.now().replace(tzinfo=pytz.utc)
|
||||||
|
isoformat = now.isoformat()
|
||||||
|
|
||||||
|
result = schema.execute('''{ datetime(in: "%s") }'''%isoformat)
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
'datetime': isoformat
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_datetime_query_variable():
|
||||||
|
now = datetime.datetime.now().replace(tzinfo=pytz.utc)
|
||||||
|
isoformat = now.isoformat()
|
||||||
|
|
||||||
|
result = schema.execute(
|
||||||
|
'''query Test($date: DateTime){ datetime(in: $date) }''',
|
||||||
|
variable_values={'date': isoformat}
|
||||||
|
)
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
'datetime': isoformat
|
||||||
|
}
|
|
@ -16,19 +16,22 @@ def test_field_basic():
|
||||||
resolver = lambda: None
|
resolver = lambda: None
|
||||||
deprecation_reason = 'Deprecated now'
|
deprecation_reason = 'Deprecated now'
|
||||||
description = 'My Field'
|
description = 'My Field'
|
||||||
|
my_default='something'
|
||||||
field = Field(
|
field = Field(
|
||||||
MyType,
|
MyType,
|
||||||
name='name',
|
name='name',
|
||||||
args=args,
|
args=args,
|
||||||
resolver=resolver,
|
resolver=resolver,
|
||||||
description=description,
|
description=description,
|
||||||
deprecation_reason=deprecation_reason
|
deprecation_reason=deprecation_reason,
|
||||||
|
default_value=my_default,
|
||||||
)
|
)
|
||||||
assert field.name == 'name'
|
assert field.name == 'name'
|
||||||
assert field.args == args
|
assert field.args == args
|
||||||
assert field.resolver == resolver
|
assert field.resolver == resolver
|
||||||
assert field.deprecation_reason == deprecation_reason
|
assert field.deprecation_reason == deprecation_reason
|
||||||
assert field.description == description
|
assert field.description == description
|
||||||
|
assert field.default_value == my_default
|
||||||
|
|
||||||
|
|
||||||
def test_field_required():
|
def test_field_required():
|
||||||
|
@ -38,6 +41,15 @@ def test_field_required():
|
||||||
assert field.type.of_type == MyType
|
assert field.type.of_type == MyType
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_default_value_not_callable():
|
||||||
|
MyType = object()
|
||||||
|
try:
|
||||||
|
Field(MyType, default_value=lambda: True)
|
||||||
|
except AssertionError as e:
|
||||||
|
# substring comparison for py 2/3 compatibility
|
||||||
|
assert 'The default value can not be a function but received' in str(e)
|
||||||
|
|
||||||
|
|
||||||
def test_field_source():
|
def test_field_source():
|
||||||
MyType = object()
|
MyType = object()
|
||||||
field = Field(MyType, source='value')
|
field = Field(MyType, source='value')
|
||||||
|
|
39
graphene/types/tests/test_json.py
Normal file
39
graphene/types/tests/test_json.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from ..json import JSONString
|
||||||
|
from ..objecttype import ObjectType
|
||||||
|
from ..schema import Schema
|
||||||
|
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
json = JSONString(input=JSONString())
|
||||||
|
|
||||||
|
def resolve_json(self, args, context, info):
|
||||||
|
input = args.get('input')
|
||||||
|
return input
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_jsonstring_query():
|
||||||
|
json_value = '{"key": "value"}'
|
||||||
|
|
||||||
|
json_value_quoted = json_value.replace('"', '\\"')
|
||||||
|
result = schema.execute('''{ json(input: "%s") }'''%json_value_quoted)
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
'json': json_value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_jsonstring_query_variable():
|
||||||
|
json_value = '{"key": "value"}'
|
||||||
|
|
||||||
|
result = schema.execute(
|
||||||
|
'''query Test($json: JSONString){ json(input: $json) }''',
|
||||||
|
variable_values={'json': json_value}
|
||||||
|
)
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
'json': json_value
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ import pytest
|
||||||
|
|
||||||
from ..mutation import Mutation
|
from ..mutation import Mutation
|
||||||
from ..objecttype import ObjectType
|
from ..objecttype import ObjectType
|
||||||
|
from ..schema import Schema
|
||||||
|
from ..scalars import String
|
||||||
|
|
||||||
|
|
||||||
def test_generate_mutation_no_args():
|
def test_generate_mutation_no_args():
|
||||||
|
@ -17,26 +19,6 @@ def test_generate_mutation_no_args():
|
||||||
assert MyMutation.Field().resolver == MyMutation.mutate
|
assert MyMutation.Field().resolver == MyMutation.mutate
|
||||||
|
|
||||||
|
|
||||||
# def test_generate_mutation_with_args():
|
|
||||||
# class MyMutation(Mutation):
|
|
||||||
# '''Documentation'''
|
|
||||||
# class Input:
|
|
||||||
# s = String()
|
|
||||||
|
|
||||||
# @classmethod
|
|
||||||
# def mutate(cls, *args, **kwargs):
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# graphql_type = MyMutation._meta.graphql_type
|
|
||||||
# field = MyMutation.Field()
|
|
||||||
# assert graphql_type.name == "MyMutation"
|
|
||||||
# assert graphql_type.description == "Documentation"
|
|
||||||
# assert isinstance(field, Field)
|
|
||||||
# assert field.type == MyMutation._meta.graphql_type
|
|
||||||
# assert 's' in field.args
|
|
||||||
# assert field.args['s'].type == String
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_mutation_with_meta():
|
def test_generate_mutation_with_meta():
|
||||||
class MyMutation(Mutation):
|
class MyMutation(Mutation):
|
||||||
|
|
||||||
|
@ -59,3 +41,35 @@ def test_mutation_raises_exception_if_no_mutate():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
assert "All mutations must define a mutate method in it" == str(excinfo.value)
|
assert "All mutations must define a mutate method in it" == str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_mutation_execution():
|
||||||
|
class CreateUser(Mutation):
|
||||||
|
class Input:
|
||||||
|
name = String()
|
||||||
|
|
||||||
|
name = String()
|
||||||
|
|
||||||
|
def mutate(self, args, context, info):
|
||||||
|
name = args.get('name')
|
||||||
|
return CreateUser(name=name)
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
a = String()
|
||||||
|
|
||||||
|
class MyMutation(ObjectType):
|
||||||
|
create_user = CreateUser.Field()
|
||||||
|
|
||||||
|
schema = Schema(query=Query, mutation=MyMutation)
|
||||||
|
result = schema.execute(''' mutation mymutation {
|
||||||
|
createUser(name:"Peter") {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {
|
||||||
|
'createUser': {
|
||||||
|
'name': "Peter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import json
|
import json
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from graphql import Source, execute, parse
|
from graphql import Source, execute, parse, GraphQLError
|
||||||
|
|
||||||
|
from ..field import Field
|
||||||
from ..inputfield import InputField
|
from ..inputfield import InputField
|
||||||
from ..inputobjecttype import InputObjectType
|
from ..inputobjecttype import InputObjectType
|
||||||
from ..objecttype import ObjectType
|
from ..objecttype import ObjectType
|
||||||
|
@ -22,6 +23,53 @@ def test_query():
|
||||||
assert executed.data == {'hello': 'World'}
|
assert executed.data == {'hello': 'World'}
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_default_value():
|
||||||
|
class MyType(ObjectType):
|
||||||
|
field = String()
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
hello = Field(MyType, default_value=MyType(field='something else!'))
|
||||||
|
|
||||||
|
hello_schema = Schema(Query)
|
||||||
|
|
||||||
|
executed = hello_schema.execute('{ hello { field } }')
|
||||||
|
assert not executed.errors
|
||||||
|
assert executed.data == {'hello': {'field': 'something else!'}}
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_wrong_default_value():
|
||||||
|
class MyType(ObjectType):
|
||||||
|
field = String()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_type_of(cls, root, context, info):
|
||||||
|
return isinstance(root, MyType)
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
hello = Field(MyType, default_value='hello')
|
||||||
|
|
||||||
|
hello_schema = Schema(Query)
|
||||||
|
|
||||||
|
executed = hello_schema.execute('{ hello { field } }')
|
||||||
|
assert len(executed.errors) == 1
|
||||||
|
assert executed.errors[0].message == GraphQLError('Expected value of type "MyType" but got: str.').message
|
||||||
|
assert executed.data == {'hello': None}
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_default_value_ignored_by_resolver():
|
||||||
|
class MyType(ObjectType):
|
||||||
|
field = String()
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
hello = Field(MyType, default_value='hello', resolver=lambda *_: MyType(field='no default.'))
|
||||||
|
|
||||||
|
hello_schema = Schema(Query)
|
||||||
|
|
||||||
|
executed = hello_schema.execute('{ hello { field } }')
|
||||||
|
assert not executed.errors
|
||||||
|
assert executed.data == {'hello': {'field': 'no default.'}}
|
||||||
|
|
||||||
|
|
||||||
def test_query_resolve_function():
|
def test_query_resolve_function():
|
||||||
class Query(ObjectType):
|
class Query(ObjectType):
|
||||||
hello = String()
|
hello = String()
|
||||||
|
@ -109,6 +157,30 @@ def test_query_middlewares():
|
||||||
assert executed.data == {'hello': 'dlroW', 'other': 'rehto'}
|
assert executed.data == {'hello': 'dlroW', 'other': 'rehto'}
|
||||||
|
|
||||||
|
|
||||||
|
def test_objecttype_on_instances():
|
||||||
|
class Ship:
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
class ShipType(ObjectType):
|
||||||
|
name = String(description="Ship name", required=True)
|
||||||
|
|
||||||
|
def resolve_name(self, context, args, info):
|
||||||
|
# Here self will be the Ship instance returned in resolve_ship
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
ship = Field(ShipType)
|
||||||
|
|
||||||
|
def resolve_ship(self, context, args, info):
|
||||||
|
return Ship(name='xwing')
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
executed = schema.execute('{ ship { name } }')
|
||||||
|
assert not executed.errors
|
||||||
|
assert executed.data == {'ship': {'name': 'xwing'}}
|
||||||
|
|
||||||
|
|
||||||
def test_big_list_query_benchmark(benchmark):
|
def test_big_list_query_benchmark(benchmark):
|
||||||
big_list = range(10000)
|
big_list = range(10000)
|
||||||
|
|
||||||
|
|
44
graphene/types/tests/test_structures.py
Normal file
44
graphene/types/tests/test_structures.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ..structures import List, NonNull
|
||||||
|
from ..scalars import String
|
||||||
|
|
||||||
|
|
||||||
|
def test_list():
|
||||||
|
_list = List(String)
|
||||||
|
assert _list.of_type == String
|
||||||
|
assert str(_list) == '[String]'
|
||||||
|
|
||||||
|
|
||||||
|
def test_nonnull():
|
||||||
|
nonnull = NonNull(String)
|
||||||
|
assert nonnull.of_type == String
|
||||||
|
assert str(nonnull) == 'String!'
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_comparasion():
|
||||||
|
list1 = List(String)
|
||||||
|
list2 = List(String)
|
||||||
|
list3 = List(None)
|
||||||
|
|
||||||
|
list1_argskwargs = List(String, None, b=True)
|
||||||
|
list2_argskwargs = List(String, None, b=True)
|
||||||
|
|
||||||
|
assert list1 == list2
|
||||||
|
assert list1 != list3
|
||||||
|
assert list1_argskwargs == list2_argskwargs
|
||||||
|
assert list1 != list1_argskwargs
|
||||||
|
|
||||||
|
|
||||||
|
def test_nonnull_comparasion():
|
||||||
|
nonnull1 = NonNull(String)
|
||||||
|
nonnull2 = NonNull(String)
|
||||||
|
nonnull3 = NonNull(None)
|
||||||
|
|
||||||
|
nonnull1_argskwargs = NonNull(String, None, b=True)
|
||||||
|
nonnull2_argskwargs = NonNull(String, None, b=True)
|
||||||
|
|
||||||
|
assert nonnull1 == nonnull2
|
||||||
|
assert nonnull1 != nonnull3
|
||||||
|
assert nonnull1_argskwargs == nonnull2_argskwargs
|
||||||
|
assert nonnull1 != nonnull1_argskwargs
|
|
@ -6,9 +6,11 @@ from graphql import (GraphQLArgument, GraphQLBoolean, GraphQLField,
|
||||||
GraphQLFloat, GraphQLID, GraphQLInputObjectField,
|
GraphQLFloat, GraphQLID, GraphQLInputObjectField,
|
||||||
GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLString)
|
GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLString)
|
||||||
from graphql.type import GraphQLEnumValue
|
from graphql.type import GraphQLEnumValue
|
||||||
|
from graphql.execution.executor import get_default_resolve_type_fn
|
||||||
from graphql.type.typemap import GraphQLTypeMap
|
from graphql.type.typemap import GraphQLTypeMap
|
||||||
|
|
||||||
from ..utils.str_converters import to_camel_case
|
from ..utils.str_converters import to_camel_case
|
||||||
|
from ..utils.get_unbound_function import get_unbound_function
|
||||||
from .dynamic import Dynamic
|
from .dynamic import Dynamic
|
||||||
from .enum import Enum
|
from .enum import Enum
|
||||||
from .inputobjecttype import InputObjectType
|
from .inputobjecttype import InputObjectType
|
||||||
|
@ -26,11 +28,14 @@ def is_graphene_type(_type):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def resolve_type(resolve_type_func, map, root, args, info):
|
def resolve_type(resolve_type_func, map, root, context, info):
|
||||||
_type = resolve_type_func(root, args, info)
|
_type = resolve_type_func(root, context, info)
|
||||||
# assert inspect.isclass(_type) and issubclass(_type, ObjectType), (
|
# assert inspect.isclass(_type) and issubclass(_type, ObjectType), (
|
||||||
# 'Received incompatible type "{}".'.format(_type)
|
# 'Received incompatible type "{}".'.format(_type)
|
||||||
# )
|
# )
|
||||||
|
if not _type:
|
||||||
|
return get_default_resolve_type_fn(root, context, info, info.return_type)
|
||||||
|
|
||||||
if inspect.isclass(_type) and issubclass(_type, ObjectType):
|
if inspect.isclass(_type) and issubclass(_type, ObjectType):
|
||||||
graphql_type = map.get(_type._meta.name)
|
graphql_type = map.get(_type._meta.name)
|
||||||
assert graphql_type and graphql_type.graphene_type == _type
|
assert graphql_type and graphql_type.graphene_type == _type
|
||||||
|
@ -190,8 +195,8 @@ class TypeMap(GraphQLTypeMap):
|
||||||
return to_camel_case(name)
|
return to_camel_case(name)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def default_resolver(self, attname, root, *_):
|
def default_resolver(self, attname, default_value, root, *_):
|
||||||
return getattr(root, attname, None)
|
return getattr(root, attname, default_value)
|
||||||
|
|
||||||
def construct_fields_for_type(self, map, type, is_input_type=False):
|
def construct_fields_for_type(self, map, type, is_input_type=False):
|
||||||
fields = OrderedDict()
|
fields = OrderedDict()
|
||||||
|
@ -224,7 +229,7 @@ class TypeMap(GraphQLTypeMap):
|
||||||
_field = GraphQLField(
|
_field = GraphQLField(
|
||||||
field_type,
|
field_type,
|
||||||
args=args,
|
args=args,
|
||||||
resolver=field.get_resolver(self.get_resolver_for_type(type, name)),
|
resolver=field.get_resolver(self.get_resolver_for_type(type, name, field.default_value)),
|
||||||
deprecation_reason=field.deprecation_reason,
|
deprecation_reason=field.deprecation_reason,
|
||||||
description=field.description
|
description=field.description
|
||||||
)
|
)
|
||||||
|
@ -232,7 +237,7 @@ class TypeMap(GraphQLTypeMap):
|
||||||
fields[field_name] = _field
|
fields[field_name] = _field
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def get_resolver_for_type(self, type, name):
|
def get_resolver_for_type(self, type, name, default_value):
|
||||||
if not issubclass(type, ObjectType):
|
if not issubclass(type, ObjectType):
|
||||||
return
|
return
|
||||||
resolver = getattr(type, 'resolve_{}'.format(name), None)
|
resolver = getattr(type, 'resolve_{}'.format(name), None)
|
||||||
|
@ -247,13 +252,12 @@ class TypeMap(GraphQLTypeMap):
|
||||||
if interface_resolver:
|
if interface_resolver:
|
||||||
break
|
break
|
||||||
resolver = interface_resolver
|
resolver = interface_resolver
|
||||||
|
|
||||||
# Only if is not decorated with classmethod
|
# Only if is not decorated with classmethod
|
||||||
if resolver:
|
if resolver:
|
||||||
if not getattr(resolver, '__self__', True):
|
return get_unbound_function(resolver)
|
||||||
return resolver.__func__
|
|
||||||
return resolver
|
|
||||||
|
|
||||||
return partial(self.default_resolver, name)
|
return partial(self.default_resolver, name, default_value)
|
||||||
|
|
||||||
def get_field_type(self, map, type):
|
def get_field_type(self, map, type):
|
||||||
if isinstance(type, List):
|
if isinstance(type, List):
|
||||||
|
|
4
graphene/utils/get_unbound_function.py
Normal file
4
graphene/utils/get_unbound_function.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
def get_unbound_function(func):
|
||||||
|
if not getattr(func, '__self__', True):
|
||||||
|
return func.__func__
|
||||||
|
return func
|
4
setup.py
4
setup.py
|
@ -70,7 +70,7 @@ setup(
|
||||||
|
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'six>=1.10.0',
|
'six>=1.10.0',
|
||||||
'graphql-core>=1.0.dev',
|
'graphql-core>=1.0',
|
||||||
'graphql-relay>=0.4.4',
|
'graphql-relay>=0.4.4',
|
||||||
'promise',
|
'promise',
|
||||||
],
|
],
|
||||||
|
@ -78,6 +78,8 @@ setup(
|
||||||
'pytest>=2.7.2',
|
'pytest>=2.7.2',
|
||||||
'pytest-benchmark',
|
'pytest-benchmark',
|
||||||
'mock',
|
'mock',
|
||||||
|
'pytz',
|
||||||
|
'iso8601',
|
||||||
],
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
'django': [
|
'django': [
|
||||||
|
|
Loading…
Reference in New Issue
Block a user