66
77import six
88
9- from testtools import TestCase
10- from testtools .matchers import raises as match_raises , MatchesException
11-
129from . import (
1310 ComposedDispatcher , Constant , Effect , Error , TypeDispatcher ,
1411 base_dispatcher , sync_perform , sync_performer )
@@ -19,125 +16,131 @@ def perf(e):
1916 return sync_perform (base_dispatcher , e )
2017
2118
22- class DoTests (TestCase ):
23-
24- def test_do_non_gf (self ):
25- """When do is passed a non-generator function, it raises an error."""
26- f = lambda : None
27- self .assertThat (
28- lambda : perf (do (f )()),
29- match_raises (TypeError (
30- "%r is not a generator function. It returned None." % (f ,)
31- )))
32-
33- def test_do_return (self ):
34- """
35- When a @do function yields a do_return, the given value becomes the
36- eventual result.
37- """
38- @do
39- def f ():
40- yield do_return ("hello" )
41- self .assertEqual (perf (f ()), "hello" )
42-
43- def test_yield_effect (self ):
44- """Yielding an effect in @do results in the Effect's result."""
45- @do
46- def f ():
47- x = yield Effect (Constant (3 ))
48- yield do_return (x )
49- self .assertEqual (perf (f ()), 3 )
50-
51- def test_fall_off_the_end (self ):
52- """Falling off the end results in None."""
53- @do
54- def f ():
55- yield Effect (Constant (3 ))
56- self .assertEqual (perf (f ()), None )
57-
58- def test_yield_non_effect (self ):
59- """Yielding a non-Effect results in a TypeError."""
60- @do
61- def f ():
62- yield 1
63- result = f ()
64- e = self .assertRaises (TypeError , lambda : perf (result ))
65- self .assertTrue (
66- str (e ).startswith (
67- '@do functions must only yield Effects or results of '
68- 'do_return. Got 1 from <generator object f at' ))
69-
70- def test_raise_from_effect (self ):
71- """
72- If an Effect results in an error, it will be raised as a synchronous
73- exception in the generator.
74- """
75- @do
76- def f ():
77- try :
78- yield Effect (Error (ZeroDivisionError ('foo' )))
79- except :
80- got_error = sys .exc_info ()
81- yield do_return (got_error )
82-
83- self .assertThat (
84- perf (f ()),
85- MatchesException (ZeroDivisionError ('foo' )))
86-
87- def test_works_with_sync_perform (self ):
88- """@sync_performer and @do cooperate fine."""
89- @sync_performer
90- @do
91- def perform_myintent (dispatcher , myintent ):
92- result = yield Effect (Constant (1 ))
93- yield do_return (result + 1 )
94-
95- class MyIntent (object ):
96- pass
97-
98- disp = ComposedDispatcher ([
99- TypeDispatcher ({MyIntent : perform_myintent }),
100- base_dispatcher ])
101- self .assertEqual (sync_perform (disp , Effect (MyIntent ())), 2 )
102-
103- def test_promote_metadata (self ):
104- """
105- The decorator copies metadata from the wrapped function onto the
106- wrapper.
107- """
108- def original (dispatcher , intent ):
109- """Original!"""
110- yield do_return (1 )
111- original .attr = 1
112- wrapped = do (original )
113- self .assertEqual (wrapped .__name__ , 'original' )
114- self .assertEqual (wrapped .attr , 1 )
115- self .assertEqual (wrapped .__doc__ , 'Original!' )
116-
117- def test_ignore_lack_of_metadata (self ):
118- """
119- When the original callable is not a function, a new function is still
120- returned.
121- """
122- def original (something , dispatcher , intent ):
123- """Original!"""
124- pass
125- new_func = partial (original , 'something' )
126- original .attr = 1
127- wrapped = do (new_func )
128- self .assertEqual (wrapped .__name__ , 'do_wrapper' )
129-
130- def test_repeatable_effect (self ):
131- """
132- The Effect returned by the call to the @do function is repeatable.
133- """
134- @do
135- def f ():
136- x = yield Effect (Constant ('foo' ))
137- yield do_return (x )
138- eff = f ()
139- self .assertEqual (perf (eff ), 'foo' )
140- self .assertEqual (perf (eff ), 'foo' )
19+ def test_do_non_gf ():
20+ """When do is passed a non-generator function, it raises an error."""
21+ f = lambda : None
22+ with raises (TypeError ) as err_info :
23+ perf (do (f )())
24+ assert (str (err_info .value )
25+ == "%r is not a generator function. It returned None." % (f ,))
26+
27+
28+ def test_do_return ():
29+ """
30+ When a @do function yields a do_return, the given value becomes the
31+ eventual result.
32+ """
33+ @do
34+ def f ():
35+ yield do_return ("hello" )
36+ assert perf (f ()) == "hello"
37+
38+
39+ def test_yield_effect ():
40+ """Yielding an effect in @do results in the Effect's result."""
41+ @do
42+ def f ():
43+ x = yield Effect (Constant (3 ))
44+ yield do_return (x )
45+ perf (f ()) == 3
46+
47+
48+ def test_fall_off_the_end ():
49+ """Falling off the end results in None."""
50+ @do
51+ def f ():
52+ yield Effect (Constant (3 ))
53+ assert perf (f ()) is None
54+
55+
56+ def test_yield_non_effect ():
57+ """Yielding a non-Effect results in a TypeError."""
58+ @do
59+ def f ():
60+ yield 1
61+ result = f ()
62+ with raises (TypeError ) as err_info :
63+ perf (result )
64+ assert str (err_info .value ).startswith (
65+ '@do functions must only yield Effects or results of '
66+ 'do_return. Got 1 from <generator object f at' )
67+
68+
69+ def test_raise_from_effect ():
70+ """
71+ If an Effect results in an error, it will be raised as a synchronous
72+ exception in the generator.
73+ """
74+ @do
75+ def f ():
76+ try :
77+ yield Effect (Error (ZeroDivisionError ('foo' )))
78+ except :
79+ got_error = sys .exc_info ()
80+ yield do_return (got_error )
81+
82+ exc_type , exc , _ = perf (f ())
83+ assert exc_type is ZeroDivisionError
84+ assert str (exc ) == 'foo'
85+
86+
87+ def test_works_with_sync_perform ():
88+ """@sync_performer and @do cooperate fine."""
89+ @sync_performer
90+ @do
91+ def perform_myintent (dispatcher , myintent ):
92+ result = yield Effect (Constant (1 ))
93+ yield do_return (result + 1 )
94+
95+ class MyIntent (object ):
96+ pass
97+
98+ disp = ComposedDispatcher ([
99+ TypeDispatcher ({MyIntent : perform_myintent }),
100+ base_dispatcher ])
101+ assert sync_perform (disp , Effect (MyIntent ())) == 2
102+
103+
104+ def test_promote_metadata ():
105+ """
106+ The decorator copies metadata from the wrapped function onto the
107+ wrapper.
108+ """
109+ def original (dispatcher , intent ):
110+ """Original!"""
111+ yield do_return (1 )
112+ original .attr = 1
113+ wrapped = do (original )
114+ assert wrapped .__name__ == 'original'
115+ assert wrapped .attr == 1
116+ assert wrapped .__doc__ == 'Original!'
117+
118+
119+ def test_ignore_lack_of_metadata ():
120+ """
121+ When the original callable is not a function, a new function is still
122+ returned.
123+ """
124+ def original (something , dispatcher , intent ):
125+ """Original!"""
126+ pass
127+ new_func = partial (original , 'something' )
128+ original .attr = 1
129+ wrapped = do (new_func )
130+ assert wrapped .__name__ == 'do_wrapper'
131+
132+
133+ def test_repeatable_effect ():
134+ """
135+ The Effect returned by the call to the @do function is repeatable.
136+ """
137+ @do
138+ def f ():
139+ x = yield Effect (Constant ('foo' ))
140+ yield do_return (x )
141+ eff = f ()
142+ assert perf (eff ) == 'foo'
143+ perf (eff ) == 'foo'
141144
142145
143146def test_stop_iteration_only_local ():
0 commit comments