Skip to content

Commit 17a8dcd

Browse files
committed
Implement scalar type class definition.
1 parent bc2393f commit 17a8dcd

File tree

5 files changed

+178
-2
lines changed

5 files changed

+178
-2
lines changed

epoxy/bases/scalar.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class ScalarBase(object):
2+
pass

epoxy/metaclasses/scalar.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from graphql.core.type import GraphQLScalarType
2+
3+
4+
class ScalarMeta(type):
5+
def __new__(mcs, name, bases, attrs):
6+
if attrs.pop('abstract', False):
7+
return super(ScalarMeta, mcs).__new__(mcs, name, bases, attrs)
8+
9+
registry = mcs._get_registry()
10+
11+
cls = super(ScalarMeta, mcs).__new__(mcs, name, bases, attrs)
12+
cls._registry = registry
13+
instance = cls()
14+
serialize = getattr(instance, 'serialize')
15+
parse_literal = getattr(instance, 'parse_literal')
16+
parse_value = getattr(instance, 'parse_value')
17+
18+
mcs._register(GraphQLScalarType(
19+
name=name,
20+
description=attrs.get('__doc__', None),
21+
serialize=serialize,
22+
parse_value=parse_value,
23+
parse_literal=parse_literal
24+
))
25+
26+
@staticmethod
27+
def _register(mutation):
28+
raise NotImplementedError('_register must be implemented in the sub-metaclass')
29+
30+
@staticmethod
31+
def _get_registry():
32+
raise NotImplementedError('_get_registry must be implemented in the sub-metaclass')

epoxy/registry.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
from .bases.input_type import InputTypeBase
2323
from .bases.mutation import MutationBase
2424
from .bases.object_type import ObjectTypeBase
25+
from .bases.scalar import ScalarBase
2526
from .field import Field, InputField
2627
from .metaclasses.input_type import InputTypeMeta
2728
from .metaclasses.interface import InterfaceMeta
2829
from .metaclasses.mutation import MutationMeta
2930
from .metaclasses.object_type import ObjectTypeMeta
31+
from .metaclasses.scalar import ScalarMeta
3032
from .metaclasses.union import UnionMeta
3133
from .thunk import AttributeTypeThunk, RootTypeThunk, ThunkList, TransformThunkList
3234
from .utils.enum_to_graphql_enum import enum_to_graphql_enum
@@ -45,7 +47,7 @@
4547
class TypeRegistry(object):
4648
_reserved_names = frozenset([
4749
# Types
48-
'ObjectType', 'InputType', 'Union' 'Interface', 'Implements',
50+
'ObjectType', 'InputType', 'Union' 'Interface', 'Implements', 'Scalar'
4951
# Functions
5052
'Schema', 'Register', 'Mixin',
5153
# Mutations
@@ -71,6 +73,7 @@ def __init__(self):
7173
self.Union = ClassTypeCreator(self, self._create_union_type_class)
7274
self.Interface = self._create_interface_type_class()
7375
self.Mutation = self._create_mutation_type_class()
76+
self.Scalar = self._create_scalar_type_class()
7477

7578
for type in builtin_scalars:
7679
self.Register(type)
@@ -214,6 +217,24 @@ class InputType(InputTypeBase):
214217

215218
return InputType
216219

220+
def _create_scalar_type_class(self):
221+
registry = self
222+
223+
class RegistryScalarMeta(ScalarMeta):
224+
@staticmethod
225+
def _register(scalar):
226+
registry.Register(scalar)
227+
228+
@staticmethod
229+
def _get_registry():
230+
return registry
231+
232+
@six.add_metaclass(RegistryScalarMeta)
233+
class Scalar(ScalarBase):
234+
abstract = True
235+
236+
return Scalar
237+
217238
def _create_mutation_type_class(self):
218239
registry = self
219240

tests/test_scalar.py

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import datetime
2+
from graphql.core import graphql
3+
from graphql.core.language import ast
4+
from graphql.core.type import GraphQLScalarType
5+
from epoxy.registry import TypeRegistry
6+
7+
8+
def test_custom_scalar_type():
9+
R = TypeRegistry()
10+
11+
def serialize_date_time(dt):
12+
assert isinstance(dt, datetime.datetime)
13+
return dt.isoformat()
14+
15+
def parse_literal(node):
16+
if isinstance(node, ast.StringValue):
17+
return datetime.datetime.strptime(node.value, "%Y-%m-%dT%H:%M:%S.%f")
18+
19+
def parse_value(value):
20+
return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
21+
22+
DateTimeType = GraphQLScalarType(name='DateTime', serialize=serialize_date_time,
23+
parse_literal=parse_literal,
24+
parse_value=parse_value)
25+
R(DateTimeType)
26+
27+
class Query(R.ObjectType):
28+
datetime = R.DateTime(args={
29+
'in': R.DateTime
30+
})
31+
32+
def resolve_datetime(self, obj, args, info):
33+
return args.get('in')
34+
35+
now = datetime.datetime.now()
36+
isoformat = now.isoformat()
37+
38+
Schema = R.Schema(R.Query)
39+
40+
response = graphql(Schema, '''
41+
{
42+
datetime(in: "%s")
43+
}
44+
45+
''' % isoformat)
46+
47+
assert not response.errors
48+
assert response.data == {
49+
'datetime': isoformat
50+
}
51+
52+
response = graphql(Schema, '''
53+
query Test($date: DateTime) {
54+
datetime(in: $date)
55+
}
56+
57+
''', args={
58+
'date': isoformat
59+
})
60+
61+
assert not response.errors
62+
assert response.data == {
63+
'datetime': isoformat
64+
}
65+
66+
67+
def test_register_scalar_type():
68+
R = TypeRegistry()
69+
70+
class DateTime(R.Scalar):
71+
@staticmethod
72+
def serialize(dt):
73+
assert isinstance(dt, datetime.datetime)
74+
return dt.isoformat()
75+
76+
@staticmethod
77+
def parse_literal(node):
78+
if isinstance(node, ast.StringValue):
79+
return datetime.datetime.strptime(node.value, "%Y-%m-%dT%H:%M:%S.%f")
80+
81+
@staticmethod
82+
def parse_value(value):
83+
return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
84+
85+
class Query(R.ObjectType):
86+
datetime = R.DateTime(args={
87+
'in': R.DateTime
88+
})
89+
90+
def resolve_datetime(self, obj, args, info):
91+
return args.get('in')
92+
93+
now = datetime.datetime.now()
94+
isoformat = now.isoformat()
95+
96+
Schema = R.Schema(R.Query)
97+
98+
response = graphql(Schema, '''
99+
{
100+
datetime(in: "%s")
101+
}
102+
103+
''' % isoformat)
104+
105+
assert not response.errors
106+
assert response.data == {
107+
'datetime': isoformat
108+
}
109+
110+
response = graphql(Schema, '''
111+
query Test($date: DateTime) {
112+
datetime(in: $date)
113+
}
114+
115+
''', args={
116+
'date': isoformat
117+
})
118+
119+
assert not response.errors
120+
assert response.data == {
121+
'datetime': isoformat
122+
}

tests/test_starwars/test_query.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from .schema import StarWarsSchema
22
from graphql.core import graphql
3-
from graphql.core.error import format_error
43

54

65
def test_hero_name_query():

0 commit comments

Comments
 (0)