Use latest graphql-core 3.1.0b1 instead of 3.0.3

Adapt Schema, because there is no type reducer in core 3.1 any more.
This commit is contained in:
Christoph Zwerschke 2020-03-04 12:23:40 +01:00
parent ffb7701466
commit 5e6f68957e
13 changed files with 293 additions and 328 deletions

View File

@ -1,2 +1,2 @@
[settings] [settings]
known_third_party = aniso8601,graphql,graphql_relay,promise,pytest,pytz,pyutils,setuptools,six,snapshottest,sphinx_graphene_theme known_third_party = aniso8601,graphql,graphql_relay,promise,pytest,pytz,pyutils,setuptools,snapshottest,sphinx_graphene_theme

View File

@ -31,7 +31,16 @@ snapshots["test_correctly_refetches_xwing 1"] = {
snapshots[ snapshots[
"test_str_schema 1" "test_str_schema 1"
] = '''"""A faction in the Star Wars saga""" ] = '''type Query {
rebels: Faction
empire: Faction
node(
"""The ID of the object"""
id: ID!
): Node
}
"""A faction in the Star Wars saga"""
type Faction implements Node { type Faction implements Node {
"""The ID of the object""" """The ID of the object"""
id: ID! id: ID!
@ -43,28 +52,20 @@ type Faction implements Node {
ships(before: String = null, after: String = null, first: Int = null, last: Int = null): ShipConnection ships(before: String = null, after: String = null, first: Int = null, last: Int = null): ShipConnection
} }
input IntroduceShipInput {
shipName: String!
factionId: String!
clientMutationId: String
}
type IntroduceShipPayload {
ship: Ship
faction: Faction
clientMutationId: String
}
type Mutation {
introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
}
"""An object with an ID""" """An object with an ID"""
interface Node { interface Node {
"""The ID of the object""" """The ID of the object"""
id: ID! id: ID!
} }
type ShipConnection {
"""Pagination data for this connection."""
pageInfo: PageInfo!
"""Contains the nodes in this connection."""
edges: [ShipEdge]!
}
""" """
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection. The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
""" """
@ -82,13 +83,13 @@ type PageInfo {
endCursor: String endCursor: String
} }
type Query { """A Relay edge containing a `Ship` and its cursor."""
rebels: Faction type ShipEdge {
empire: Faction """The item at the end of the edge"""
node( node: Ship
"""The ID of the object"""
id: ID! """A cursor for use in pagination"""
): Node cursor: String!
} }
"""A ship in the Star Wars saga""" """A ship in the Star Wars saga"""
@ -100,20 +101,19 @@ type Ship implements Node {
name: String name: String
} }
type ShipConnection { type Mutation {
"""Pagination data for this connection.""" introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
pageInfo: PageInfo!
"""Contains the nodes in this connection."""
edges: [ShipEdge]!
} }
"""A Relay edge containing a `Ship` and its cursor.""" type IntroduceShipPayload {
type ShipEdge { ship: Ship
"""The item at the end of the edge""" faction: Faction
node: Ship clientMutationId: String
}
"""A cursor for use in pagination""" input IntroduceShipInput {
cursor: String! shipName: String!
factionId: String!
clientMutationId: String
} }
''' '''

View File

@ -42,7 +42,7 @@ from .utils.resolve_only_args import resolve_only_args
from .utils.module_loading import lazy_import from .utils.module_loading import lazy_import
VERSION = (3, 0, 0, "alpha", 1) VERSION = (3, 0, 0, "beta", 0)
__version__ = get_version(VERSION) __version__ = get_version(VERSION)

View File

@ -183,6 +183,12 @@ def test_str_schema():
name: String name: String
} }
"""An object with an ID"""
interface Node {
"""The ID of the object"""
id: ID!
}
type MyOtherNode implements Node { type MyOtherNode implements Node {
"""The ID of the object""" """The ID of the object"""
id: ID! id: ID!
@ -191,12 +197,6 @@ def test_str_schema():
extraField: String extraField: String
} }
"""An object with an ID"""
interface Node {
"""The ID of the object"""
id: ID!
}
type RootQuery { type RootQuery {
first: String first: String
node( node(

View File

@ -59,9 +59,12 @@ def test_str_schema_correct():
query: RootQuery query: RootQuery
} }
interface BasePhoto { type User implements Node {
"""The width of the photo in pixels""" """The ID of the object"""
width: Int id: ID!
"""The full name of the user"""
name: String
} }
interface Node { interface Node {
@ -77,20 +80,17 @@ def test_str_schema_correct():
width: Int width: Int
} }
interface BasePhoto {
"""The width of the photo in pixels"""
width: Int
}
type RootQuery { type RootQuery {
node( node(
"""The ID of the object""" """The ID of the object"""
id: ID! id: ID!
): Node ): Node
} }
type User implements Node {
"""The ID of the object"""
id: ID!
"""The full name of the user"""
name: String
}
''' '''
) )

View File

@ -27,7 +27,7 @@ def test_issue():
graphene.Schema(query=Query) graphene.Schema(query=Query)
assert str(exc_info.value) == ( assert str(exc_info.value) == (
"Query fields cannot be resolved:" "Query fields cannot be resolved."
" IterableConnectionField type has to be a subclass of Connection." " IterableConnectionField type has to be a subclass of Connection."
' Received "MyUnion".' ' Received "MyUnion".'
) )

View File

@ -3,8 +3,8 @@ from __future__ import absolute_import
import datetime import datetime
from aniso8601 import parse_date, parse_datetime, parse_time from aniso8601 import parse_date, parse_datetime, parse_time
from graphql import Undefined from graphql.error import GraphQLError
from graphql.language import StringValueNode from graphql.language import StringValueNode, print_ast
from .scalars import Scalar from .scalars import Scalar
@ -20,25 +20,30 @@ class Date(Scalar):
def serialize(date): def serialize(date):
if isinstance(date, datetime.datetime): if isinstance(date, datetime.datetime):
date = date.date() date = date.date()
assert isinstance( if not isinstance(date, datetime.date):
date, datetime.date raise GraphQLError("Date cannot represent value: {}".format(repr(date)))
), 'Received not compatible date "{}"'.format(repr(date))
return date.isoformat() return date.isoformat()
@classmethod @classmethod
def parse_literal(cls, node): def parse_literal(cls, node):
if isinstance(node, StringValueNode): if not isinstance(node, StringValueNode):
raise GraphQLError(
"Date cannot represent non-string value: {}".format(print_ast(node))
)
return cls.parse_value(node.value) return cls.parse_value(node.value)
@staticmethod @staticmethod
def parse_value(value): def parse_value(value):
try:
if isinstance(value, datetime.date): if isinstance(value, datetime.date):
return value return value
elif isinstance(value, str): if not isinstance(value, str):
raise GraphQLError(
"Date cannot represent non-string value: {}".format(repr(value))
)
try:
return parse_date(value) return parse_date(value)
except ValueError: except ValueError:
return Undefined raise GraphQLError("Date cannot represent value: {}".format(repr(value)))
class DateTime(Scalar): class DateTime(Scalar):
@ -50,25 +55,32 @@ class DateTime(Scalar):
@staticmethod @staticmethod
def serialize(dt): def serialize(dt):
assert isinstance( if not isinstance(dt, (datetime.datetime, datetime.date)):
dt, (datetime.datetime, datetime.date) raise GraphQLError("DateTime cannot represent value: {}".format(repr(dt)))
), 'Received not compatible datetime "{}"'.format(repr(dt))
return dt.isoformat() return dt.isoformat()
@classmethod @classmethod
def parse_literal(cls, node): def parse_literal(cls, node):
if isinstance(node, StringValueNode): if not isinstance(node, StringValueNode):
raise GraphQLError(
"DateTime cannot represent non-string value: {}".format(print_ast(node))
)
return cls.parse_value(node.value) return cls.parse_value(node.value)
@staticmethod @staticmethod
def parse_value(value): def parse_value(value):
try:
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
return value return value
elif isinstance(value, str): if not isinstance(value, str):
raise GraphQLError(
"DateTime cannot represent non-string value: {}".format(repr(value))
)
try:
return parse_datetime(value) return parse_datetime(value)
except ValueError: except ValueError:
return Undefined raise GraphQLError(
"DateTime cannot represent value: {}".format(repr(value))
)
class Time(Scalar): class Time(Scalar):
@ -80,22 +92,27 @@ class Time(Scalar):
@staticmethod @staticmethod
def serialize(time): def serialize(time):
assert isinstance( if not isinstance(time, datetime.time):
time, datetime.time raise GraphQLError("Time cannot represent value: {}".format(repr(time)))
), 'Received not compatible time "{}"'.format(repr(time))
return time.isoformat() return time.isoformat()
@classmethod @classmethod
def parse_literal(cls, node): def parse_literal(cls, node):
if isinstance(node, StringValueNode): if not isinstance(node, StringValueNode):
raise GraphQLError(
"Time cannot represent non-string value: {}".format(print_ast(node))
)
return cls.parse_value(node.value) return cls.parse_value(node.value)
@classmethod @classmethod
def parse_value(cls, value): def parse_value(cls, value):
try:
if isinstance(value, datetime.time): if isinstance(value, datetime.time):
return value return value
elif isinstance(value, str): if not isinstance(value, str):
raise GraphQLError(
"Time cannot represent non-string value: {}".format(repr(value))
)
try:
return parse_time(value) return parse_time(value)
except ValueError: except ValueError:
return Undefined raise GraphQLError("Time cannot represent value: {}".format(repr(value)))

View File

@ -1,4 +1,5 @@
from graphql import Undefined from graphql import Undefined
from .mountedtype import MountedType from .mountedtype import MountedType
from .structures import NonNull from .structures import NonNull
from .utils import get_type from .utils import get_type

View File

@ -7,7 +7,6 @@ from graphql import (
graphql, graphql,
graphql_sync, graphql_sync,
introspection_types, introspection_types,
is_type,
print_schema, print_schema,
GraphQLArgument, GraphQLArgument,
GraphQLBoolean, GraphQLBoolean,
@ -71,118 +70,74 @@ def is_graphene_type(type_):
return True return True
def resolve_type(resolve_type_func, map_, type_name, root, info, _type):
type_ = resolve_type_func(root, info)
if not type_:
return_type = map_[type_name]
return default_type_resolver(root, info, return_type)
if inspect.isclass(type_) and issubclass(type_, ObjectType):
graphql_type = map_.get(type_._meta.name)
assert graphql_type, "Can't find type {} in schema".format(type_._meta.name)
assert graphql_type.graphene_type == type_, (
"The type {} does not match with the associated graphene type {}."
).format(type_, graphql_type.graphene_type)
return graphql_type
return type_
def is_type_of_from_possible_types(possible_types, root, _info): def is_type_of_from_possible_types(possible_types, root, _info):
return isinstance(root, possible_types) return isinstance(root, possible_types)
class GrapheneGraphQLSchema(GraphQLSchema): class TypeMap(dict):
"""A GraphQLSchema that can deal with Graphene types as well."""
def __init__( def __init__(
self, self,
query=None, query=None,
mutation=None, mutation=None,
subscription=None, subscription=None,
types=None, types=None,
directives=None,
auto_camelcase=True, auto_camelcase=True,
): ):
assert_valid_root_type(query) assert_valid_root_type(query)
assert_valid_root_type(mutation) assert_valid_root_type(mutation)
assert_valid_root_type(subscription) assert_valid_root_type(subscription)
if types is None:
types = []
for type_ in types:
assert is_graphene_type(type_)
self.auto_camelcase = auto_camelcase self.auto_camelcase = auto_camelcase
super().__init__(query, mutation, subscription, types, directives)
if query: create_graphql_type = self.add_type
self.query_type = self.get_type(
query.name if isinstance(query, GraphQLObjectType) else query._meta.name
)
if mutation:
self.mutation_type = self.get_type(
mutation.name
if isinstance(mutation, GraphQLObjectType)
else mutation._meta.name
)
if subscription:
self.subscription_type = self.get_type(
subscription.name
if isinstance(subscription, GraphQLObjectType)
else subscription._meta.name
)
def get_graphql_type(self, _type): self.query = create_graphql_type(query) if query else None
if not _type: self.mutation = create_graphql_type(mutation) if mutation else None
return _type self.subscription = create_graphql_type(subscription) if subscription else None
if is_type(_type):
return _type self.types = [create_graphql_type(graphene_type) for graphene_type in types]
if is_graphene_type(_type):
graphql_type = self.get_type(_type._meta.name) def add_type(self, graphene_type):
assert graphql_type, "Type {} not found in this schema.".format( if inspect.isfunction(graphene_type):
_type._meta.name graphene_type = graphene_type()
if isinstance(graphene_type, List):
return GraphQLList(self.add_type(graphene_type.of_type))
if isinstance(graphene_type, NonNull):
return GraphQLNonNull(self.add_type(graphene_type.of_type))
try:
name = graphene_type._meta.name
except AttributeError:
raise TypeError(
"Expected Graphene type, but received: {}.".format(graphene_type)
) )
assert graphql_type.graphene_type == _type graphql_type = self.get(name)
if graphql_type:
return graphql_type return graphql_type
raise Exception("{} is not a valid GraphQL type.".format(_type)) if issubclass(graphene_type, ObjectType):
graphql_type = self.create_objecttype(graphene_type)
# noinspection PyMethodOverriding elif issubclass(graphene_type, InputObjectType):
def type_map_reducer(self, map_, type_): graphql_type = self.create_inputobjecttype(graphene_type)
if not type_: elif issubclass(graphene_type, Interface):
return map_ graphql_type = self.create_interface(graphene_type)
if inspect.isfunction(type_): elif issubclass(graphene_type, Scalar):
type_ = type_() graphql_type = self.create_scalar(graphene_type)
if is_graphene_type(type_): elif issubclass(graphene_type, Enum):
return self.graphene_reducer(map_, type_) graphql_type = self.create_enum(graphene_type)
return super().type_map_reducer(map_, type_) elif issubclass(graphene_type, Union):
graphql_type = self.construct_union(graphene_type)
def graphene_reducer(self, map_, type_):
if isinstance(type_, (List, NonNull)):
return self.type_map_reducer(map_, type_.of_type)
if type_._meta.name in map_:
_type = map_[type_._meta.name]
if isinstance(_type, GrapheneGraphQLType):
assert _type.graphene_type == type_, (
"Found different types with the same name in the schema: {}, {}."
).format(_type.graphene_type, type_)
return map_
if issubclass(type_, ObjectType):
internal_type = self.construct_objecttype(map_, type_)
elif issubclass(type_, InputObjectType):
internal_type = self.construct_inputobjecttype(map_, type_)
elif issubclass(type_, Interface):
internal_type = self.construct_interface(map_, type_)
elif issubclass(type_, Scalar):
internal_type = self.construct_scalar(type_)
elif issubclass(type_, Enum):
internal_type = self.construct_enum(type_)
elif issubclass(type_, Union):
internal_type = self.construct_union(map_, type_)
else: else:
raise Exception("Expected Graphene type, but received: {}.".format(type_)) raise TypeError(
"Expected Graphene type, but received: {}.".format(graphene_type)
return super().type_map_reducer(map_, internal_type) )
self[name] = graphql_type
return graphql_type
@staticmethod @staticmethod
def construct_scalar(type_): def create_scalar(graphene_type):
# We have a mapping to the original GraphQL types # We have a mapping to the original GraphQL types
# so there are no collisions. # so there are no collisions.
_scalars = { _scalars = {
@ -192,29 +147,31 @@ class GrapheneGraphQLSchema(GraphQLSchema):
Boolean: GraphQLBoolean, Boolean: GraphQLBoolean,
ID: GraphQLID, ID: GraphQLID,
} }
if type_ in _scalars: if graphene_type in _scalars:
return _scalars[type_] return _scalars[graphene_type]
return GrapheneScalarType( return GrapheneScalarType(
graphene_type=type_, graphene_type=graphene_type,
name=type_._meta.name, name=graphene_type._meta.name,
description=type_._meta.description, description=graphene_type._meta.description,
serialize=getattr(type_, "serialize", None), serialize=getattr(graphene_type, "serialize", None),
parse_value=getattr(type_, "parse_value", None), parse_value=getattr(graphene_type, "parse_value", None),
parse_literal=getattr(type_, "parse_literal", None), parse_literal=getattr(graphene_type, "parse_literal", None),
) )
@staticmethod @staticmethod
def construct_enum(type_): def create_enum(graphene_type):
values = {} values = {}
for name, value in type_._meta.enum.__members__.items(): for name, value in graphene_type._meta.enum.__members__.items():
description = getattr(value, "description", None) description = getattr(value, "description", None)
deprecation_reason = getattr(value, "deprecation_reason", None) deprecation_reason = getattr(value, "deprecation_reason", None)
if not description and callable(type_._meta.description): if not description and callable(graphene_type._meta.description):
description = type_._meta.description(value) description = graphene_type._meta.description(value)
if not deprecation_reason and callable(type_._meta.deprecation_reason): if not deprecation_reason and callable(
deprecation_reason = type_._meta.deprecation_reason(value) graphene_type._meta.deprecation_reason
):
deprecation_reason = graphene_type._meta.deprecation_reason(value)
values[name] = GraphQLEnumValue( values[name] = GraphQLEnumValue(
value=value.value, value=value.value,
@ -223,107 +180,98 @@ class GrapheneGraphQLSchema(GraphQLSchema):
) )
type_description = ( type_description = (
type_._meta.description(None) graphene_type._meta.description(None)
if callable(type_._meta.description) if callable(graphene_type._meta.description)
else type_._meta.description else graphene_type._meta.description
) )
return GrapheneEnumType( return GrapheneEnumType(
graphene_type=type_, graphene_type=graphene_type,
values=values, values=values,
name=type_._meta.name, name=graphene_type._meta.name,
description=type_description, description=type_description,
) )
def construct_objecttype(self, map_, type_): def create_objecttype(self, graphene_type):
if type_._meta.name in map_: create_graphql_type = self.add_type
_type = map_[type_._meta.name]
if isinstance(_type, GrapheneGraphQLType):
assert _type.graphene_type == type_, (
"Found different types with the same name in the schema: {}, {}."
).format(_type.graphene_type, type_)
return _type
def interfaces(): def interfaces():
interfaces = [] interfaces = []
for interface in type_._meta.interfaces: for graphene_interface in graphene_type._meta.interfaces:
self.graphene_reducer(map_, interface) interface = create_graphql_type(graphene_interface)
internal_type = map_[interface._meta.name] assert interface.graphene_type == graphene_interface
assert internal_type.graphene_type == interface interfaces.append(interface)
interfaces.append(internal_type)
return interfaces return interfaces
if type_._meta.possible_types: if graphene_type._meta.possible_types:
is_type_of = partial( is_type_of = partial(
is_type_of_from_possible_types, type_._meta.possible_types is_type_of_from_possible_types, graphene_type._meta.possible_types
) )
else: else:
is_type_of = type_.is_type_of is_type_of = graphene_type.is_type_of
return GrapheneObjectType( return GrapheneObjectType(
graphene_type=type_, graphene_type=graphene_type,
name=type_._meta.name, name=graphene_type._meta.name,
description=type_._meta.description, description=graphene_type._meta.description,
fields=partial(self.construct_fields_for_type, map_, type_), fields=partial(self.create_fields_for_type, graphene_type),
is_type_of=is_type_of, is_type_of=is_type_of,
interfaces=interfaces, interfaces=interfaces,
) )
def construct_interface(self, map_, type_): def create_interface(self, graphene_type):
if type_._meta.name in map_: resolve_type = (
_type = map_[type_._meta.name] partial(
if isinstance(_type, GrapheneInterfaceType): self.resolve_type, graphene_type.resolve_type, graphene_type._meta.name
assert _type.graphene_type == type_, (
"Found different types with the same name in the schema: {}, {}."
).format(_type.graphene_type, type_)
return _type
_resolve_type = None
if type_.resolve_type:
_resolve_type = partial(
resolve_type, type_.resolve_type, map_, type_._meta.name
) )
if graphene_type.resolve_type
else None
)
return GrapheneInterfaceType( return GrapheneInterfaceType(
graphene_type=type_, graphene_type=graphene_type,
name=type_._meta.name, name=graphene_type._meta.name,
description=type_._meta.description, description=graphene_type._meta.description,
fields=partial(self.construct_fields_for_type, map_, type_), fields=partial(self.create_fields_for_type, graphene_type),
resolve_type=_resolve_type, resolve_type=resolve_type,
) )
def construct_inputobjecttype(self, map_, type_): def create_inputobjecttype(self, graphene_type):
return GrapheneInputObjectType( return GrapheneInputObjectType(
graphene_type=type_, graphene_type=graphene_type,
name=type_._meta.name, name=graphene_type._meta.name,
description=type_._meta.description, description=graphene_type._meta.description,
out_type=type_._meta.container, out_type=graphene_type._meta.container,
fields=partial( fields=partial(
self.construct_fields_for_type, map_, type_, is_input_type=True self.create_fields_for_type, graphene_type, is_input_type=True
), ),
) )
def construct_union(self, map_, type_): def construct_union(self, graphene_type):
_resolve_type = None create_graphql_type = self.add_type
if type_.resolve_type:
_resolve_type = partial(
resolve_type, type_.resolve_type, map_, type_._meta.name
)
def types(): def types():
union_types = [] union_types = []
for objecttype in type_._meta.types: for graphene_objecttype in graphene_type._meta.types:
self.graphene_reducer(map_, objecttype) object_type = create_graphql_type(graphene_objecttype)
internal_type = map_[objecttype._meta.name] assert object_type.graphene_type == graphene_objecttype
assert internal_type.graphene_type == objecttype union_types.append(object_type)
union_types.append(internal_type)
return union_types return union_types
resolve_type = (
partial(
self.resolve_type, graphene_type.resolve_type, graphene_type._meta.name
)
if graphene_type.resolve_type
else None
)
return GrapheneUnionType( return GrapheneUnionType(
graphene_type=type_, graphene_type=graphene_type,
name=type_._meta.name, name=graphene_type._meta.name,
description=type_._meta.description, description=graphene_type._meta.description,
types=types, types=types,
resolve_type=_resolve_type, resolve_type=resolve_type,
) )
def get_name(self, name): def get_name(self, name):
@ -331,15 +279,16 @@ class GrapheneGraphQLSchema(GraphQLSchema):
return to_camel_case(name) return to_camel_case(name)
return name return name
def construct_fields_for_type(self, map_, type_, is_input_type=False): def create_fields_for_type(self, graphene_type, is_input_type=False):
create_graphql_type = self.add_type
fields = {} fields = {}
for name, field in type_._meta.fields.items(): for name, field in graphene_type._meta.fields.items():
if isinstance(field, Dynamic): if isinstance(field, Dynamic):
field = get_field_as(field.get_type(self), _as=Field) field = get_field_as(field.get_type(self), _as=Field)
if not field: if not field:
continue continue
map_ = self.type_map_reducer(map_, field.type) field_type = create_graphql_type(field.type)
field_type = self.get_field_type(map_, field.type)
if is_input_type: if is_input_type:
_field = GraphQLInputField( _field = GraphQLInputField(
field_type, field_type,
@ -350,8 +299,7 @@ class GrapheneGraphQLSchema(GraphQLSchema):
else: else:
args = {} args = {}
for arg_name, arg in field.args.items(): for arg_name, arg in field.args.items():
map_ = self.type_map_reducer(map_, arg.type) arg_type = create_graphql_type(arg.type)
arg_type = self.get_field_type(map_, arg.type)
processed_arg_name = arg.name or self.get_name(arg_name) processed_arg_name = arg.name or self.get_name(arg_name)
args[processed_arg_name] = GraphQLArgument( args[processed_arg_name] = GraphQLArgument(
arg_type, arg_type,
@ -361,12 +309,13 @@ class GrapheneGraphQLSchema(GraphQLSchema):
if isinstance(arg.type, NonNull) if isinstance(arg.type, NonNull)
else arg.default_value, else arg.default_value,
) )
resolve = field.get_resolver(
self.get_resolver(graphene_type, name, field.default_value)
)
_field = GraphQLField( _field = GraphQLField(
field_type, field_type,
args=args, args=args,
resolve=field.get_resolver( resolve=resolve,
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,
) )
@ -374,15 +323,32 @@ class GrapheneGraphQLSchema(GraphQLSchema):
fields[field_name] = _field fields[field_name] = _field
return fields return fields
def get_resolver_for_type(self, type_, name, default_value): def resolve_type(self, resolve_type_func, type_name, root, info, _type):
if not issubclass(type_, ObjectType): type_ = resolve_type_func(root, info)
if not type_:
return_type = self[type_name]
return default_type_resolver(root, info, return_type)
if inspect.isclass(type_) and issubclass(type_, ObjectType):
graphql_type = self.get(type_._meta.name)
assert graphql_type, "Can't find type {} in schema".format(type_._meta.name)
assert graphql_type.graphene_type == type_, (
"The type {} does not match with the associated graphene type {}."
).format(type_, graphql_type.graphene_type)
return graphql_type
return type_
def get_resolver(self, graphene_type, name, default_value):
if not issubclass(graphene_type, ObjectType):
return return
resolver = getattr(type_, "resolve_{}".format(name), None) resolver = getattr(graphene_type, "resolve_{}".format(name), None)
if not resolver: if not resolver:
# If we don't find the resolver in the ObjectType class, then try to # If we don't find the resolver in the ObjectType class, then try to
# find it in each of the interfaces # find it in each of the interfaces
interface_resolver = None interface_resolver = None
for interface in type_._meta.interfaces: for interface in graphene_type._meta.interfaces:
if name not in interface._meta.fields: if name not in interface._meta.fields:
continue continue
interface_resolver = getattr(interface, "resolve_{}".format(name), None) interface_resolver = getattr(interface, "resolve_{}".format(name), None)
@ -394,16 +360,11 @@ class GrapheneGraphQLSchema(GraphQLSchema):
if resolver: if resolver:
return get_unbound_function(resolver) return get_unbound_function(resolver)
default_resolver = type_._meta.default_resolver or get_default_resolver() default_resolver = (
graphene_type._meta.default_resolver or get_default_resolver()
)
return partial(default_resolver, name, default_value) return partial(default_resolver, name, default_value)
def get_field_type(self, map_, type_):
if isinstance(type_, List):
return GraphQLList(self.get_field_type(map_, type_.of_type))
if isinstance(type_, NonNull):
return GraphQLNonNull(self.get_field_type(map_, type_.of_type))
return map_.get(type_._meta.name)
class Schema: class Schema:
"""Schema Definition. """Schema Definition.
@ -419,11 +380,11 @@ class Schema:
fields to *create, update or delete* data in your API. fields to *create, update or delete* data in your API.
subscription (ObjectType, optional): Root subscription *ObjectType*. Describes entry point subscription (ObjectType, optional): Root subscription *ObjectType*. Describes entry point
for fields to receive continuous updates. for fields to receive continuous updates.
types (List[ObjectType], optional): List of any types to include in schema that
may not be introspected through root types.
directives (List[GraphQLDirective], optional): List of custom directives to include in the directives (List[GraphQLDirective], optional): List of custom directives to include in the
GraphQL schema. Defaults to only include directives defined by GraphQL spec (@include GraphQL schema. Defaults to only include directives defined by GraphQL spec (@include
and @skip) [GraphQLIncludeDirective, GraphQLSkipDirective]. and @skip) [GraphQLIncludeDirective, GraphQLSkipDirective].
types (List[GraphQLType], optional): List of any types to include in schema that
may not be introspected through root types.
auto_camelcase (bool): Fieldnames will be transformed in Schema's TypeMap from snake_case auto_camelcase (bool): Fieldnames will be transformed in Schema's TypeMap from snake_case
to camelCase (preferred by GraphQL standard). Default True. to camelCase (preferred by GraphQL standard). Default True.
""" """
@ -440,13 +401,15 @@ class Schema:
self.query = query self.query = query
self.mutation = mutation self.mutation = mutation
self.subscription = subscription self.subscription = subscription
self.graphql_schema = GrapheneGraphQLSchema( type_map = TypeMap(
query, query, mutation, subscription, types, auto_camelcase=auto_camelcase
mutation, )
subscription, self.graphql_schema = GraphQLSchema(
types, type_map.query,
type_map.mutation,
type_map.subscription,
type_map.types,
directives, directives,
auto_camelcase=auto_camelcase,
) )
def __str__(self): def __str__(self):

View File

@ -3,7 +3,7 @@ import datetime
import pytz import pytz
from graphql import GraphQLError from graphql import GraphQLError
from pytest import fixture, mark from pytest import fixture
from ..datetime import Date, DateTime, Time from ..datetime import Date, DateTime, Time
from ..objecttype import ObjectType from ..objecttype import ObjectType
@ -84,8 +84,9 @@ def test_bad_datetime_query():
assert result.errors and len(result.errors) == 1 assert result.errors and len(result.errors) == 1
error = result.errors[0] error = result.errors[0]
assert isinstance(error, GraphQLError) assert isinstance(error, GraphQLError)
assert error.message == ( assert (
'Expected type DateTime, found "Some string that\'s not a datetime".' error.message == "DateTime cannot represent value:"
' "Some string that\'s not a datetime"'
) )
assert result.data is None assert result.data is None
@ -97,8 +98,9 @@ def test_bad_date_query():
error = result.errors[0] error = result.errors[0]
assert isinstance(error, GraphQLError) assert isinstance(error, GraphQLError)
assert error.message == ( assert (
'Expected type Date, found "Some string that\'s not a date".' error.message == "Date cannot represent value:"
' "Some string that\'s not a date"'
) )
assert result.data is None assert result.data is None
@ -110,8 +112,9 @@ def test_bad_time_query():
error = result.errors[0] error = result.errors[0]
assert isinstance(error, GraphQLError) assert isinstance(error, GraphQLError)
assert error.message == ( assert (
'Expected type Time, found "Some string that\'s not a time".' error.message == "Time cannot represent value:"
' "Some string that\'s not a time"'
) )
assert result.data is None assert result.data is None
@ -174,9 +177,6 @@ def test_time_query_variable(sample_time):
assert result.data == {"time": isoformat} assert result.data == {"time": isoformat}
@mark.xfail(
reason="creating the error message fails when un-parsable object is not JSON serializable."
)
def test_bad_variables(sample_date, sample_datetime, sample_time): def test_bad_variables(sample_date, sample_datetime, sample_time):
def _test_bad_variables(type_, input_): def _test_bad_variables(type_, input_):
result = schema.execute( result = schema.execute(
@ -185,8 +185,6 @@ def test_bad_variables(sample_date, sample_datetime, sample_time):
), ),
variables={"input": input_}, variables={"input": input_},
) )
# when `input` is not JSON serializable formatting the error message in
# `graphql.utils.is_valid_value` line 79 fails with a TypeError
assert isinstance(result.errors, list) assert isinstance(result.errors, list)
assert len(result.errors) == 1 assert len(result.errors) == 1
assert isinstance(result.errors[0], GraphQLError) assert isinstance(result.errors[0], GraphQLError)
@ -205,7 +203,6 @@ def test_bad_variables(sample_date, sample_datetime, sample_time):
("DateTime", time), ("DateTime", time),
("Date", not_a_date), ("Date", not_a_date),
("Date", not_a_date_str), ("Date", not_a_date_str),
("Date", now),
("Date", time), ("Date", time),
("Time", not_a_date), ("Time", not_a_date),
("Time", not_a_date_str), ("Time", not_a_date_str),

View File

@ -1,5 +1,6 @@
from pytest import raises from pytest import raises
from graphql.type import GraphQLObjectType, GraphQLSchema
from graphql.pyutils import dedent from graphql.pyutils import dedent
from ..field import Field from ..field import Field
@ -17,8 +18,13 @@ class Query(ObjectType):
def test_schema(): def test_schema():
schema = Schema(Query).graphql_schema schema = Schema(Query)
assert schema.query_type == schema.get_graphql_type(Query) graphql_schema = schema.graphql_schema
assert isinstance(graphql_schema, GraphQLSchema)
query_type = graphql_schema.query_type
assert isinstance(query_type, GraphQLObjectType)
assert query_type.name == "Query"
assert query_type.graphene_type is Query
def test_schema_get_type(): def test_schema_get_type():
@ -39,13 +45,13 @@ def test_schema_str():
schema = Schema(Query) schema = Schema(Query)
assert str(schema) == dedent( assert str(schema) == dedent(
""" """
type MyOtherType {
field: String
}
type Query { type Query {
inner: MyOtherType inner: MyOtherType
} }
type MyOtherType {
field: String
}
""" """
) )

View File

@ -1,5 +1,3 @@
from pytest import raises
from graphql.type import ( from graphql.type import (
GraphQLArgument, GraphQLArgument,
GraphQLEnumType, GraphQLEnumType,
@ -21,13 +19,13 @@ from ..interface import Interface
from ..objecttype import ObjectType from ..objecttype import ObjectType
from ..scalars import Int, String from ..scalars import Int, String
from ..structures import List, NonNull from ..structures import List, NonNull
from ..schema import GrapheneGraphQLSchema, resolve_type from ..schema import Schema
def create_type_map(types, auto_camelcase=True): def create_type_map(types, auto_camelcase=True):
query = GraphQLObjectType("Query", {}) query = type("Query", (ObjectType,), {})
schema = GrapheneGraphQLSchema(query, types=types, auto_camelcase=auto_camelcase) schema = Schema(query, types=types, auto_camelcase=auto_camelcase)
return schema.type_map return schema.graphql_schema.type_map
def test_enum(): def test_enum():
@ -272,20 +270,3 @@ def test_objecttype_with_possible_types():
assert graphql_type.is_type_of assert graphql_type.is_type_of
assert graphql_type.is_type_of({}, None) is True assert graphql_type.is_type_of({}, None) is True
assert graphql_type.is_type_of(MyObjectType(), None) is False assert graphql_type.is_type_of(MyObjectType(), None) is False
def test_resolve_type_with_missing_type():
class MyObjectType(ObjectType):
foo_bar = String()
class MyOtherObjectType(ObjectType):
fizz_buzz = String()
def resolve_type_func(root, info):
return MyOtherObjectType
type_map = create_type_map([MyObjectType])
with raises(AssertionError) as excinfo:
resolve_type(resolve_type_func, type_map, "MyOtherObjectType", {}, {}, None)
assert "MyOtherObjectTyp" in str(excinfo.value)

View File

@ -82,7 +82,7 @@ setup(
keywords="api graphql protocol rest relay graphene", keywords="api graphql protocol rest relay graphene",
packages=find_packages(exclude=["tests", "tests.*", "examples"]), packages=find_packages(exclude=["tests", "tests.*", "examples"]),
install_requires=[ install_requires=[
"graphql-core>=3.0.3,<3.1", "graphql-core>=3.1.0b1,<4",
"graphql-relay>=3.0,<4", "graphql-relay>=3.0,<4",
"aniso8601>=8,<9", "aniso8601>=8,<9",
"unidecode>=1.1.1,<2", "unidecode>=1.1.1,<2",