mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-11 04:07:16 +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.*
|
||||
.cache
|
||||
.pytest_cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,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
|
||||
> 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
|
||||
|
||||
|
|
|
@ -55,12 +55,12 @@ Example of a custom node:
|
|||
return '{}:{}'.format(type, id)
|
||||
|
||||
@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(':')
|
||||
if only_node:
|
||||
if only_type:
|
||||
# We assure that the node type that we want to retrieve
|
||||
# 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':
|
||||
return get_user(id)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Required library
|
||||
Sphinx==1.5.3
|
||||
# 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.
|
||||
It works the same for arguments, where the validation step will expect a list
|
||||
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):
|
||||
type = super(IterableConnectionField, self).type
|
||||
connection_type = type
|
||||
if is_node(type):
|
||||
if isinstance(type, NonNull):
|
||||
connection_type = type.of_type
|
||||
|
||||
if is_node(connection_type):
|
||||
raise Exception(
|
||||
"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), (
|
||||
'{} type have to be a subclass of Connection. Received "{}".'
|
||||
).format(self.__class__.__name__, connection_type)
|
||||
return connection_type
|
||||
return type
|
||||
|
||||
@classmethod
|
||||
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):
|
||||
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)
|
||||
if is_thenable(resolved):
|
||||
return Promise.resolve(resolved).then(on_resolve)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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 ..node import Node
|
||||
|
||||
|
@ -155,3 +155,23 @@ def test_connectionfield_custom_args():
|
|||
'last': Argument(Int),
|
||||
'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):
|
||||
warn_deprecation(
|
||||
"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)
|
||||
|
|
|
@ -27,7 +27,11 @@ class EnumOptions(BaseOptions):
|
|||
class EnumMeta(SubclassWithMeta_Meta):
|
||||
|
||||
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)
|
||||
|
||||
def get(cls, value):
|
||||
|
|
|
@ -50,7 +50,8 @@ class Mutation(ObjectType):
|
|||
warn_deprecation((
|
||||
"Please use {name}.Arguments instead of {name}.Input."
|
||||
"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__))
|
||||
|
||||
if input_class:
|
||||
|
@ -75,7 +76,12 @@ class Mutation(ObjectType):
|
|||
super(Mutation, cls).__init_subclass_with_meta__(_meta=_meta, **options)
|
||||
|
||||
@classmethod
|
||||
def Field(cls, *args, **kwargs):
|
||||
def Field(cls, name=None, description=None, deprecation_reason=None):
|
||||
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
|
||||
|
||||
from graphql import GraphQLSchema, graphql, is_type
|
||||
from graphql import GraphQLSchema, graphql, is_type, GraphQLObjectType
|
||||
from graphql.type.directives import (GraphQLDirective, GraphQLIncludeDirective,
|
||||
GraphQLSkipDirective)
|
||||
from graphql.type.introspection import IntrospectionSchema
|
||||
|
@ -12,6 +12,17 @@ from .objecttype import ObjectType
|
|||
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):
|
||||
'''
|
||||
Schema Definition
|
||||
|
@ -20,21 +31,23 @@ class Schema(GraphQLSchema):
|
|||
query and mutation (optional).
|
||||
'''
|
||||
|
||||
def __init__(self, query=None, mutation=None, subscription=None,
|
||||
directives=None, types=None, auto_camelcase=True):
|
||||
assert inspect.isclass(query) and issubclass(query, ObjectType), (
|
||||
'Schema query must be Object Type but got: {}.'
|
||||
).format(query)
|
||||
def __init__(self,
|
||||
query=None,
|
||||
mutation=None,
|
||||
subscription=None,
|
||||
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._mutation = mutation
|
||||
self._subscription = subscription
|
||||
self.types = types
|
||||
self.auto_camelcase = auto_camelcase
|
||||
if directives is None:
|
||||
directives = [
|
||||
GraphQLIncludeDirective,
|
||||
GraphQLSkipDirective
|
||||
]
|
||||
directives = [GraphQLIncludeDirective, GraphQLSkipDirective]
|
||||
|
||||
assert all(isinstance(d, GraphQLDirective) for d in directives), \
|
||||
'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)
|
||||
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):
|
||||
return _type.graphene_type
|
||||
return _type
|
||||
|
@ -73,7 +87,8 @@ class Schema(GraphQLSchema):
|
|||
return _type
|
||||
if is_graphene_type(_type):
|
||||
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
|
||||
return graphql_type
|
||||
raise Exception("{} is not a valid GraphQL type.".format(_type))
|
||||
|
@ -102,4 +117,8 @@ class Schema(GraphQLSchema):
|
|||
]
|
||||
if 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
|
||||
|
||||
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):
|
||||
foo = Episode()
|
||||
|
@ -214,3 +215,19 @@ def test_enum_to_enum_comparison_should_differ():
|
|||
assert RGB1.RED != RGB2.RED
|
||||
assert RGB1.GREEN != RGB2.GREEN
|
||||
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',
|
||||
|
||||
packages=find_packages(exclude=['tests', 'tests.*']),
|
||||
packages=find_packages(exclude=['tests', 'tests.*', 'examples']),
|
||||
|
||||
install_requires=[
|
||||
'six>=1.10.0,<2',
|
||||
|
|
Loading…
Reference in New Issue
Block a user