5
This module provides a very simple way to integrate your tests with the
8
You must import this module *before* importing anything from Twisted itself!
12
from nose.twistedtools import reactor, deferred
16
return reactor.resolve("www.python.org")
18
Or, more realistically::
20
@deferred(timeout=5.0)
22
d = reactor.resolve("www.python.org")
24
assert ip == "67.15.36.43"
25
d.addCallback(check_ip)
28
.. _Twisted: http://twistedmatrix.com/trac/
32
from Queue import Queue, Empty
33
from nose.tools import make_decorator, TimeExpired
36
'threaded_reactor', 'reactor', 'deferred', 'TimeExpired',
40
_twisted_thread = None
42
def threaded_reactor():
44
Start the Twisted reactor in a separate thread, if not already done.
46
The thread will automatically be destroyed when all the tests are done.
48
global _twisted_thread
50
from twisted.internet import reactor
53
if not _twisted_thread:
54
from twisted.python import threadable
55
from threading import Thread
56
_twisted_thread = Thread(target=lambda: reactor.run( \
57
installSignalHandlers=False))
58
_twisted_thread.setDaemon(True)
59
_twisted_thread.start()
60
return reactor, _twisted_thread
62
# Export global reactor variable, as Twisted does
63
reactor, reactor_thread = threaded_reactor()
67
"""Stop the reactor and join the reactor thread until it stops.
68
Call this function in teardown at the module or package level to
69
reset the twisted system after your tests. You *must* do this if
70
you mix tests using these tools and tests using twisted.trial.
72
global _twisted_thread
75
for p in reactor.getDelayedCalls():
78
_twisted_thread = None
81
def deferred(timeout=None):
83
By wrapping a test function with this decorator, you can return a
84
twisted Deferred and the test will wait for the deferred to be triggered.
85
The whole test function will run inside the Twisted event loop.
87
The optional timeout parameter specifies the maximum duration of the test.
88
The difference with timed() is that timed() will still wait for the test
89
to end, while deferred() will stop the test when its timeout has expired.
90
The latter is more desireable when dealing with network tests, because
91
the result may actually never arrive.
93
If the callback is triggered, the test has passed.
94
If the errback is triggered or the timeout expires, the test has failed.
98
@deferred(timeout=5.0)
100
return reactor.resolve("nose.python-hosting.com")
102
Attention! If you combine this decorator with other decorators (like
103
"raises"), deferred() must be called *first*!
105
In other words, this is good::
107
@raises(DNSLookupError)
110
return reactor.resolve("xxxjhjhj.biz")
115
@raises(DNSLookupError)
117
return reactor.resolve("xxxjhjhj.biz")
119
reactor, reactor_thread = threaded_reactor()
121
raise ImportError("twisted is not available or could not be imported")
122
# Check for common syntax mistake
123
# (otherwise, tests can be silently ignored
124
# if one writes "@deferred" instead of "@deferred()")
126
timeout is None or timeout + 0
128
raise TypeError("'timeout' argument must be a number or None")
131
def wrapper(*args, **kargs):
135
def errback(failure):
136
# Retrieve and save full exception info
138
failure.raiseException()
140
q.put(sys.exc_info())
143
d = func(*args, **kargs)
145
d.addCallbacks(callback, errback)
146
# Check for a common mistake and display a nice error
148
except AttributeError:
149
raise TypeError("you must return a twisted Deferred "
150
"from your test case!")
151
# Catch exceptions raised in the test body (from the
154
q.put(sys.exc_info())
155
reactor.callFromThread(g)
157
error = q.get(timeout=timeout)
159
raise TimeExpired("timeout expired before end of test (%f s.)"
161
# Re-raise all exceptions
162
if error is not None:
163
exc_type, exc_value, tb = error
164
raise exc_type, exc_value, tb
165
wrapper = make_decorator(func)(wrapper)