mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-08 23:50:38 +03:00
Merge pull request #1 from graphql-python/master
Sync with original repo.
This commit is contained in:
commit
dbc2ee4da9
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -42,6 +42,7 @@ htmlcov/
|
||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
.coverage.*
|
||||||
.cache
|
.cache
|
||||||
|
.pytest_cache
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
*,cover
|
*,cover
|
||||||
|
|
|
@ -21,7 +21,7 @@ developer has to write to use them.
|
||||||
|
|
||||||
|
|
||||||
> 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
|
||||||
> on this strategy for creating custom attrs, see an [example on how to do it in 2.0](https://github.com/graphql-python/graphene/blob/2.0/graphene/tests/issues/test_425.py).
|
> on this strategy for creating custom attrs, see an [example on how to do it in 2.0](https://github.com/graphql-python/graphene/blob/v2.0.0/graphene/tests/issues/test_425.py).
|
||||||
|
|
||||||
## Deprecations
|
## Deprecations
|
||||||
|
|
||||||
|
|
|
@ -55,12 +55,12 @@ Example of a custom node:
|
||||||
return '{}:{}'.format(type, id)
|
return '{}:{}'.format(type, id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_node_from_global_id(info, global_id, only_node=None):
|
def get_node_from_global_id(info, global_id, only_type=None):
|
||||||
type, id = global_id.split(':')
|
type, id = global_id.split(':')
|
||||||
if only_node:
|
if only_type:
|
||||||
# We assure that the node type that we want to retrieve
|
# We assure that the node type that we want to retrieve
|
||||||
# is the same that was indicated in the field type
|
# is the same that was indicated in the field type
|
||||||
assert type == only_node._meta.name, 'Received not compatible node.'
|
assert type == only_type._meta.name, 'Received not compatible node.'
|
||||||
|
|
||||||
if type == 'User':
|
if type == 'User':
|
||||||
return get_user(id)
|
return get_user(id)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Required library
|
# Required library
|
||||||
Sphinx==1.5.3
|
Sphinx==1.5.3
|
||||||
# Docs template
|
# Docs template
|
||||||
https://github.com/graphql-python/graphene-python.org/archive/docs.zip
|
http://graphene-python.org/sphinx_graphene_theme.zip
|
||||||
|
|
|
@ -48,3 +48,24 @@ Lists work in a similar way: We can use a type modifier to mark a type as a
|
||||||
``List``, which indicates that this field will return a list of that type.
|
``List``, which indicates that this field will return a list of that type.
|
||||||
It works the same for arguments, where the validation step will expect a list
|
It works the same for arguments, where the validation step will expect a list
|
||||||
for that value.
|
for that value.
|
||||||
|
|
||||||
|
NonNull Lists
|
||||||
|
-------------
|
||||||
|
|
||||||
|
By default items in a list will be considered nullable. To define a list without
|
||||||
|
any nullable items the type needs to be marked as ``NonNull``. For example:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
import graphene
|
||||||
|
|
||||||
|
class Character(graphene.ObjectType):
|
||||||
|
appears_in = graphene.List(graphene.NonNull(graphene.String))
|
||||||
|
|
||||||
|
The above results in the type definition:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
type Character {
|
||||||
|
appearsIn: [String!]
|
||||||
|
}
|
||||||
|
|
|
@ -99,16 +99,19 @@ class IterableConnectionField(Field):
|
||||||
def type(self):
|
def type(self):
|
||||||
type = super(IterableConnectionField, self).type
|
type = super(IterableConnectionField, self).type
|
||||||
connection_type = type
|
connection_type = type
|
||||||
if is_node(type):
|
if isinstance(type, NonNull):
|
||||||
|
connection_type = type.of_type
|
||||||
|
|
||||||
|
if is_node(connection_type):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"ConnectionField's now need a explicit ConnectionType for Nodes.\n"
|
"ConnectionField's now need a explicit ConnectionType for Nodes.\n"
|
||||||
"Read more: https://github.com/graphql-python/graphene/blob/2.0/UPGRADE-v2.0.md#node-connections"
|
"Read more: https://github.com/graphql-python/graphene/blob/v2.0.0/UPGRADE-v2.0.md#node-connections"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert issubclass(connection_type, Connection), (
|
assert issubclass(connection_type, Connection), (
|
||||||
'{} type have to be a subclass of Connection. Received "{}".'
|
'{} type have to be a subclass of Connection. Received "{}".'
|
||||||
).format(self.__class__.__name__, connection_type)
|
).format(self.__class__.__name__, connection_type)
|
||||||
return connection_type
|
return type
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resolve_connection(cls, connection_type, args, resolved):
|
def resolve_connection(cls, connection_type, args, resolved):
|
||||||
|
@ -133,6 +136,9 @@ class IterableConnectionField(Field):
|
||||||
def connection_resolver(cls, resolver, connection_type, root, info, **args):
|
def connection_resolver(cls, resolver, connection_type, root, info, **args):
|
||||||
resolved = resolver(root, info, **args)
|
resolved = resolver(root, info, **args)
|
||||||
|
|
||||||
|
if isinstance(connection_type, NonNull):
|
||||||
|
connection_type = connection_type.of_type
|
||||||
|
|
||||||
on_resolve = partial(cls.resolve_connection, connection_type, args)
|
on_resolve = partial(cls.resolve_connection, connection_type, args)
|
||||||
if is_thenable(resolved):
|
if is_thenable(resolved):
|
||||||
return Promise.resolve(resolved).then(on_resolve)
|
return Promise.resolve(resolved).then(on_resolve)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ...types import Argument, Field, Int, List, NonNull, ObjectType, String
|
from ...types import Argument, Field, Int, List, NonNull, ObjectType, String, Schema
|
||||||
from ..connection import Connection, ConnectionField, PageInfo
|
from ..connection import Connection, ConnectionField, PageInfo
|
||||||
from ..node import Node
|
from ..node import Node
|
||||||
|
|
||||||
|
@ -155,3 +155,23 @@ def test_connectionfield_custom_args():
|
||||||
'last': Argument(Int),
|
'last': Argument(Int),
|
||||||
'extra': Argument(String),
|
'extra': Argument(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_connectionfield_required():
|
||||||
|
class MyObjectConnection(Connection):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
node = MyObject
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
test_connection = ConnectionField(MyObjectConnection, required=True)
|
||||||
|
|
||||||
|
def resolve_test_connection(root, info, **args):
|
||||||
|
return []
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
executed = schema.execute(
|
||||||
|
'{ testConnection { edges { cursor } } }'
|
||||||
|
)
|
||||||
|
assert not executed.errors
|
||||||
|
assert executed.data == {'testConnection': {'edges': []}}
|
||||||
|
|
|
@ -7,6 +7,6 @@ class AbstractType(SubclassWithMeta):
|
||||||
def __init_subclass__(cls, *args, **kwargs):
|
def __init_subclass__(cls, *args, **kwargs):
|
||||||
warn_deprecation(
|
warn_deprecation(
|
||||||
"Abstract type is deprecated, please use normal object inheritance instead.\n"
|
"Abstract type is deprecated, please use normal object inheritance instead.\n"
|
||||||
"See more: https://github.com/graphql-python/graphene/blob/2.0/UPGRADE-v2.0.md#deprecations"
|
"See more: https://github.com/graphql-python/graphene/blob/v2.0.0/UPGRADE-v2.0.md#deprecations"
|
||||||
)
|
)
|
||||||
super(AbstractType, cls).__init_subclass__(*args, **kwargs)
|
super(AbstractType, cls).__init_subclass__(*args, **kwargs)
|
||||||
|
|
|
@ -27,7 +27,11 @@ class EnumOptions(BaseOptions):
|
||||||
class EnumMeta(SubclassWithMeta_Meta):
|
class EnumMeta(SubclassWithMeta_Meta):
|
||||||
|
|
||||||
def __new__(cls, name, bases, classdict, **options):
|
def __new__(cls, name, bases, classdict, **options):
|
||||||
enum = PyEnum(cls.__name__, OrderedDict(classdict, __eq__=eq_enum))
|
enum_members = OrderedDict(classdict, __eq__=eq_enum)
|
||||||
|
# We remove the Meta attribute from the class to not collide
|
||||||
|
# with the enum values.
|
||||||
|
enum_members.pop('Meta', None)
|
||||||
|
enum = PyEnum(cls.__name__, enum_members)
|
||||||
return SubclassWithMeta_Meta.__new__(cls, name, bases, OrderedDict(classdict, __enum__=enum), **options)
|
return SubclassWithMeta_Meta.__new__(cls, name, bases, OrderedDict(classdict, __enum__=enum), **options)
|
||||||
|
|
||||||
def get(cls, value):
|
def get(cls, value):
|
||||||
|
|
|
@ -50,7 +50,8 @@ class Mutation(ObjectType):
|
||||||
warn_deprecation((
|
warn_deprecation((
|
||||||
"Please use {name}.Arguments instead of {name}.Input."
|
"Please use {name}.Arguments instead of {name}.Input."
|
||||||
"Input is now only used in ClientMutationID.\n"
|
"Input is now only used in ClientMutationID.\n"
|
||||||
"Read more: https://github.com/graphql-python/graphene/blob/2.0/UPGRADE-v2.0.md#mutation-input"
|
"Read more:"
|
||||||
|
" https://github.com/graphql-python/graphene/blob/v2.0.0/UPGRADE-v2.0.md#mutation-input"
|
||||||
).format(name=cls.__name__))
|
).format(name=cls.__name__))
|
||||||
|
|
||||||
if input_class:
|
if input_class:
|
||||||
|
@ -75,7 +76,12 @@ class Mutation(ObjectType):
|
||||||
super(Mutation, cls).__init_subclass_with_meta__(_meta=_meta, **options)
|
super(Mutation, cls).__init_subclass_with_meta__(_meta=_meta, **options)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def Field(cls, *args, **kwargs):
|
def Field(cls, name=None, description=None, deprecation_reason=None):
|
||||||
return Field(
|
return Field(
|
||||||
cls._meta.output, args=cls._meta.arguments, resolver=cls._meta.resolver
|
cls._meta.output,
|
||||||
|
args=cls._meta.arguments,
|
||||||
|
resolver=cls._meta.resolver,
|
||||||
|
name=name,
|
||||||
|
description=description,
|
||||||
|
deprecation_reason=deprecation_reason,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from graphql import GraphQLSchema, graphql, is_type
|
from graphql import GraphQLSchema, graphql, is_type, GraphQLObjectType
|
||||||
from graphql.type.directives import (GraphQLDirective, GraphQLIncludeDirective,
|
from graphql.type.directives import (GraphQLDirective, GraphQLIncludeDirective,
|
||||||
GraphQLSkipDirective)
|
GraphQLSkipDirective)
|
||||||
from graphql.type.introspection import IntrospectionSchema
|
from graphql.type.introspection import IntrospectionSchema
|
||||||
|
@ -12,6 +12,17 @@ from .objecttype import ObjectType
|
||||||
from .typemap import TypeMap, is_graphene_type
|
from .typemap import TypeMap, is_graphene_type
|
||||||
|
|
||||||
|
|
||||||
|
def assert_valid_root_type(_type):
|
||||||
|
if _type is None:
|
||||||
|
return
|
||||||
|
is_graphene_objecttype = inspect.isclass(
|
||||||
|
_type) and issubclass(_type, ObjectType)
|
||||||
|
is_graphql_objecttype = isinstance(_type, GraphQLObjectType)
|
||||||
|
assert is_graphene_objecttype or is_graphql_objecttype, (
|
||||||
|
"Type {} is not a valid ObjectType."
|
||||||
|
).format(_type)
|
||||||
|
|
||||||
|
|
||||||
class Schema(GraphQLSchema):
|
class Schema(GraphQLSchema):
|
||||||
'''
|
'''
|
||||||
Schema Definition
|
Schema Definition
|
||||||
|
@ -20,21 +31,23 @@ class Schema(GraphQLSchema):
|
||||||
query and mutation (optional).
|
query and mutation (optional).
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, query=None, mutation=None, subscription=None,
|
def __init__(self,
|
||||||
directives=None, types=None, auto_camelcase=True):
|
query=None,
|
||||||
assert inspect.isclass(query) and issubclass(query, ObjectType), (
|
mutation=None,
|
||||||
'Schema query must be Object Type but got: {}.'
|
subscription=None,
|
||||||
).format(query)
|
directives=None,
|
||||||
|
types=None,
|
||||||
|
auto_camelcase=True):
|
||||||
|
assert_valid_root_type(query)
|
||||||
|
assert_valid_root_type(mutation)
|
||||||
|
assert_valid_root_type(subscription)
|
||||||
self._query = query
|
self._query = query
|
||||||
self._mutation = mutation
|
self._mutation = mutation
|
||||||
self._subscription = subscription
|
self._subscription = subscription
|
||||||
self.types = types
|
self.types = types
|
||||||
self.auto_camelcase = auto_camelcase
|
self.auto_camelcase = auto_camelcase
|
||||||
if directives is None:
|
if directives is None:
|
||||||
directives = [
|
directives = [GraphQLIncludeDirective, GraphQLSkipDirective]
|
||||||
GraphQLIncludeDirective,
|
|
||||||
GraphQLSkipDirective
|
|
||||||
]
|
|
||||||
|
|
||||||
assert all(isinstance(d, GraphQLDirective) for d in directives), \
|
assert all(isinstance(d, GraphQLDirective) for d in directives), \
|
||||||
'Schema directives must be List[GraphQLDirective] if provided but got: {}.'.format(
|
'Schema directives must be List[GraphQLDirective] if provided but got: {}.'.format(
|
||||||
|
@ -61,7 +74,8 @@ class Schema(GraphQLSchema):
|
||||||
'''
|
'''
|
||||||
_type = super(Schema, self).get_type(type_name)
|
_type = super(Schema, self).get_type(type_name)
|
||||||
if _type is None:
|
if _type is None:
|
||||||
raise AttributeError('Type "{}" not found in the Schema'.format(type_name))
|
raise AttributeError(
|
||||||
|
'Type "{}" not found in the Schema'.format(type_name))
|
||||||
if isinstance(_type, GrapheneGraphQLType):
|
if isinstance(_type, GrapheneGraphQLType):
|
||||||
return _type.graphene_type
|
return _type.graphene_type
|
||||||
return _type
|
return _type
|
||||||
|
@ -73,7 +87,8 @@ class Schema(GraphQLSchema):
|
||||||
return _type
|
return _type
|
||||||
if is_graphene_type(_type):
|
if is_graphene_type(_type):
|
||||||
graphql_type = self.get_type(_type._meta.name)
|
graphql_type = self.get_type(_type._meta.name)
|
||||||
assert graphql_type, "Type {} not found in this schema.".format(_type._meta.name)
|
assert graphql_type, "Type {} not found in this schema.".format(
|
||||||
|
_type._meta.name)
|
||||||
assert graphql_type.graphene_type == _type
|
assert graphql_type.graphene_type == _type
|
||||||
return graphql_type
|
return graphql_type
|
||||||
raise Exception("{} is not a valid GraphQL type.".format(_type))
|
raise Exception("{} is not a valid GraphQL type.".format(_type))
|
||||||
|
@ -102,4 +117,8 @@ class Schema(GraphQLSchema):
|
||||||
]
|
]
|
||||||
if self.types:
|
if self.types:
|
||||||
initial_types += self.types
|
initial_types += self.types
|
||||||
self._type_map = TypeMap(initial_types, auto_camelcase=self.auto_camelcase, schema=self)
|
self._type_map = TypeMap(
|
||||||
|
initial_types,
|
||||||
|
auto_camelcase=self.auto_camelcase,
|
||||||
|
schema=self
|
||||||
|
)
|
||||||
|
|
|
@ -80,7 +80,8 @@ def test_enum_from_builtin_enum_accepts_lambda_description():
|
||||||
return 'meh' if value == Episode.NEWHOPE else None
|
return 'meh' if value == Episode.NEWHOPE else None
|
||||||
|
|
||||||
PyEpisode = PyEnum('PyEpisode', 'NEWHOPE,EMPIRE,JEDI')
|
PyEpisode = PyEnum('PyEpisode', 'NEWHOPE,EMPIRE,JEDI')
|
||||||
Episode = Enum.from_enum(PyEpisode, description=custom_description, deprecation_reason=custom_deprecation_reason)
|
Episode = Enum.from_enum(PyEpisode, description=custom_description,
|
||||||
|
deprecation_reason=custom_deprecation_reason)
|
||||||
|
|
||||||
class Query(ObjectType):
|
class Query(ObjectType):
|
||||||
foo = Episode()
|
foo = Episode()
|
||||||
|
@ -214,3 +215,19 @@ def test_enum_to_enum_comparison_should_differ():
|
||||||
assert RGB1.RED != RGB2.RED
|
assert RGB1.RED != RGB2.RED
|
||||||
assert RGB1.GREEN != RGB2.GREEN
|
assert RGB1.GREEN != RGB2.GREEN
|
||||||
assert RGB1.BLUE != RGB2.BLUE
|
assert RGB1.BLUE != RGB2.BLUE
|
||||||
|
|
||||||
|
|
||||||
|
def test_enum_skip_meta_from_members():
|
||||||
|
class RGB1(Enum):
|
||||||
|
class Meta:
|
||||||
|
name = 'RGB'
|
||||||
|
|
||||||
|
RED = 1
|
||||||
|
GREEN = 2
|
||||||
|
BLUE = 3
|
||||||
|
|
||||||
|
assert dict(RGB1._meta.enum.__members__) == {
|
||||||
|
'RED': RGB1.RED,
|
||||||
|
'GREEN': RGB1.GREEN,
|
||||||
|
'BLUE': RGB1.BLUE,
|
||||||
|
}
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -83,7 +83,7 @@ setup(
|
||||||
|
|
||||||
keywords='api graphql protocol rest relay graphene',
|
keywords='api graphql protocol rest relay graphene',
|
||||||
|
|
||||||
packages=find_packages(exclude=['tests', 'tests.*']),
|
packages=find_packages(exclude=['tests', 'tests.*', 'examples']),
|
||||||
|
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'six>=1.10.0,<2',
|
'six>=1.10.0,<2',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user