diff -r de13ce98ca3b Doc/library/socketserver.rst --- a/Doc/library/socketserver.rst Mon Jul 25 19:58:13 2011 +0100 +++ b/Doc/library/socketserver.rst Tue Jul 26 00:42:12 2011 +0200 @@ -173,6 +173,11 @@ Tells the :meth:`serve_forever` loop to stop and waits until it does. +.. attribute:: BaseServer.running + + True if the server is running. + + .. versionadded:: 3.3 .. attribute:: BaseServer.address_family diff -r de13ce98ca3b Lib/socketserver.py --- a/Lib/socketserver.py Mon Jul 25 19:58:13 2011 +0100 +++ b/Lib/socketserver.py Tue Jul 26 00:42:12 2011 +0200 @@ -200,6 +200,22 @@ self.RequestHandlerClass = RequestHandlerClass self.__is_shut_down = threading.Event() self.__shutdown_request = False + self.__running = False + + def __repr__(self): + info = [] + name = self.__class__.__module__ + "." + self.__class__.__name__ + info.append("status=%s" % ("running" if self.running else "stopped")) + if self.server_address: + info.append("address=%s" % repr(self.server_address)) + return '<%s %s at %#x>' % (name, ', '.join(info), id(self)) + + __str__ = __repr__ + + @property + def running(self): + """Return True if the server is running.""" + return self.__running def server_activate(self): """Called by constructor to activate the server. @@ -216,6 +232,9 @@ self.timeout. If you need to do periodic tasks, do them in another thread. """ + if self.running: + raise RuntimeError("the server is already running") + self.__running = True self.__is_shut_down.clear() try: while not self.__shutdown_request: @@ -230,6 +249,7 @@ self.service_actions() finally: self.__shutdown_request = False + self.__running = False self.__is_shut_down.set() def shutdown(self): @@ -239,6 +259,8 @@ serve_forever() is running in another thread, or it will deadlock. """ + if not self.running: + raise RuntimeError("the server is not running") self.__shutdown_request = True self.__is_shut_down.wait() diff -r de13ce98ca3b Lib/test/test_socketserver.py --- a/Lib/test/test_socketserver.py Mon Jul 25 19:58:13 2011 +0100 +++ b/Lib/test/test_socketserver.py Tue Jul 26 00:42:12 2011 +0200 @@ -11,6 +11,7 @@ import tempfile import unittest import socketserver +import time import test.support from test.support import reap_children, reap_threads, verbose @@ -266,13 +267,39 @@ kwargs={'poll_interval':0.01}) t.daemon = True # In case this function raises. threads.append((t, s)) + for t, s in threads: t.start() + for t, s in threads: s.shutdown() for t, s in threads: t.join() s.server_close() + def test_running(self): + class MyServer(socketserver.TCPServer): + pass + + class MyHandler(socketserver.StreamRequestHandler): + pass + + s = MyServer((HOST, 0), MyHandler) + with self.assertRaises(RuntimeError) as cm: + s.shutdown() + self.assertIn('not running', str(cm.exception)) + + t = threading.Thread(target=s.serve_forever, + kwargs={'poll_interval':0.01}) + t.start() + time.sleep(.1) + try: + with self.assertRaises(RuntimeError) as cm: + s.serve_forever() + self.assertIn('already running', str(cm.exception)) + finally: + s.shutdown() + s.server_close() + def test_main(): if imp.lock_held():