Skip to content

Commit afe8614

Browse files
committed
First types implementation
1 parent 2f0bd7c commit afe8614

File tree

15 files changed

+492
-1
lines changed

15 files changed

+492
-1
lines changed

graphene/core/ntypes/__init__.py

Whitespace-only changes.

graphene/core/ntypes/argument.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from itertools import chain
2+
3+
from graphql.core.type import GraphQLArgument
4+
5+
from .base import OrderedType, ArgumentType
6+
from ...utils import to_camel_case
7+
8+
9+
class Argument(OrderedType):
10+
def __init__(self, type, description=None, default=None, name=None, _creation_counter=None):
11+
super(Argument, self).__init__(_creation_counter=_creation_counter)
12+
self.name = name
13+
self.type = type
14+
self.description = description
15+
self.default = default
16+
17+
def internal_type(self, schema):
18+
return GraphQLArgument(schema.T(self.type), self.default, self.description)
19+
20+
def __repr__(self):
21+
return self.name
22+
23+
24+
def to_arguments(*args, **kwargs):
25+
arguments = {}
26+
iter_arguments = chain(kwargs.items(), [(None, a) for a in args])
27+
28+
for name, arg in iter_arguments:
29+
if isinstance(arg, Argument):
30+
argument = arg
31+
elif isinstance(arg, ArgumentType):
32+
argument = arg.as_argument()
33+
else:
34+
raise ValueError('Unknown argument value type %r' % arg)
35+
36+
if name:
37+
argument.name = to_camel_case(name)
38+
assert argument.name, 'Argument in field must have a name'
39+
assert argument.name not in arguments, 'Found more than one Argument with same name {}'.format(argument.name)
40+
arguments[argument.name] = argument
41+
42+
return sorted(arguments.values())

graphene/core/ntypes/base.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from functools import total_ordering
2+
from ..types import BaseObjectType, InputObjectType
3+
4+
5+
@total_ordering
6+
class OrderedType(object):
7+
creation_counter = 0
8+
9+
def __init__(self, _creation_counter=None):
10+
self.creation_counter = _creation_counter or self.gen_counter()
11+
12+
@staticmethod
13+
def gen_counter():
14+
counter = OrderedType.creation_counter
15+
OrderedType.creation_counter += 1
16+
return counter
17+
18+
def __eq__(self, other):
19+
# Needed for @total_ordering
20+
if type(self) == type(other):
21+
return self.creation_counter == other.creation_counter
22+
return NotImplemented
23+
24+
def __lt__(self, other):
25+
# This is needed because bisect does not take a comparison function.
26+
if type(self) == type(other):
27+
return self.creation_counter < other.creation_counter
28+
return NotImplemented
29+
30+
def __hash__(self):
31+
return hash((self.creation_counter))
32+
33+
34+
class MirroredType(OrderedType):
35+
def __init__(self, *args, **kwargs):
36+
_creation_counter = kwargs.pop('_creation_counter', None)
37+
super(MirroredType, self).__init__(_creation_counter=_creation_counter)
38+
self.args = args
39+
self.kwargs = kwargs
40+
41+
@classmethod
42+
def internal_type(cls, schema):
43+
return getattr(cls, 'T', None)
44+
45+
46+
class ArgumentType(MirroredType):
47+
def as_argument(self):
48+
from .argument import Argument
49+
return Argument(self.__class__, _creation_counter=self.creation_counter, *self.args, **self.kwargs)
50+
51+
52+
class FieldType(MirroredType):
53+
def contribute_to_class(self, cls, name):
54+
if issubclass(cls, InputObjectType):
55+
inputfield = self.as_inputfield()
56+
return inputfield.contribute_to_class(cls, name)
57+
elif issubclass(cls, BaseObjectType):
58+
field = self.as_field()
59+
return field.contribute_to_class(cls, name)
60+
61+
def as_field(self):
62+
from .field import Field
63+
return Field(self.__class__, _creation_counter=self.creation_counter, *self.args, **self.kwargs)
64+
65+
def as_inputfield(self):
66+
from .field import InputField
67+
return InputField(self.__class__, _creation_counter=self.creation_counter, *self.args, **self.kwargs)
68+
69+
70+
class MountedType(FieldType, ArgumentType):
71+
pass

graphene/core/ntypes/definitions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from graphql.core.type import (GraphQLList, GraphQLNonNull)
2+
3+
from .base import MountedType
4+
5+
6+
class OfType(MountedType):
7+
def __init__(self, of_type, *args, **kwargs):
8+
self.of_type = of_type
9+
super(OfType, self).__init__(*args, **kwargs)
10+
11+
def internal_type(self, schema):
12+
return self.T(schema.T(self.of_type))
13+
14+
15+
class List(OfType):
16+
T = GraphQLList
17+
18+
19+
class NonNull(OfType):
20+
T = GraphQLNonNull

graphene/core/ntypes/field.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from collections import OrderedDict
2+
3+
from graphql.core.type import GraphQLField, GraphQLInputObjectField
4+
5+
from .base import OrderedType
6+
from .argument import to_arguments
7+
from ...utils import to_camel_case
8+
from ..types import BaseObjectType, InputObjectType
9+
10+
11+
class Field(OrderedType):
12+
def __init__(self, type, description=None, args=None, name=None, resolver=None, *args_list, **kwargs):
13+
_creation_counter = kwargs.pop('_creation_counter', None)
14+
super(Field, self).__init__(_creation_counter=_creation_counter)
15+
self.name = name
16+
self.type = type
17+
self.description = description
18+
args = OrderedDict(args or {}, **kwargs)
19+
self.arguments = to_arguments(*args_list, **args)
20+
self.resolver = resolver
21+
22+
def contribute_to_class(self, cls, attname):
23+
assert issubclass(cls, BaseObjectType), 'Field {} cannot be mounted in {}'.format(self, cls)
24+
if not self.name:
25+
self.name = to_camel_case(attname)
26+
self.attname = attname
27+
self.object_type = cls
28+
if self.type == 'self':
29+
self.type = cls
30+
cls._meta.add_field(self)
31+
32+
def internal_type(self, schema):
33+
return GraphQLField(schema.T(self.type), args=self.get_arguments(schema), resolver=self.resolver,
34+
description=self.description,)
35+
36+
def get_arguments(self, schema):
37+
if not self.arguments:
38+
return None
39+
40+
return OrderedDict([(arg.name, schema.T(arg)) for arg in self.arguments])
41+
42+
43+
class InputField(OrderedType):
44+
def __init__(self, type, description=None, default=None, name=None, _creation_counter=None):
45+
super(InputField, self).__init__(_creation_counter=_creation_counter)
46+
self.name = name
47+
self.type = type
48+
self.description = description
49+
self.default = default
50+
51+
def contribute_to_class(self, cls, attname):
52+
assert issubclass(cls, InputObjectType), 'InputField {} cannot be mounted in {}'.format(self, cls)
53+
if not self.name:
54+
self.name = to_camel_case(attname)
55+
self.attname = attname
56+
self.object_type = cls
57+
cls._meta.add_field(self)
58+
59+
def internal_type(self, schema):
60+
return GraphQLInputObjectField(schema.T(self.type), default_value=self.default,
61+
description=self.description)

graphene/core/ntypes/scalars.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from graphql.core.type import (GraphQLBoolean, GraphQLFloat, GraphQLID,
2+
GraphQLInt, GraphQLScalarType, GraphQLString)
3+
4+
from .base import MountedType
5+
6+
7+
class String(MountedType):
8+
T = GraphQLString
9+
10+
11+
class Int(MountedType):
12+
T = GraphQLInt
13+
14+
15+
class Boolean(MountedType):
16+
T = GraphQLBoolean
17+
18+
19+
class ID(MountedType):
20+
T = GraphQLID
21+
22+
23+
class Float(MountedType):
24+
T = GraphQLFloat
25+
26+
27+
class Scalar(MountedType):
28+
@classmethod
29+
def internal_type(cls, schema):
30+
serialize = getattr(cls, 'serialize')
31+
parse_literal = getattr(cls, 'parse_literal')
32+
parse_value = getattr(cls, 'parse_value')
33+
34+
return GraphQLScalarType(
35+
name=cls.__name__,
36+
description=cls.__doc__,
37+
serialize=serialize,
38+
parse_value=parse_value,
39+
parse_literal=parse_literal
40+
)

graphene/core/ntypes/tests/__init__.py

Whitespace-only changes.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from pytest import raises
2+
from graphql.core.type import GraphQLArgument
3+
4+
from ..argument import Argument, to_arguments
5+
from ..scalars import String
6+
from graphene.core.types import ObjectType
7+
from graphene.core.schema import Schema
8+
9+
10+
def test_argument_internal_type():
11+
class MyObjectType(ObjectType):
12+
pass
13+
schema = Schema(query=MyObjectType)
14+
a = Argument(MyObjectType, description='My argument', default='3')
15+
type = schema.T(a)
16+
assert isinstance(type, GraphQLArgument)
17+
assert type.description == 'My argument'
18+
assert type.default_value == '3'
19+
20+
21+
def test_to_arguments():
22+
arguments = to_arguments(
23+
Argument(String, name='myArg'),
24+
String(name='otherArg'),
25+
my_kwarg=String(),
26+
other_kwarg=String(),
27+
)
28+
29+
assert [a.name for a in arguments] == ['myArg', 'otherArg', 'myKwarg', 'otherKwarg']
30+
31+
32+
def test_to_arguments_no_name():
33+
with raises(AssertionError) as excinfo:
34+
to_arguments(
35+
String(),
36+
)
37+
assert 'must have a name' in str(excinfo.value)
38+
39+
40+
def test_to_arguments_wrong_type():
41+
with raises(ValueError) as excinfo:
42+
to_arguments(
43+
p=3
44+
)
45+
assert 'Unknown argument value type 3' == str(excinfo.value)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from mock import patch
2+
3+
from ..base import OrderedType, MountedType
4+
from ..field import Field, InputField
5+
from ..argument import Argument
6+
from graphene.core.types import ObjectType, InputObjectType
7+
8+
9+
def test_orderedtype_equal():
10+
a = OrderedType()
11+
assert a == a
12+
assert hash(a) == hash(a)
13+
14+
15+
def test_orderedtype_different():
16+
a = OrderedType()
17+
b = OrderedType()
18+
assert a != b
19+
assert hash(a) != hash(b)
20+
assert a < b
21+
assert b > a
22+
23+
24+
@patch('graphene.core.ntypes.field.Field')
25+
def test_type_as_field_called(Field):
26+
resolver = lambda x: x
27+
a = MountedType(2, description='A', resolver=resolver)
28+
a.as_field()
29+
Field.assert_called_with(MountedType, 2, _creation_counter=a.creation_counter, description='A', resolver=resolver)
30+
31+
32+
@patch('graphene.core.ntypes.argument.Argument')
33+
def test_type_as_argument_called(Argument):
34+
a = MountedType(2, description='A')
35+
a.as_argument()
36+
Argument.assert_called_with(MountedType, 2, _creation_counter=a.creation_counter, description='A')
37+
38+
39+
def test_type_as_field():
40+
resolver = lambda x: x
41+
42+
class MyObjectType(ObjectType):
43+
t = MountedType(description='A', resolver=resolver)
44+
45+
fields_map = MyObjectType._meta.fields_map
46+
field = fields_map.get('t')
47+
assert isinstance(field, Field)
48+
assert field.description == 'A'
49+
assert field.object_type == MyObjectType
50+
51+
52+
def test_type_as_inputfield():
53+
class MyObjectType(InputObjectType):
54+
t = MountedType(description='A')
55+
56+
fields_map = MyObjectType._meta.fields_map
57+
field = fields_map.get('t')
58+
assert isinstance(field, InputField)
59+
assert field.description == 'A'
60+
assert field.object_type == MyObjectType
61+
62+
63+
def test_type_as_argument():
64+
a = MountedType(description='A')
65+
argument = a.as_argument()
66+
assert isinstance(argument, Argument)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from graphql.core.type import (GraphQLList, GraphQLString, GraphQLNonNull)
2+
3+
from ..definitions import List, NonNull
4+
from ..scalars import String
5+
from graphene.core.schema import Schema
6+
7+
schema = Schema()
8+
9+
10+
def test_list_scalar():
11+
type = schema.T(List(String()))
12+
assert isinstance(type, GraphQLList)
13+
assert type.of_type == GraphQLString
14+
15+
16+
def test_nonnull_scalar():
17+
type = schema.T(NonNull(String()))
18+
assert isinstance(type, GraphQLNonNull)
19+
assert type.of_type == GraphQLString
20+
21+
22+
def test_mixed_scalar():
23+
type = schema.T(NonNull(List(String())))
24+
assert isinstance(type, GraphQLNonNull)
25+
assert isinstance(type.of_type, GraphQLList)
26+
assert type.of_type.of_type == GraphQLString

0 commit comments

Comments
 (0)