Rev 2206: Merge from bzr.dev, resolving conflicts. in sftp://bazaar.launchpad.net/%7Ebzr/bzr/hpss/

Andrew Bennetts andrew.bennetts at canonical.com
Tue Apr 10 16:57:51 BST 2007


At sftp://bazaar.launchpad.net/%7Ebzr/bzr/hpss/

------------------------------------------------------------
revno: 2206
revision-id: andrew.bennetts at canonical.com-20070410155415-hyzlzwevu3ud0dny
parent: robertc at robertcollins.net-20070405093526-bfbpg82tlc7kldni
parent: pqm at pqm.ubuntu.com-20070410074302-cf6b95587a1058cd
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: hpss
timestamp: Wed 2007-04-11 01:54:15 +1000
message:
  Merge from bzr.dev, resolving conflicts.
added:
  bzrlib/hooks.py                hooks.py-20070325015548-ix4np2q0kd8452au-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
  bzrlib/smart/medium.py         medium.py-20061103051856-rgu2huy59fkz902q-1
  bzrlib/smart/server.py         server.py-20061110062051-chzu10y32vx8gvur-1
  bzrlib/tests/HTTPTestUtil.py   HTTPTestUtil.py-20050914180604-247d3aafb7a43343
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/blackbox/test_serve.py test_serve.py-20060913064329-8t2pvmsikl4s3xhl-1
  bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
  bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
  bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
  bzrlib/tests/test_transport_implementations.py test_transport_implementations.py-20051227111451-f97c5c7d5c49fce7
  bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
  bzrlib/transport/http/__init__.py http_transport.py-20050711212304-506c5fd1059ace96
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.21
    merged: pqm at pqm.ubuntu.com-20070410074302-cf6b95587a1058cd
    parent: pqm at pqm.ubuntu.com-20070405073143-8fa894c829ab5e50
    parent: andrew.bennetts at canonical.com-20070410072043-5vqutcw42bsml4tl
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Tue 2007-04-10 08:43:02 +0100
    message:
      (Andrew Bennetts) Split bzrlib/transport/smart.py into several smaller modules.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.9
        merged: andrew.bennetts at canonical.com-20070410072043-5vqutcw42bsml4tl
        parent: andrew.bennetts at canonical.com-20070410055415-58qifwhu6xfrb5sr
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Tue 2007-04-10 17:20:43 +1000
        message:
          Fix blackbox/test_serve, there were some trivial changes that needed to be brought across from the hpss branch.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.8
        merged: andrew.bennetts at canonical.com-20070410055415-58qifwhu6xfrb5sr
        parent: andrew.bennetts at canonical.com-20070410054902-iphhiovkapcnzl9k
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Tue 2007-04-10 15:54:15 +1000
        message:
          Add a NEWS entry.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.7
        merged: andrew.bennetts at canonical.com-20070410054902-iphhiovkapcnzl9k
        parent: andrew.bennetts at canonical.com-20070410023142-7xgeph6572ikrnu8
        parent: pqm at pqm.ubuntu.com-20070405073143-8fa894c829ab5e50
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Tue 2007-04-10 15:49:02 +1000
        message:
          Merge from bzr.dev.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.6
        merged: andrew.bennetts at canonical.com-20070410023142-7xgeph6572ikrnu8
        parent: andrew.bennetts at canonical.com-20070410022255-e1dhysj2zhukca5c
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Tue 2007-04-10 12:31:42 +1000
        message:
          Cosmetic changes to minimise the difference between this branch and the hpss branch.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.5
        merged: andrew.bennetts at canonical.com-20070410022255-e1dhysj2zhukca5c
        parent: andrew.bennetts at canonical.com-20070410020914-rjhtq6bfcbrj2vjs
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Tue 2007-04-10 12:22:55 +1000
        message:
          Add some missing docstrings and copyright boilerplate.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.4
        merged: andrew.bennetts at canonical.com-20070410020914-rjhtq6bfcbrj2vjs
        parent: andrew.bennetts at canonical.com-20070409044955-h3ijql5cvqxia4d4
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Tue 2007-04-10 12:09:14 +1000
        message:
          Tidy up accidental changes.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.3
        merged: andrew.bennetts at canonical.com-20070409044955-h3ijql5cvqxia4d4
        parent: andrew.bennetts at canonical.com-20070406011930-wy4emdgu3yu527pm
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Mon 2007-04-09 14:49:55 +1000
        message:
          Split smart transport code into several separate modules.
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.2
        merged: andrew.bennetts at canonical.com-20070406011930-wy4emdgu3yu527pm
        parent: andrew.bennetts at canonical.com-20070405152057-gokz8syll1lk2nrm
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Fri 2007-04-06 11:19:30 +1000
        message:
          Move SmartTCPServer classes into bzrlib/smart/server.py
        ------------------------------------------------------------
        revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.19.1.1
        merged: andrew.bennetts at canonical.com-20070405152057-gokz8syll1lk2nrm
        parent: pqm at pqm.ubuntu.com-20070404213903-fe78a7a55c37ee95
        committer: Andrew Bennetts <andrew.bennetts at canonical.com>
        branch nick: split-smart-part-1-rename
        timestamp: Fri 2007-04-06 01:20:57 +1000
        message:
          Rename bzrlib/transport/smart.py to bzrlib/transport/remote.py.
          
          First part of the code reorganisation from the hpss branch.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.74.2.2.2.20
    merged: pqm at pqm.ubuntu.com-20070405073143-8fa894c829ab5e50
    parent: pqm at pqm.ubuntu.com-20070404213903-fe78a7a55c37ee95
    parent: robertc at robertcollins.net-20070405065917-mu99paqftpq55f5m
    committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
    branch nick: +trunk
    timestamp: Thu 2007-04-05 08:31:43 +0100
    message:
      (robertc) SmartServer startup and shutdown hooks, and smart server shutdown logic implementation. (Robert Collins)
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.9
    merged: robertc at robertcollins.net-20070405065917-mu99paqftpq55f5m
    parent: robertc at robertcollins.net-20070405003903-u1ys8t2lo5gs6b35
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: hpss-hooks
    timestamp: Thu 2007-04-05 16:59:17 +1000
    message:
      Review comments.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.8
    merged: robertc at robertcollins.net-20070405003903-u1ys8t2lo5gs6b35
    parent: robertc at robertcollins.net-20070404085028-abgpe4zylhsdoaqd
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: hpss-hooks
    timestamp: Thu 2007-04-05 10:39:03 +1000
    message:
      Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.7
    merged: robertc at robertcollins.net-20070404085028-abgpe4zylhsdoaqd
    parent: robertc at robertcollins.net-20070404051938-2lnvpsm2tbo5a6g2
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: integration
    timestamp: Wed 2007-04-04 18:50:28 +1000
    message:
      Avoid logging smart server errors when ctrl-C'd.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.6
    merged: robertc at robertcollins.net-20070404051938-2lnvpsm2tbo5a6g2
    parent: robertc at robertcollins.net-20070327214048-286uc943sgxma2xe
    parent: pqm at pqm.ubuntu.com-20070404022946-b93fc2e49b3bb195
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: hpss-hooks
    timestamp: Wed 2007-04-04 15:19:38 +1000
    message:
      Merge bzr.dev.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.5
    merged: robertc at robertcollins.net-20070327214048-286uc943sgxma2xe
    parent: robertc at robertcollins.net-20070327045759-hb7j634wpm1ris7l
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: integration
    timestamp: Wed 2007-03-28 07:40:48 +1000
    message:
      Fix Branch hook tests for the new eans of preserving hooks by the test suite.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.4
    merged: robertc at robertcollins.net-20070327045759-hb7j634wpm1ris7l
    parent: robertc at robertcollins.net-20070327043224-9ql8frcekqjbb64g
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: integration
    timestamp: Tue 2007-03-27 14:57:59 +1000
    message:
      Add more debugging code to the smart server to debug pqm failures.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.3
    merged: robertc at robertcollins.net-20070327043224-9ql8frcekqjbb64g
    parent: robertc at robertcollins.net-20070327042126-32pgs6ityvdt72tg
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: integration
    timestamp: Tue 2007-03-27 14:32:24 +1000
    message:
      Fix all smart_transport tests.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.2
    merged: robertc at robertcollins.net-20070327042126-32pgs6ityvdt72tg
    parent: robertc at robertcollins.net-20070327024546-c7t95nl7j9t4x3jq
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: integration
    timestamp: Tue 2007-03-27 14:21:26 +1000
    message:
      Convert unconvered SmartServer subclass to use the cached sockname.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.71.3.1
    merged: robertc at robertcollins.net-20070327024546-c7t95nl7j9t4x3jq
    parent: pqm at pqm.ubuntu.com-20070326073003-37941d0fa5a5a6c4
    parent: robertc at robertcollins.net-20070327024201-ntm3ek1rrkkmg2yd
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: integration
    timestamp: Tue 2007-03-27 12:45:46 +1000
    message:
      New SmartServer hooks facility. There are two initial hooks documented in
      bzrlib.transport.smart.SmartServerHooks. The two initial hooks allow
      plugins to execute code upon server startup and shutdown.
      (Robert Collins).
      
      SmartServer in standalone mode will now close its listening socket when it
      stops, rather than waiting for garbage collection.  This primarily fixes
      test suite hangs when a test tries to connect to a shutdown server.  It
      may also help improve behaviour when dealing with a server running on a
      specific port (rather than dynamically assigned ports).  (Robert Collins)
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.65.4.2
    merged: robertc at robertcollins.net-20070327024201-ntm3ek1rrkkmg2yd
    parent: robertc at robertcollins.net-20070325085956-my8jv7cifqzyltyz
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: hpss-hooks
    timestamp: Tue 2007-03-27 12:42:01 +1000
    message:
      Review feedback.
    ------------------------------------------------------------
    revno: 2018.1.2.1.50.2.80.1.99.1.9.1.21.1.26.2.65.4.1
    merged: robertc at robertcollins.net-20070325085956-my8jv7cifqzyltyz
    parent: pqm at pqm.ubuntu.com-20070321071219-55447700ec71371f
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: hpss-hooks
    timestamp: Sun 2007-03-25 18:59:56 +1000
    message:
      New SmartServer hooks facility. There are two initial hooks documented
      in bzrlib.transport.smart.SmartServerHooks. The two initial hooks allow
      plugins to execute code upon server startup and shutdown.
      (Robert Collins).
      
      SmartServer in standalone mode will now close its listening socket
      when it stops, rather than waiting for garbage collection. This primarily
      fixes test suite hangs when a test tries to connect to a shutdown server.
      It may also help improve behaviour when dealing with a server running
      on a specific port (rather than dynamically assigned ports).
      (Robert Collins)
=== added file 'bzrlib/hooks.py'
--- a/bzrlib/hooks.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/hooks.py	2007-03-25 08:59:56 +0000
@@ -0,0 +1,46 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+"""Support for plugin hooking logic."""
+from bzrlib.lazy_import import lazy_import
+lazy_import(globals(), """
+from bzrlib import (
+        errors,
+        )
+""")
+
+
+class Hooks(dict):
+    """A dictionary mapping hook name to a list of callables.
+    
+    e.g. ['FOO'] Is the list of items to be called when the
+    FOO hook is triggered.
+    """
+
+    def install_hook(self, hook_name, a_callable):
+        """Install a_callable in to the hook hook_name.
+
+        :param hook_name: A hook name. See the __init__ method of BranchHooks
+            for the complete list of hooks.
+        :param a_callable: The callable to be invoked when the hook triggers.
+            The exact signature will depend on the hook - see the __init__ 
+            method of BranchHooks for details on each hook.
+        """
+        try:
+            self[hook_name].append(a_callable)
+        except KeyError:
+            raise errors.UnknownHook(self.__class__.__name__, hook_name)

=== modified file 'NEWS'
--- a/NEWS	2007-04-05 09:35:26 +0000
+++ b/NEWS	2007-04-10 15:54:15 +0000
@@ -25,6 +25,22 @@
     * Convenience method TestCase.expectFailure ensures that known failures
       do not silently pass.  (Aaron Bentley)
 
+    * New SmartServer hooks facility. There are two initial hooks documented
+      in bzrlib.transport.smart.SmartServerHooks. The two initial hooks allow
+      plugins to execute code upon server startup and shutdown.
+      (Robert Collins).
+
+    * SmartServer in standalone mode will now close its listening socket
+      when it stops, rather than waiting for garbage collection. This primarily
+      fixes test suite hangs when a test tries to connect to a shutdown server.
+      It may also help improve behaviour when dealing with a server running
+      on a specific port (rather than dynamically assigned ports).
+      (Robert Collins)
+
+    * Move most SmartServer code into a new package, bzrlib/smart.
+      bzrlib/transport/remote.py contains just the Transport classes that used
+      to be in bzrlib/transport/smart.py.  (Andrew Bennetts)
+ 
     * ``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument,
       so that a seperate LockableFiles instance can share a lock if it has the
       right token.  (Andrew Bennetts, Robert Collins)

=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py	2007-03-29 05:09:14 +0000
+++ b/bzrlib/branch.py	2007-04-10 15:54:15 +0000
@@ -54,6 +54,7 @@
                            NotBranchError, UninitializableFormat,
                            UnlistableStore, UnlistableBranch,
                            )
+from bzrlib.hooks import Hooks
 from bzrlib.symbol_versioning import (deprecated_function,
                                       deprecated_method,
                                       DEPRECATED_PARAMETER,
@@ -924,7 +925,7 @@
             control_files.unlock()
 
 
-class BranchHooks(dict):
+class BranchHooks(Hooks):
     """A dictionary mapping hook name to a list of callables for branch hooks.
     
     e.g. ['set_rh'] Is the list of items to be called when the
@@ -937,7 +938,7 @@
         These are all empty initially, because by default nothing should get
         notified.
         """
-        dict.__init__(self)
+        Hooks.__init__(self)
         # Introduced in 0.15:
         # invoked whenever the revision history has been set
         # with set_revision_history. The api signature is
@@ -976,20 +977,6 @@
         # and an empty branch recieves new_revno of 0, new_revid of None.
         self['post_uncommit'] = []
 
-    def install_hook(self, hook_name, a_callable):
-        """Install a_callable in to the hook hook_name.
-
-        :param hook_name: A hook name. See the __init__ method of BranchHooks
-            for the complete list of hooks.
-        :param a_callable: The callable to be invoked when the hook triggers.
-            The exact signature will depend on the hook - see the __init__ 
-            method of BranchHooks for details on each hook.
-        """
-        try:
-            self[hook_name].append(a_callable)
-        except KeyError:
-            raise errors.UnknownHook('branch', hook_name)
-
 
 # install the default hooks into the Branch class.
 Branch.hooks = BranchHooks()

=== modified file 'bzrlib/smart/medium.py'
--- a/bzrlib/smart/medium.py	2006-11-14 22:10:15 +0000
+++ b/bzrlib/smart/medium.py	2007-04-10 15:54:15 +0000
@@ -515,4 +515,3 @@
         """
         return self._medium._read_bytes(count)
 
-

=== modified file 'bzrlib/smart/server.py'
--- a/bzrlib/smart/server.py	2007-04-05 05:41:13 +0000
+++ b/bzrlib/smart/server.py	2007-04-10 15:54:15 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2007 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -16,23 +16,28 @@
 
 """Server for smart-server protocol."""
 
+import errno
 import socket
 import os
 import threading
 
+from bzrlib.hooks import Hooks
 from bzrlib.smart import medium
 from bzrlib import (
     trace,
     transport,
     urlutils,
 )
+from bzrlib.smart.medium import SmartServerSocketStreamMedium
 
 
 class SmartTCPServer(object):
     """Listens on a TCP socket and accepts connections from smart clients.
-    
+
     Each connection will be served by a SmartServerSocketStreamMedium running in
-    thread.
+    a thread.
+
+    hooks: An instance of SmartServerHooks.
     """
 
     def __init__(self, backing_transport, host='127.0.0.1', port=0):
@@ -44,73 +49,142 @@
         :param host: Name of the interface to listen on.
         :param port: TCP port to listen on, or 0 to allocate a transient port.
         """
+        # let connections timeout so that we get a chance to terminate
+        # Keep a reference to the exceptions we want to catch because the socket
+        # module's globals get set to None during interpreter shutdown.
+        from socket import timeout as socket_timeout
+        from socket import error as socket_error
+        self._socket_error = socket_error
+        self._socket_timeout = socket_timeout
         self._server_socket = socket.socket()
         self._server_socket.bind((host, port))
-        self.port = self._server_socket.getsockname()[1]
+        self._sockname = self._server_socket.getsockname()
+        self.port = self._sockname[1]
         self._server_socket.listen(1)
         self._server_socket.settimeout(1)
         self.backing_transport = backing_transport
+        self._started = threading.Event()
+        self._stopped = threading.Event()
 
     def serve(self):
-        # let connections timeout so that we get a chance to terminate
-        # Keep a reference to the exceptions we want to catch because the socket
-        # module's globals get set to None during interpreter shutdown.
-        from socket import timeout as socket_timeout
-        from socket import error as socket_error
         self._should_terminate = False
-        while not self._should_terminate:
-            try:
-                self.accept_and_serve()
-            except socket_timeout:
-                # just check if we're asked to stop
-                pass
-            except socket_error, e:
-                trace.warning("client disconnected: %s", e)
-                pass
+        for hook in SmartTCPServer.hooks['server_started']:
+            hook(self.backing_transport.base, self.get_url())
+        self._started.set()
+        try:
+            try:
+                while not self._should_terminate:
+                    try:
+                        conn, client_addr = self._server_socket.accept()
+                    except self._socket_timeout:
+                        # just check if we're asked to stop
+                        pass
+                    except self._socket_error, e:
+                        # if the socket is closed by stop_background_thread
+                        # we might get a EBADF here, any other socket errors
+                        # should get logged.
+                        if e.args[0] != errno.EBADF:
+                            trace.warning("listening socket error: %s", e)
+                    else:
+                        self.serve_conn(conn)
+            except KeyboardInterrupt:
+                # dont log when CTRL-C'd.
+                raise
+            except Exception, e:
+                trace.error("Unhandled smart server error.")
+                trace.log_exception_quietly()
+                raise
+        finally:
+            self._stopped.set()
+            try:
+                # ensure the server socket is closed.
+                self._server_socket.close()
+            except self._socket_error:
+                # ignore errors on close
+                pass
+            for hook in SmartTCPServer.hooks['server_stopped']:
+                hook(self.backing_transport.base, self.get_url())
 
     def get_url(self):
         """Return the url of the server"""
-        return "bzr://%s:%d/" % self._server_socket.getsockname()
+        return "bzr://%s:%d/" % self._sockname
 
-    def accept_and_serve(self):
-        conn, client_addr = self._server_socket.accept()
+    def serve_conn(self, conn):
         # For WIN32, where the timeout value from the listening socket
         # propogates to the newly accepted socket.
         conn.setblocking(True)
         conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-        handler = medium.SmartServerSocketStreamMedium(conn, self.backing_transport)
-        connection_thread = threading.Thread(
-            None, handler.serve, name='smart-server-child')
+        handler = SmartServerSocketStreamMedium(conn, self.backing_transport)
+        connection_thread = threading.Thread(None, handler.serve, name='smart-server-child')
         connection_thread.setDaemon(True)
         connection_thread.start()
 
     def start_background_thread(self):
+        self._started.clear()
         self._server_thread = threading.Thread(None,
                 self.serve,
                 name='server-' + self.get_url())
         self._server_thread.setDaemon(True)
         self._server_thread.start()
+        self._started.wait()
 
     def stop_background_thread(self):
+        self._stopped.clear()
+        # tell the main loop to quit on the next iteration.
         self._should_terminate = True
-        # self._server_socket.close()
-        # we used to join the thread, but it's not really necessary; it will
-        # terminate in time
-        ## self._server_thread.join()
-
+        # close the socket - gives error to connections from here on in,
+        # rather than a connection reset error to connections made during
+        # the period between setting _should_terminate = True and 
+        # the current request completing/aborting. It may also break out the
+        # main loop if it was currently in accept() (on some platforms).
+        try:
+            self._server_socket.close()
+        except self._socket_error:
+            # ignore errors on close
+            pass
+        if not self._stopped.isSet():
+            # server has not stopped (though it may be stopping)
+            # its likely in accept(), so give it a connection
+            temp_socket = socket.socket()
+            temp_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+            if not temp_socket.connect_ex(self._sockname):
+                # and close it immediately: we dont choose to send any requests.
+                temp_socket.close()
+        self._stopped.wait()
+        self._server_thread.join()
+
+
+class SmartServerHooks(Hooks):
+    """Hooks for the smart server."""
+
+    def __init__(self):
+        """Create the default hooks.
+
+        These are all empty initially, because by default nothing should get
+        notified.
+        """
+        Hooks.__init__(self)
+        # Introduced in 0.16:
+        # invoked whenever the server starts serving a directory.
+        # The api signature is (backing url, public url).
+        self['server_started'] = []
+        # Introduced in 0.16:
+        # invoked whenever the server stops serving a directory.
+        # The api signature is (backing url, public url).
+        self['server_stopped'] = []
+
+SmartTCPServer.hooks = SmartServerHooks()
 
 
 class SmartTCPServer_for_testing(SmartTCPServer):
     """Server suitable for use by transport tests.
     
-    This server has a _homedir of the current cwd.
+    This server is backed by the process's cwd.
     """
 
     def __init__(self):
-        # The server is set up by default like for inetd access: the backing
-        # transport is connected to a local path that is not '/'.
         SmartTCPServer.__init__(self, None)
-
+        
     def get_backing_transport(self, backing_transport_server):
         """Get a backing transport from a server we are decorating."""
         return transport.get_transport(backing_transport_server.get_url())

=== modified file 'bzrlib/tests/HTTPTestUtil.py'
--- a/bzrlib/tests/HTTPTestUtil.py	2007-04-05 09:35:26 +0000
+++ b/bzrlib/tests/HTTPTestUtil.py	2007-04-10 15:54:15 +0000
@@ -31,6 +31,7 @@
 from bzrlib.transport import (
     get_transport,
     )
+from bzrlib.smart import protocol
 
 
 class WallRequestHandler(TestingHTTPRequestHandler):

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2007-04-05 09:35:26 +0000
+++ b/bzrlib/tests/__init__.py	2007-04-10 15:54:15 +0000
@@ -755,7 +755,10 @@
         self._benchcalls = []
         self._benchtime = None
         # prevent hooks affecting tests
-        self._preserved_hooks = bzrlib.branch.Branch.hooks
+        self._preserved_hooks = {
+            bzrlib.branch.Branch:bzrlib.branch.Branch.hooks,
+            bzrlib.smart.server.SmartTCPServer:bzrlib.smart.server.SmartTCPServer.hooks,
+            }
         self.addCleanup(self._restoreHooks)
         # this list of hooks must be kept in sync with the defaults
         # in branch.py
@@ -1098,7 +1101,8 @@
             osutils.set_or_unset_env(name, value)
 
     def _restoreHooks(self):
-        bzrlib.branch.Branch.hooks = self._preserved_hooks
+        for klass, hooks in self._preserved_hooks.items():
+            setattr(klass, 'hooks', hooks)
 
     def knownFailure(self, reason):
         """This test has failed for some known reason."""

=== modified file 'bzrlib/tests/blackbox/test_serve.py'
--- a/bzrlib/tests/blackbox/test_serve.py	2007-03-13 06:55:43 +0000
+++ b/bzrlib/tests/blackbox/test_serve.py	2007-04-10 15:54:15 +0000
@@ -30,9 +30,9 @@
 from bzrlib.branch import Branch
 from bzrlib.bzrdir import BzrDir
 from bzrlib.errors import ParamikoNotPresent
+from bzrlib.smart import medium
 from bzrlib.tests import TestCaseWithTransport, TestSkipped
 from bzrlib.transport import get_transport, remote
-from bzrlib.smart import medium
 
 
 class TestBzrServe(TestCaseWithTransport):

=== modified file 'bzrlib/tests/test_branch.py'
--- a/bzrlib/tests/test_branch.py	2007-03-27 06:42:26 +0000
+++ b/bzrlib/tests/test_branch.py	2007-04-10 15:54:15 +0000
@@ -324,7 +324,7 @@
     def test_installed_hooks_are_BranchHooks(self):
         """The installed hooks object should be a BranchHooks."""
         # the installed hooks are saved in self._preserved_hooks.
-        self.assertIsInstance(self._preserved_hooks, BranchHooks)
+        self.assertIsInstance(self._preserved_hooks[_mod_branch.Branch], BranchHooks)
 
     def test_install_hook_raises_unknown_hook(self):
         """install_hook should raise UnknownHook if a hook is unknown."""

=== modified file 'bzrlib/tests/test_selftest.py'
--- a/bzrlib/tests/test_selftest.py	2007-04-05 09:35:26 +0000
+++ b/bzrlib/tests/test_selftest.py	2007-04-10 15:54:15 +0000
@@ -1179,6 +1179,8 @@
         """The bzrlib hooks should be sanitised by setUp."""
         self.assertEqual(bzrlib.branch.BranchHooks(),
             bzrlib.branch.Branch.hooks)
+        self.assertEqual(bzrlib.smart.server.SmartServerHooks(),
+            bzrlib.smart.server.SmartTCPServer.hooks)
 
     def test__gather_lsprof_in_benchmarks(self):
         """When _gather_lsprof_in_benchmarks is on, accumulate profile data.

=== modified file 'bzrlib/tests/test_smart_transport.py'
--- a/bzrlib/tests/test_smart_transport.py	2007-04-05 09:35:26 +0000
+++ b/bzrlib/tests/test_smart_transport.py	2007-04-10 15:54:15 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2007 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -782,6 +782,7 @@
         """Error reported by server with no specific representation"""
         self._captureVar('NO_SMART_VFS', None)
         class FlakyTransport(object):
+            base = 'a_url'
             def get_bytes(self, path):
                 raise Exception("some random exception from inside server")
         smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
@@ -819,14 +820,38 @@
         self.server = server.SmartTCPServer(self.backing_transport)
         self.server.start_background_thread()
         self.transport = remote.SmartTCPTransport(self.server.get_url())
+        self.addCleanup(self.tearDownServer)
 
-    def tearDown(self):
+    def tearDownServer(self):
         if getattr(self, 'transport', None):
             self.transport.disconnect()
+            del self.transport
         if getattr(self, 'server', None):
             self.server.stop_background_thread()
-        super(SmartTCPTests, self).tearDown()
-        
+            del self.server
+
+
+class TestServerSocketUsage(SmartTCPTests):
+
+    def test_server_setup_teardown(self):
+        """It should be safe to teardown the server with no requests."""
+        self.setUpServer()
+        server = self.server
+        transport = remote.SmartTCPTransport(self.server.get_url())
+        self.tearDownServer()
+        self.assertRaises(errors.ConnectionError, transport.has, '.')
+
+    def test_server_closes_listening_sock_on_shutdown_after_request(self):
+        """The server should close its listening socket when it's stopped."""
+        self.setUpServer()
+        server = self.server
+        self.transport.has('.')
+        self.tearDownServer()
+        # if the listening socket has closed, we should get a BADFD error
+        # when connecting, rather than a hang.
+        transport = remote.SmartTCPTransport(server.get_url())
+        self.assertRaises(errors.ConnectionError, transport.has, '.')
+
 
 class WritableEndToEndTests(SmartTCPTests):
     """Client to server tests that require a writable transport."""
@@ -917,7 +942,46 @@
         self.setUpServer(readonly=True)
         self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
             'foo')
-        
+
+
+class TestServerHooks(SmartTCPTests):
+
+    def capture_server_call(self, backing_url, public_url):
+        """Record a server_started|stopped hook firing."""
+        self.hook_calls.append((backing_url, public_url))
+
+    def test_server_started_hook(self):
+        """The server_started hook fires when the server is started."""
+        self.hook_calls = []
+        server.SmartTCPServer.hooks.install_hook('server_started',
+            self.capture_server_call)
+        self.setUpServer()
+        # at this point, the server will be starting a thread up.
+        # there is no indicator at the moment, so bodge it by doing a request.
+        self.transport.has('.')
+        self.assertEqual([(self.backing_transport.base, self.transport.base)],
+            self.hook_calls)
+
+    def test_server_stopped_hook_simple(self):
+        """The server_stopped hook fires when the server is stopped."""
+        self.hook_calls = []
+        server.SmartTCPServer.hooks.install_hook('server_stopped',
+            self.capture_server_call)
+        self.setUpServer()
+        result = [(self.backing_transport.base, self.transport.base)]
+        # check the stopping message isn't emitted up front.
+        self.assertEqual([], self.hook_calls)
+        # nor after a single message
+        self.transport.has('.')
+        self.assertEqual([], self.hook_calls)
+        # clean up the server
+        self.tearDownServer()
+        # now it should have fired.
+        self.assertEqual(result, self.hook_calls)
+
+# TODO: test that when the server suffers an exception that it calls the
+# server-stopped hook.
+
 
 class SmartServerCommandTests(tests.TestCaseWithTransport):
     """Tests that call directly into the command objects, bypassing the network

=== modified file 'bzrlib/tests/test_transport_implementations.py'
--- a/bzrlib/tests/test_transport_implementations.py	2007-04-05 09:35:26 +0000
+++ b/bzrlib/tests/test_transport_implementations.py	2007-04-10 15:54:15 +0000
@@ -36,11 +36,11 @@
                            TransportNotPossible, ConnectionError,
                            InvalidURL)
 from bzrlib.osutils import getcwd
+from bzrlib.smart import medium
 from bzrlib.symbol_versioning import zero_eleven
 from bzrlib.tests import TestCaseInTempDir, TestSkipped
 from bzrlib.tests.test_transport import TestTransportImplementation
-from bzrlib.transport import memory
-from bzrlib.smart import medium
+from bzrlib.transport import memory, remote
 import bzrlib.transport
 
 

=== modified file 'bzrlib/trace.py'
--- a/bzrlib/trace.py	2007-01-30 10:51:38 +0000
+++ b/bzrlib/trace.py	2007-03-27 04:57:59 +0000
@@ -181,6 +181,8 @@
 
     The exception string representation is used as the error
     summary, unless msg is given.
+
+    Please see log_exception_quietly() for the replacement API.
     """
     if msg:
         error(msg)

=== modified file 'bzrlib/transport/http/__init__.py'
--- a/bzrlib/transport/http/__init__.py	2007-04-05 09:35:26 +0000
+++ b/bzrlib/transport/http/__init__.py	2007-04-10 15:54:15 +0000
@@ -27,6 +27,7 @@
 import sys
 
 from bzrlib import errors, ui
+from bzrlib.smart import medium
 from bzrlib.trace import mutter
 from bzrlib.transport import (
     Transport,




More information about the bazaar-commits mailing list