/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/smart/server.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
import errno
20
20
import os.path
 
21
import select
21
22
import socket
22
23
import sys
23
24
import threading
51
52
    hooks: An instance of SmartServerHooks.
52
53
    """
53
54
 
54
 
    def __init__(self, backing_transport, host='127.0.0.1', port=0,
55
 
                 root_client_path='/'):
 
55
    def __init__(self, backing_transport, root_client_path='/'):
56
56
        """Construct a new server.
57
57
 
58
58
        To actually start it running, call either start_background_thread or
59
59
        serve.
60
60
 
61
61
        :param backing_transport: The transport to serve.
 
62
        :param root_client_path: The client path that will correspond to root
 
63
            of backing_transport.
 
64
        """
 
65
        self.backing_transport = backing_transport
 
66
        self.root_client_path = root_client_path
 
67
 
 
68
    def start_server(self, host, port):
 
69
        """Create the server listening socket.
 
70
 
62
71
        :param host: Name of the interface to listen on.
63
72
        :param port: TCP port to listen on, or 0 to allocate a transient port.
64
 
        :param root_client_path: The client path that will correspond to root
65
 
            of backing_transport.
66
73
        """
67
74
        # let connections timeout so that we get a chance to terminate
68
75
        # Keep a reference to the exceptions we want to catch because the socket
89
96
        self.port = self._sockname[1]
90
97
        self._server_socket.listen(1)
91
98
        self._server_socket.settimeout(1)
92
 
        self.backing_transport = backing_transport
93
99
        self._started = threading.Event()
94
100
        self._stopped = threading.Event()
95
 
        self.root_client_path = root_client_path
96
101
 
97
 
    def serve(self, thread_name_suffix=''):
98
 
        self._should_terminate = False
99
 
        # for hooks we are letting code know that a server has started (and
100
 
        # later stopped).
 
102
    def _backing_urls(self):
101
103
        # There are three interesting urls:
102
104
        # The URL the server can be contacted on. (e.g. bzr://host/)
103
105
        # The URL that a commit done on the same machine as the server will
113
115
        # The latter two urls are different aliases to the servers url,
114
116
        # so we group those in a list - as there might be more aliases
115
117
        # in the future.
116
 
        backing_urls = [self.backing_transport.base]
 
118
        urls = [self.backing_transport.base]
117
119
        try:
118
 
            backing_urls.append(self.backing_transport.external_url())
 
120
            urls.append(self.backing_transport.external_url())
119
121
        except errors.InProcessTransport:
120
122
            pass
 
123
        return urls
 
124
 
 
125
    def run_server_started_hooks(self, backing_urls=None):
 
126
        if backing_urls is None:
 
127
            backing_urls = self._backing_urls()
121
128
        for hook in SmartTCPServer.hooks['server_started']:
122
129
            hook(backing_urls, self.get_url())
123
130
        for hook in SmartTCPServer.hooks['server_started_ex']:
124
131
            hook(backing_urls, self)
 
132
 
 
133
    def run_server_stopped_hooks(self, backing_urls=None):
 
134
        if backing_urls is None:
 
135
            backing_urls = self._backing_urls()
 
136
        for hook in SmartTCPServer.hooks['server_stopped']:
 
137
            hook(backing_urls, self.get_url())
 
138
 
 
139
    def serve(self, thread_name_suffix=''):
 
140
        self._should_terminate = False
 
141
        # for hooks we are letting code know that a server has started (and
 
142
        # later stopped).
 
143
        self.run_server_started_hooks()
125
144
        self._started.set()
126
145
        try:
127
146
            try:
155
174
            except self._socket_error:
156
175
                # ignore errors on close
157
176
                pass
158
 
            for hook in SmartTCPServer.hooks['server_stopped']:
159
 
                hook(backing_urls, self.get_url())
 
177
            self.run_server_stopped_hooks()
160
178
 
161
179
    def get_url(self):
162
180
        """Return the url of the server"""
172
190
        thread_name = 'smart-server-child' + thread_name_suffix
173
191
        connection_thread = threading.Thread(
174
192
            None, handler.serve, name=thread_name)
 
193
        # FIXME: This thread is never joined, it should at least be collected
 
194
        # somewhere so that tests that want to check for leaked threads can get
 
195
        # rid of them -- vila 20100531
175
196
        connection_thread.setDaemon(True)
176
197
        connection_thread.start()
 
198
        return connection_thread
177
199
 
178
200
    def start_background_thread(self, thread_name_suffix=''):
179
201
        self._started.clear()
324
346
                host = medium.BZR_DEFAULT_INTERFACE
325
347
            if port is None:
326
348
                port = medium.BZR_DEFAULT_PORT
327
 
            smart_server = SmartTCPServer(self.transport, host=host, port=port)
 
349
            smart_server = SmartTCPServer(self.transport)
 
350
            smart_server.start_server(host, port)
328
351
            trace.note('listening on port: %s' % smart_server.port)
329
352
        self.smart_server = smart_server
330
353