~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_server.py

Merge pt1 hooks branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
 
24
24
from bzrlib import (
 
25
    cethread,
25
26
    osutils,
26
27
    transport,
27
28
    urlutils,
242
243
        raise NotImplementedError
243
244
 
244
245
 
245
 
class ThreadWithException(threading.Thread):
246
 
    """A catching exception thread.
247
 
 
248
 
    If an exception occurs during the thread execution, it's caught and
249
 
    re-raised when the thread is joined().
250
 
    """
251
 
 
252
 
    def __init__(self, *args, **kwargs):
253
 
        # There are cases where the calling thread must wait, yet, if an
254
 
        # exception occurs, the event should be set so the caller is not
255
 
        # blocked. The main example is a calling thread that want to wait for
256
 
        # the called thread to be in a given state before continuing.
257
 
        try:
258
 
            event = kwargs.pop('event')
259
 
        except KeyError:
260
 
            # If the caller didn't pass a specific event, create our own
261
 
            event = threading.Event()
262
 
        super(ThreadWithException, self).__init__(*args, **kwargs)
263
 
        self.set_ready_event(event)
264
 
        self.exception = None
265
 
        self.ignored_exceptions = None # see set_ignored_exceptions
266
 
 
267
 
    # compatibility thunk for python-2.4 and python-2.5...
268
 
    if sys.version_info < (2, 6):
269
 
        name = property(threading.Thread.getName, threading.Thread.setName)
270
 
 
271
 
    def set_ready_event(self, event):
272
 
        """Set the ``ready`` event used to synchronize exception catching.
273
 
 
274
 
        When the thread uses an event to synchronize itself with another thread
275
 
        (setting it when the other thread can wake up from a ``wait`` call),
276
 
        the event must be set after catching an exception or the other thread
277
 
        will hang.
278
 
 
279
 
        Some threads require multiple events and should set the relevant one
280
 
        when appropriate.
281
 
        """
282
 
        self.ready = event
283
 
 
284
 
    def set_ignored_exceptions(self, ignored):
285
 
        """Declare which exceptions will be ignored.
286
 
 
287
 
        :param ignored: Can be either:
288
 
           - None: all exceptions will be raised,
289
 
           - an exception class: the instances of this class will be ignored,
290
 
           - a tuple of exception classes: the instances of any class of the
291
 
             list will be ignored,
292
 
           - a callable: that will be passed the exception object
293
 
             and should return True if the exception should be ignored
294
 
        """
295
 
        if ignored is None:
296
 
            self.ignored_exceptions = None
297
 
        elif isinstance(ignored, (Exception, tuple)):
298
 
            self.ignored_exceptions = lambda e: isinstance(e, ignored)
299
 
        else:
300
 
            self.ignored_exceptions = ignored
301
 
 
302
 
    def run(self):
303
 
        """Overrides Thread.run to capture any exception."""
304
 
        self.ready.clear()
305
 
        try:
306
 
            try:
307
 
                super(ThreadWithException, self).run()
308
 
            except:
309
 
                self.exception = sys.exc_info()
310
 
        finally:
311
 
            # Make sure the calling thread is released
312
 
            self.ready.set()
313
 
 
 
246
class TestThread(cethread.CatchingExceptionThread):
314
247
 
315
248
    def join(self, timeout=5):
316
 
        """Overrides Thread.join to raise any exception caught.
317
 
 
318
 
 
319
 
        Calling join(timeout=0) will raise the caught exception or return None
320
 
        if the thread is still alive.
 
249
        """Overrides to use a default timeout.
321
250
 
322
251
        The default timeout is set to 5 and should expire only when a thread
323
252
        serving a client connection is hung.
324
253
        """
325
 
        super(ThreadWithException, self).join(timeout)
326
 
        if self.exception is not None:
327
 
            exc_class, exc_value, exc_tb = self.exception
328
 
            self.exception = None # The exception should be raised only once
329
 
            if (self.ignored_exceptions is None
330
 
                or not self.ignored_exceptions(exc_value)):
331
 
                # Raise non ignored exceptions
332
 
                raise exc_class, exc_value, exc_tb
 
254
        super(TestThread, self).join(timeout)
333
255
        if timeout and self.isAlive():
334
256
            # The timeout expired without joining the thread, the thread is
335
257
            # therefore stucked and that's a failure as far as the test is
342
264
            sys.stderr.write('thread %s hung\n' % (self.name,))
343
265
            #raise AssertionError('thread %s hung' % (self.name,))
344
266
 
345
 
    def pending_exception(self):
346
 
        """Raise the caught exception.
347
 
 
348
 
        This does nothing if no exception occurred.
349
 
        """
350
 
        self.join(timeout=0)
351
 
 
352
267
 
353
268
class TestingTCPServerMixin:
354
269
    """Mixin to support running SocketServer.TCPServer in a thread.
522
437
        """Start a new thread to process the request."""
523
438
        started = threading.Event()
524
439
        stopped = threading.Event()
525
 
        t = ThreadWithException(
526
 
            event=stopped,
 
440
        t = TestThread(
 
441
            sync_event=stopped,
527
442
            name='%s -> %s' % (client_address, self.server_address),
528
443
            target = self.process_request_thread,
529
444
            args = (started, stopped, request, client_address))
589
504
 
590
505
    def start_server(self):
591
506
        self.server = self.create_server()
592
 
        self._server_thread = ThreadWithException(
593
 
            event=self.server.started,
 
507
        self._server_thread = TestThread(
 
508
            sync_event=self.server.started,
594
509
            target=self.run_server)
595
510
        self._server_thread.start()
596
511
        # Wait for the server thread to start (i.e release the lock)
606
521
        self._server_thread.pending_exception()
607
522
        # From now on, we'll use a different event to ensure the server can set
608
523
        # its exception
609
 
        self._server_thread.set_ready_event(self.server.stopped)
 
524
        self._server_thread.set_sync_event(self.server.stopped)
610
525
 
611
526
    def run_server(self):
612
527
        self.server.serve()