Skip to content

Commit feeba6e

Browse files
committed
Improve test coverage: test defaultordered dict, and additional cases in async/gevent executor.
1 parent 2d6ae99 commit feeba6e

File tree

5 files changed

+108
-26
lines changed

5 files changed

+108
-26
lines changed
Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
from collections import Callable, OrderedDict
1+
from collections import OrderedDict
2+
import copy
23

34

45
class DefaultOrderedDict(OrderedDict):
56
__slots__ = 'default_factory',
67

78
# Source: http://stackoverflow.com/a/6190500/562769
89
def __init__(self, default_factory=None, *a, **kw):
9-
if (default_factory is not None and
10-
not isinstance(default_factory, Callable)):
10+
if default_factory is not None and not callable(default_factory):
1111
raise TypeError('first argument must be callable')
12+
1213
OrderedDict.__init__(self, *a, **kw)
1314
self.default_factory = default_factory
1415

15-
def __getitem__(self, key):
16-
try:
17-
return OrderedDict.__getitem__(self, key)
18-
except KeyError:
19-
return self.__missing__(key)
20-
2116
def __missing__(self, key):
2217
if self.default_factory is None:
2318
raise KeyError(key)
@@ -29,7 +24,7 @@ def __reduce__(self):
2924
args = tuple()
3025
else:
3126
args = self.default_factory,
32-
return type(self), args, None, None, self.items()
27+
return type(self), args, None, None, iter(self.items())
3328

3429
def copy(self):
3530
return self.__copy__()
@@ -38,13 +33,8 @@ def __copy__(self):
3833
return type(self)(self.default_factory, self)
3934

4035
def __deepcopy__(self, memo):
41-
import copy
42-
return type(self)(self.default_factory,
43-
copy.deepcopy(self.items()))
44-
45-
def __repr__(self, _repr_running=None):
46-
if _repr_running is None:
47-
_repr_running = {}
36+
return self.__class__(self.default_factory, copy.deepcopy(list(self.items())))
4837

49-
return 'OrderedDefaultDict(%s, %s)' % (self.default_factory,
50-
OrderedDict.__repr__(self, _repr_running))
38+
def __repr__(self):
39+
return 'DefaultOrderedDict(%s, %s)' % (self.default_factory,
40+
OrderedDict.__repr__(self)[19:-1])

tests/core_execution/test_gevent.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414

1515

1616
def test_gevent_executor():
17-
doc = 'query Example { a, b }'
18-
1917
@run_in_greenlet
2018
def resolver(context, *_):
2119
gevent.sleep(0.001)
@@ -26,15 +24,20 @@ def resolver_2(context, *_):
2624
gevent.sleep(0.003)
2725
return 'hey2'
2826

27+
def resolver_3(contest, *_):
28+
return 'hey3'
29+
2930
Type = GraphQLObjectType('Type', {
3031
'a': GraphQLField(GraphQLString, resolver=resolver),
31-
'b': GraphQLField(GraphQLString, resolver=resolver_2)
32+
'b': GraphQLField(GraphQLString, resolver=resolver_2),
33+
'c': GraphQLField(GraphQLString, resolver=resolver_3)
3234
})
3335

36+
doc = '{ a b c }'
3437
executor = Executor([GeventExecutionMiddleware()])
3538
result = executor.execute(GraphQLSchema(Type), doc)
3639
assert not result.errors
37-
assert result.data == {'a': 'hey', 'b': 'hey2'}
40+
assert result.data == {'a': 'hey', 'b': 'hey2', 'c': 'hey3'}
3841

3942

4043
def test_gevent_executor_with_error():

tests/core_pyutils/__init__.py

Whitespace-only changes.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import copy
2+
from graphql.core.pyutils.default_ordered_dict import DefaultOrderedDict
3+
from pytest import raises
4+
import pickle
5+
6+
def test_will_missing_will_set_value_from_factory():
7+
d = DefaultOrderedDict(list)
8+
f = d['foo']
9+
assert isinstance(f, list)
10+
assert d['foo'] is f
11+
12+
13+
def test_preserves_input_order():
14+
d = DefaultOrderedDict(list)
15+
d['a'].append(1)
16+
d['b'].append(2)
17+
d['c'].append(3)
18+
d['a'].append(4)
19+
20+
assert list(d.keys()) == ['a', 'b', 'c']
21+
assert list(d.values()) == [[1, 4], [2], [3]]
22+
23+
24+
def test_will_act_list_default_dict_if_no_factory_defined():
25+
d = DefaultOrderedDict()
26+
27+
with raises(KeyError) as excinfo:
28+
assert d['test']
29+
30+
assert str(excinfo.value) == "'test'"
31+
32+
33+
def test_will_repr_properly():
34+
d = DefaultOrderedDict(list, [('a', 1), ('b', 2)])
35+
assert repr(d) == "DefaultOrderedDict({}, [('a', 1), ('b', 2)])".format(list)
36+
37+
38+
def test_requires_callable_default_factory():
39+
with raises(TypeError) as excinfo:
40+
DefaultOrderedDict('not callable')
41+
42+
assert str(excinfo.value) == 'first argument must be callable'
43+
44+
45+
def test_picklable():
46+
d = DefaultOrderedDict(list, [('a', 1), ('b', 2)])
47+
d_copied = pickle.loads(pickle.dumps(d))
48+
49+
assert d_copied == d
50+
assert d.default_factory == d_copied.default_factory
51+
52+
d = DefaultOrderedDict(None, [('a', 1), ('b', 2)])
53+
d_copied = pickle.loads(pickle.dumps(d))
54+
55+
assert d_copied == d
56+
assert d.default_factory == d_copied.default_factory
57+
58+
59+
def test_copy():
60+
d = DefaultOrderedDict(list, [('a', [1, 2]), ('b', [3, 4])])
61+
d_copied = copy.copy(d)
62+
63+
assert d_copied == d
64+
assert d.default_factory == d_copied.default_factory
65+
assert d_copied['a'] is d['a']
66+
assert d_copied['b'] is d['b']
67+
68+
d_copied = d.copy()
69+
70+
assert d_copied == d
71+
assert d.default_factory == d_copied.default_factory
72+
assert d_copied['a'] is d['a']
73+
assert d_copied['b'] is d['b']
74+
75+
76+
def test_deep_copy():
77+
d = DefaultOrderedDict(list, [('a', [1, 2]), ('b', [3, 4])])
78+
d_copied = copy.deepcopy(d)
79+
80+
assert d_copied == d
81+
assert d.default_factory == d_copied.default_factory
82+
assert d_copied['a'] == d['a']
83+
assert d_copied['a'] is not d['a']
84+
assert d_copied['b'] == d['b']
85+
assert d_copied['b'] is not d['b']

tests_py35/core_execution/test_asyncio_executor.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def wrapper(*args, **kwargs):
2323

2424
@run_until_complete
2525
async def test_asyncio_py35_executor():
26-
doc = 'query Example { a, b }'
26+
doc = 'query Example { a, b, c }'
2727

2828
async def resolver(context, *_):
2929
await asyncio.sleep(0.001)
@@ -33,15 +33,19 @@ async def resolver_2(context, *_):
3333
await asyncio.sleep(0.003)
3434
return 'hey2'
3535

36+
def resolver_3(context, *_):
37+
return 'hey3'
38+
3639
Type = GraphQLObjectType('Type', {
3740
'a': GraphQLField(GraphQLString, resolver=resolver),
38-
'b': GraphQLField(GraphQLString, resolver=resolver_2)
41+
'b': GraphQLField(GraphQLString, resolver=resolver_2),
42+
'c': GraphQLField(GraphQLString, resolver=resolver_3)
3943
})
4044

4145
executor = Executor([AsyncioExecutionMiddleware()])
4246
result = await executor.execute(GraphQLSchema(Type), doc)
4347
assert not result.errors
44-
assert result.data == {'a': 'hey', 'b': 'hey2'}
48+
assert result.data == {'a': 'hey', 'b': 'hey2', 'c': 'hey3'}
4549

4650
@run_until_complete
4751
async def test_asyncio_py35_executor_with_error():

0 commit comments

Comments
 (0)