[Twisted-Python] Global reactor unit tests in the Twisted test suite

exarkun at twistedmatrix.com exarkun at twistedmatrix.com
Mon Nov 7 13:25:36 EST 2011


On 06:06 pm, jml at mumak.net wrote:
>On Tue, Nov 1, 2011 at 6:48 PM, Glyph <glyph at twistedmatrix.com> wrote:
>>On Nov 1, 2011, at 12:14 PM, Phil Mayers wrote:
>>>On 01/11/11 15:28, Christopher Armstrong wrote:
>>>>I whole-heartedly agree with the sentiment, though. We need to get 
>>>>rid
>>>>of the global reactor.
>>>
>>>Why?
>>
>>Sometimes you want a different reactor.  The most common reason is 
>>unit testing, although if we could successfully eliminate the global 
>>reactor everywhere, there are other things that we might be able to 
>>do: slowing down or speeding up time from a protocol's perspective (by 
>>replacing IReactorTime), grouping related objects together in reactors 
>>that can be shut down together (so that reactor.stop() doesn't 
>>actually end the process); or, similarly, suspending a group of 
>>related objects (removeReader()/removeWriter() on everything) so that 
>>they can be inspected for debugging purposes, without suspending the 
>>thing doing the inspecting (a manhole python prompt).
>>>I find the "pass reactor as 1st argument to everything" API pattern
>>>messy. I'm sure there's a good reason. What is it?
>>
>>This pattern is a solution to the problem, but I agree that it is 
>>possibly not the optimal solution.  It sort of points in a direction 
>>where every possible module that might be imported becomes an argument 
>>to your function.  After all, there are plenty of other modules which 
>>have to be mocked for testing, why not just make everyone's __init__ 
>>method take sys.modules as an argument too, and never import anything? 
>>In more complex systems this can definitely turn into a bit of a mess.
>>
>>Nevertheless, "real reactor as default argument" is not a huge 
>>improvement either, because it typically breaks one level out.  If you 
>>have 'a(reactor=defaultReactor)' and then 'b()' needs to call 'a', 
>>half the time 'b' will forget to supply a reactor argument and call 
>>'a()' passing nothing, because that appears to be the suggested API. 
>>Any code that calls 'b()' then needs to deal with the fact that the 
>>global reactor is going to get used, even if it has a cleanly 
>>encapsulated parameter of its own.  With sufficient discipline, of 
>>course, this approach is equivalent.
>...
>>If you can think of a better solution that addresses all of these 
>>concerns simultaneously somehow, please share, I'd love to hear it 
>>:-).
>
>reactor = getUtility(IReactor)?

That's not really a solution.  It's the barest glimpse of a large system 
which might be applied as a solution.  Do you want to expand it?

Jean-Paul



More information about the Twisted-Python mailing list