/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/request.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2011 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Infrastructure for server-side request handlers.
 
18
 
 
19
Interesting module attributes:
 
20
    * The request_handlers registry maps verb names to SmartServerRequest
 
21
      classes.
 
22
    * The jail_info threading.local() object is used to prevent accidental
 
23
      opening of BzrDirs outside of the backing transport, or any other
 
24
      transports placed in jail_info.transports.  The jail_info is reset on
 
25
      every call into a request handler (which can happen an arbitrary number
 
26
      of times during a request).
 
27
"""
 
28
 
 
29
from __future__ import absolute_import
 
30
 
 
31
# XXX: The class names are a little confusing: the protocol will instantiate a
 
32
# SmartServerRequestHandler, whose dispatch_command method creates an instance
 
33
# of a SmartServerRequest subclass.
 
34
 
 
35
 
 
36
import threading
 
37
 
 
38
from bzrlib import (
 
39
    bzrdir,
 
40
    debug,
 
41
    errors,
 
42
    osutils,
 
43
    registry,
 
44
    revision,
 
45
    trace,
 
46
    urlutils,
 
47
    )
 
48
from bzrlib.lazy_import import lazy_import
 
49
lazy_import(globals(), """
 
50
from bzrlib.bundle import serializer
 
51
 
 
52
import tempfile
 
53
import thread
 
54
""")
 
55
 
 
56
 
 
57
jail_info = threading.local()
 
58
jail_info.transports = None
 
59
 
 
60
 
 
61
def _install_hook():
 
62
    bzrdir.BzrDir.hooks.install_named_hook(
 
63
        'pre_open', _pre_open_hook, 'checking server jail')
 
64
 
 
65
 
 
66
def _pre_open_hook(transport):
 
67
    allowed_transports = getattr(jail_info, 'transports', None)
 
68
    if allowed_transports is None:
 
69
        return
 
70
    abspath = transport.base
 
71
    for allowed_transport in allowed_transports:
 
72
        try:
 
73
            allowed_transport.relpath(abspath)
 
74
        except errors.PathNotChild:
 
75
            continue
 
76
        else:
 
77
            return
 
78
    raise errors.JailBreak(abspath)
 
79
 
 
80
 
 
81
_install_hook()
 
82
 
 
83
 
 
84
class SmartServerRequest(object):
 
85
    """Base class for request handlers.
 
86
 
 
87
    To define a new request, subclass this class and override the `do` method
 
88
    (and if appropriate, `do_body` as well).  Request implementors should take
 
89
    care to call `translate_client_path` and `transport_from_client_path` as
 
90
    appropriate when dealing with paths received from the client.
 
91
    """
 
92
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
 
93
    # *handler* is a different concept to the request.
 
94
 
 
95
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
 
96
        """Constructor.
 
97
 
 
98
        :param backing_transport: the base transport to be used when performing
 
99
            this request.
 
100
        :param root_client_path: the client path that maps to the root of
 
101
            backing_transport.  This is used to interpret relpaths received
 
102
            from the client.  Clients will not be able to refer to paths above
 
103
            this root.  If root_client_path is None, then no translation will
 
104
            be performed on client paths.  Default is '/'.
 
105
        :param jail_root: if specified, the root of the BzrDir.open jail to use
 
106
            instead of backing_transport.
 
107
        """
 
108
        self._backing_transport = backing_transport
 
109
        if jail_root is None:
 
110
            jail_root = backing_transport
 
111
        self._jail_root = jail_root
 
112
        if root_client_path is not None:
 
113
            if not root_client_path.startswith('/'):
 
114
                root_client_path = '/' + root_client_path
 
115
            if not root_client_path.endswith('/'):
 
116
                root_client_path += '/'
 
117
        self._root_client_path = root_client_path
 
118
        self._body_chunks = []
 
119
 
 
120
    def _check_enabled(self):
 
121
        """Raises DisabledMethod if this method is disabled."""
 
122
        pass
 
123
 
 
124
    def do(self, *args):
 
125
        """Mandatory extension point for SmartServerRequest subclasses.
 
126
 
 
127
        Subclasses must implement this.
 
128
 
 
129
        This should return a SmartServerResponse if this command expects to
 
130
        receive no body.
 
131
        """
 
132
        raise NotImplementedError(self.do)
 
133
 
 
134
    def execute(self, *args):
 
135
        """Public entry point to execute this request.
 
136
 
 
137
        It will return a SmartServerResponse if the command does not expect a
 
138
        body.
 
139
 
 
140
        :param args: the arguments of the request.
 
141
        """
 
142
        self._check_enabled()
 
143
        return self.do(*args)
 
144
 
 
145
    def do_body(self, body_bytes):
 
146
        """Called if the client sends a body with the request.
 
147
 
 
148
        The do() method is still called, and must have returned None.
 
149
 
 
150
        Must return a SmartServerResponse.
 
151
        """
 
152
        if body_bytes != '':
 
153
            raise errors.SmartProtocolError('Request does not expect a body')
 
154
 
 
155
    def do_chunk(self, chunk_bytes):
 
156
        """Called with each body chunk if the request has a streamed body.
 
157
 
 
158
        The do() method is still called, and must have returned None.
 
159
        """
 
160
        self._body_chunks.append(chunk_bytes)
 
161
 
 
162
    def do_end(self):
 
163
        """Called when the end of the request has been received."""
 
164
        body_bytes = ''.join(self._body_chunks)
 
165
        self._body_chunks = None
 
166
        return self.do_body(body_bytes)
 
167
 
 
168
    def setup_jail(self):
 
169
        jail_info.transports = [self._jail_root]
 
170
 
 
171
    def teardown_jail(self):
 
172
        jail_info.transports = None
 
173
 
 
174
    def translate_client_path(self, client_path):
 
175
        """Translate a path received from a network client into a local
 
176
        relpath.
 
177
 
 
178
        All paths received from the client *must* be translated.
 
179
 
 
180
        :param client_path: the path from the client.
 
181
        :returns: a relpath that may be used with self._backing_transport
 
182
            (unlike the untranslated client_path, which must not be used with
 
183
            the backing transport).
 
184
        """
 
185
        if self._root_client_path is None:
 
186
            # no translation necessary!
 
187
            return client_path
 
188
        if not client_path.startswith('/'):
 
189
            client_path = '/' + client_path
 
190
        if client_path + '/' == self._root_client_path:
 
191
            return '.'
 
192
        if client_path.startswith(self._root_client_path):
 
193
            path = client_path[len(self._root_client_path):]
 
194
            relpath = urlutils.joinpath('/', path)
 
195
            if not relpath.startswith('/'):
 
196
                raise ValueError(relpath)
 
197
            return urlutils.escape('.' + relpath)
 
198
        else:
 
199
            raise errors.PathNotChild(client_path, self._root_client_path)
 
200
 
 
201
    def transport_from_client_path(self, client_path):
 
202
        """Get a backing transport corresponding to the location referred to by
 
203
        a network client.
 
204
 
 
205
        :seealso: translate_client_path
 
206
        :returns: a transport cloned from self._backing_transport
 
207
        """
 
208
        relpath = self.translate_client_path(client_path)
 
209
        return self._backing_transport.clone(relpath)
 
210
 
 
211
 
 
212
class SmartServerResponse(object):
 
213
    """A response to a client request.
 
214
 
 
215
    This base class should not be used. Instead use
 
216
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
 
217
    """
 
218
 
 
219
    def __init__(self, args, body=None, body_stream=None):
 
220
        """Constructor.
 
221
 
 
222
        :param args: tuple of response arguments.
 
223
        :param body: string of a response body.
 
224
        :param body_stream: iterable of bytestrings to be streamed to the
 
225
            client.
 
226
        """
 
227
        self.args = args
 
228
        if body is not None and body_stream is not None:
 
229
            raise errors.BzrError(
 
230
                "'body' and 'body_stream' are mutually exclusive.")
 
231
        self.body = body
 
232
        self.body_stream = body_stream
 
233
 
 
234
    def __eq__(self, other):
 
235
        if other is None:
 
236
            return False
 
237
        return (other.args == self.args and
 
238
                other.body == self.body and
 
239
                other.body_stream is self.body_stream)
 
240
 
 
241
    def __repr__(self):
 
242
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
 
243
            self.args, self.body)
 
244
 
 
245
 
 
246
class FailedSmartServerResponse(SmartServerResponse):
 
247
    """A SmartServerResponse for a request which failed."""
 
248
 
 
249
    def is_successful(self):
 
250
        """FailedSmartServerResponse are not successful."""
 
251
        return False
 
252
 
 
253
 
 
254
class SuccessfulSmartServerResponse(SmartServerResponse):
 
255
    """A SmartServerResponse for a successfully completed request."""
 
256
 
 
257
    def is_successful(self):
 
258
        """SuccessfulSmartServerResponse are successful."""
 
259
        return True
 
260
 
 
261
 
 
262
class SmartServerRequestHandler(object):
 
263
    """Protocol logic for smart server.
 
264
 
 
265
    This doesn't handle serialization at all, it just processes requests and
 
266
    creates responses.
 
267
    """
 
268
 
 
269
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
 
270
    # not contain encoding or decoding logic to allow the wire protocol to vary
 
271
    # from the object protocol: we will want to tweak the wire protocol separate
 
272
    # from the object model, and ideally we will be able to do that without
 
273
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
 
274
    # just a Protocol subclass.
 
275
 
 
276
    # TODO: Better way of representing the body for commands that take it,
 
277
    # and allow it to be streamed into the server.
 
278
 
 
279
    def __init__(self, backing_transport, commands, root_client_path,
 
280
        jail_root=None):
 
281
        """Constructor.
 
282
 
 
283
        :param backing_transport: a Transport to handle requests for.
 
284
        :param commands: a registry mapping command names to SmartServerRequest
 
285
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
 
286
        """
 
287
        self._backing_transport = backing_transport
 
288
        self._root_client_path = root_client_path
 
289
        self._commands = commands
 
290
        if jail_root is None:
 
291
            jail_root = backing_transport
 
292
        self._jail_root = jail_root
 
293
        self.response = None
 
294
        self.finished_reading = False
 
295
        self._command = None
 
296
        if 'hpss' in debug.debug_flags:
 
297
            self._request_start_time = osutils.timer_func()
 
298
            self._thread_id = thread.get_ident()
 
299
 
 
300
    def _trace(self, action, message, extra_bytes=None, include_time=False):
 
301
        # It is a bit of a shame that this functionality overlaps with that of 
 
302
        # ProtocolThreeRequester._trace. However, there is enough difference
 
303
        # that just putting it in a helper doesn't help a lot. And some state
 
304
        # is taken from the instance.
 
305
        if include_time:
 
306
            t = '%5.3fs ' % (osutils.timer_func() - self._request_start_time)
 
307
        else:
 
308
            t = ''
 
309
        if extra_bytes is None:
 
310
            extra = ''
 
311
        else:
 
312
            extra = ' ' + repr(extra_bytes[:40])
 
313
            if len(extra) > 33:
 
314
                extra = extra[:29] + extra[-1] + '...'
 
315
        trace.mutter('%12s: [%s] %s%s%s'
 
316
                     % (action, self._thread_id, t, message, extra))
 
317
 
 
318
    def accept_body(self, bytes):
 
319
        """Accept body data."""
 
320
        if self._command is None:
 
321
            # no active command object, so ignore the event.
 
322
            return
 
323
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
 
324
        if 'hpss' in debug.debug_flags:
 
325
            self._trace('accept body',
 
326
                        '%d bytes' % (len(bytes),), bytes)
 
327
 
 
328
    def end_of_body(self):
 
329
        """No more body data will be received."""
 
330
        self._run_handler_code(self._command.do_end, (), {})
 
331
        # cannot read after this.
 
332
        self.finished_reading = True
 
333
        if 'hpss' in debug.debug_flags:
 
334
            self._trace('end of body', '', include_time=True)
 
335
 
 
336
    def _run_handler_code(self, callable, args, kwargs):
 
337
        """Run some handler specific code 'callable'.
 
338
 
 
339
        If a result is returned, it is considered to be the commands response,
 
340
        and finished_reading is set true, and its assigned to self.response.
 
341
 
 
342
        Any exceptions caught are translated and a response object created
 
343
        from them.
 
344
        """
 
345
        result = self._call_converting_errors(callable, args, kwargs)
 
346
 
 
347
        if result is not None:
 
348
            self.response = result
 
349
            self.finished_reading = True
 
350
 
 
351
    def _call_converting_errors(self, callable, args, kwargs):
 
352
        """Call callable converting errors to Response objects."""
 
353
        # XXX: most of this error conversion is VFS-related, and thus ought to
 
354
        # be in SmartServerVFSRequestHandler somewhere.
 
355
        try:
 
356
            self._command.setup_jail()
 
357
            try:
 
358
                return callable(*args, **kwargs)
 
359
            finally:
 
360
                self._command.teardown_jail()
 
361
        except (KeyboardInterrupt, SystemExit):
 
362
            raise
 
363
        except Exception, err:
 
364
            err_struct = _translate_error(err)
 
365
            return FailedSmartServerResponse(err_struct)
 
366
 
 
367
    def headers_received(self, headers):
 
368
        # Just a no-op at the moment.
 
369
        if 'hpss' in debug.debug_flags:
 
370
            self._trace('headers', repr(headers))
 
371
 
 
372
    def args_received(self, args):
 
373
        cmd = args[0]
 
374
        args = args[1:]
 
375
        try:
 
376
            command = self._commands.get(cmd)
 
377
        except LookupError:
 
378
            if 'hpss' in debug.debug_flags:
 
379
                self._trace('hpss unknown request', 
 
380
                            cmd, repr(args)[1:-1])
 
381
            raise errors.UnknownSmartMethod(cmd)
 
382
        if 'hpss' in debug.debug_flags:
 
383
            from bzrlib.smart import vfs
 
384
            if issubclass(command, vfs.VfsRequest):
 
385
                action = 'hpss vfs req'
 
386
            else:
 
387
                action = 'hpss request'
 
388
            self._trace(action, 
 
389
                        '%s %s' % (cmd, repr(args)[1:-1]))
 
390
        self._command = command(
 
391
            self._backing_transport, self._root_client_path, self._jail_root)
 
392
        self._run_handler_code(self._command.execute, args, {})
 
393
 
 
394
    def end_received(self):
 
395
        if self._command is None:
 
396
            # no active command object, so ignore the event.
 
397
            return
 
398
        self._run_handler_code(self._command.do_end, (), {})
 
399
        if 'hpss' in debug.debug_flags:
 
400
            self._trace('end', '', include_time=True)
 
401
 
 
402
    def post_body_error_received(self, error_args):
 
403
        # Just a no-op at the moment.
 
404
        pass
 
405
 
 
406
 
 
407
def _translate_error(err):
 
408
    if isinstance(err, errors.NoSuchFile):
 
409
        return ('NoSuchFile', err.path)
 
410
    elif isinstance(err, errors.FileExists):
 
411
        return ('FileExists', err.path)
 
412
    elif isinstance(err, errors.DirectoryNotEmpty):
 
413
        return ('DirectoryNotEmpty', err.path)
 
414
    elif isinstance(err, errors.IncompatibleRepositories):
 
415
        return ('IncompatibleRepositories', str(err.source), str(err.target),
 
416
            str(err.details))
 
417
    elif isinstance(err, errors.ShortReadvError):
 
418
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
 
419
                str(err.actual))
 
420
    elif isinstance(err, errors.RevisionNotPresent):
 
421
        return ('RevisionNotPresent', err.revision_id, err.file_id)
 
422
    elif isinstance(err, errors.UnstackableRepositoryFormat):
 
423
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
 
424
    elif isinstance(err, errors.UnstackableBranchFormat):
 
425
        return ('UnstackableBranchFormat', str(err.format), err.url)
 
426
    elif isinstance(err, errors.NotStacked):
 
427
        return ('NotStacked',)
 
428
    elif isinstance(err, errors.BzrCheckError):
 
429
        return ('BzrCheckError', err.msg)
 
430
    elif isinstance(err, UnicodeError):
 
431
        # If it is a DecodeError, than most likely we are starting
 
432
        # with a plain string
 
433
        str_or_unicode = err.object
 
434
        if isinstance(str_or_unicode, unicode):
 
435
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
 
436
            # byte) in it, so this encoding could cause broken responses.
 
437
            # Newer clients use protocol v3, so will be fine.
 
438
            val = 'u:' + str_or_unicode.encode('utf-8')
 
439
        else:
 
440
            val = 's:' + str_or_unicode.encode('base64')
 
441
        # This handles UnicodeEncodeError or UnicodeDecodeError
 
442
        return (err.__class__.__name__, err.encoding, val, str(err.start),
 
443
                str(err.end), err.reason)
 
444
    elif isinstance(err, errors.TransportNotPossible):
 
445
        if err.msg == "readonly transport":
 
446
            return ('ReadOnlyError', )
 
447
    elif isinstance(err, errors.ReadError):
 
448
        # cannot read the file
 
449
        return ('ReadError', err.path)
 
450
    elif isinstance(err, errors.PermissionDenied):
 
451
        return ('PermissionDenied', err.path, err.extra)
 
452
    elif isinstance(err, errors.TokenMismatch):
 
453
        return ('TokenMismatch', err.given_token, err.lock_token)
 
454
    elif isinstance(err, errors.LockContention):
 
455
        return ('LockContention',)
 
456
    elif isinstance(err, MemoryError):
 
457
        # GZ 2011-02-24: Copy bzrlib.trace -Dmem_dump functionality here?
 
458
        return ('MemoryError',)
 
459
    # Unserialisable error.  Log it, and return a generic error
 
460
    trace.log_exception_quietly()
 
461
    return ('error', trace._qualified_exception_name(err.__class__, True),
 
462
        str(err))
 
463
 
 
464
 
 
465
class HelloRequest(SmartServerRequest):
 
466
    """Answer a version request with the highest protocol version this server
 
467
    supports.
 
468
    """
 
469
 
 
470
    def do(self):
 
471
        return SuccessfulSmartServerResponse(('ok', '2'))
 
472
 
 
473
 
 
474
class GetBundleRequest(SmartServerRequest):
 
475
    """Get a bundle of from the null revision to the specified revision."""
 
476
 
 
477
    def do(self, path, revision_id):
 
478
        # open transport relative to our base
 
479
        t = self.transport_from_client_path(path)
 
480
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
 
481
        repo = control.open_repository()
 
482
        tmpf = tempfile.TemporaryFile()
 
483
        base_revision = revision.NULL_REVISION
 
484
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
 
485
        tmpf.seek(0)
 
486
        return SuccessfulSmartServerResponse((), tmpf.read())
 
487
 
 
488
 
 
489
class SmartServerIsReadonly(SmartServerRequest):
 
490
    # XXX: this request method belongs somewhere else.
 
491
 
 
492
    def do(self):
 
493
        if self._backing_transport.is_readonly():
 
494
            answer = 'yes'
 
495
        else:
 
496
            answer = 'no'
 
497
        return SuccessfulSmartServerResponse((answer,))
 
498
 
 
499
 
 
500
# In the 'info' attribute, we store whether this request is 'safe' to retry if
 
501
# we get a disconnect while reading the response. It can have the values:
 
502
#   read    This is purely a read request, so retrying it is perfectly ok.
 
503
#   idem    An idempotent write request. Something like 'put' where if you put
 
504
#           the same bytes twice you end up with the same final bytes.
 
505
#   semi    This is a request that isn't strictly idempotent, but doesn't
 
506
#           result in corruption if it is retried. This is for things like
 
507
#           'lock' and 'unlock'. If you call lock, it updates the disk
 
508
#           structure. If you fail to read the response, you won't be able to
 
509
#           use the lock, because you don't have the lock token. Calling lock
 
510
#           again will fail, because the lock is already taken. However, we
 
511
#           can't tell if the server received our request or not. If it didn't,
 
512
#           then retrying the request is fine, as it will actually do what we
 
513
#           want. If it did, we will interrupt the current operation, but we
 
514
#           are no worse off than interrupting the current operation because of
 
515
#           a ConnectionReset.
 
516
#   semivfs Similar to semi, but specific to a Virtual FileSystem request.
 
517
#   stream  This is a request that takes a stream that cannot be restarted if
 
518
#           consumed. This request is 'safe' in that if we determine the
 
519
#           connection is closed before we consume the stream, we can try
 
520
#           again.
 
521
#   mutate  State is updated in a way that replaying that request results in a
 
522
#           different state. For example 'append' writes more bytes to a given
 
523
#           file. If append succeeds, it moves the file pointer.
 
524
request_handlers = registry.Registry()
 
525
request_handlers.register_lazy(
 
526
    'append', 'bzrlib.smart.vfs', 'AppendRequest', info='mutate')
 
527
request_handlers.register_lazy(
 
528
    'Branch.break_lock', 'bzrlib.smart.branch',
 
529
    'SmartServerBranchBreakLock', info='idem')
 
530
request_handlers.register_lazy(
 
531
    'Branch.get_config_file', 'bzrlib.smart.branch',
 
532
    'SmartServerBranchGetConfigFile', info='read')
 
533
request_handlers.register_lazy(
 
534
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent',
 
535
    info='read')
 
536
request_handlers.register_lazy(
 
537
    'Branch.put_config_file', 'bzrlib.smart.branch',
 
538
    'SmartServerBranchPutConfigFile', info='idem')
 
539
request_handlers.register_lazy(
 
540
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
 
541
    'SmartServerBranchGetTagsBytes', info='read')
 
542
request_handlers.register_lazy(
 
543
    'Branch.set_tags_bytes', 'bzrlib.smart.branch',
 
544
    'SmartServerBranchSetTagsBytes', info='idem')
 
545
request_handlers.register_lazy(
 
546
    'Branch.heads_to_fetch', 'bzrlib.smart.branch',
 
547
    'SmartServerBranchHeadsToFetch', info='read')
 
548
request_handlers.register_lazy(
 
549
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch',
 
550
    'SmartServerBranchRequestGetStackedOnURL', info='read')
 
551
request_handlers.register_lazy(
 
552
    'Branch.get_physical_lock_status', 'bzrlib.smart.branch',
 
553
    'SmartServerBranchRequestGetPhysicalLockStatus', info='read')
 
554
request_handlers.register_lazy(
 
555
    'Branch.last_revision_info', 'bzrlib.smart.branch',
 
556
    'SmartServerBranchRequestLastRevisionInfo', info='read')
 
557
request_handlers.register_lazy(
 
558
    'Branch.lock_write', 'bzrlib.smart.branch',
 
559
    'SmartServerBranchRequestLockWrite', info='semi')
 
560
request_handlers.register_lazy(
 
561
    'Branch.revision_history', 'bzrlib.smart.branch',
 
562
    'SmartServerRequestRevisionHistory', info='read')
 
563
request_handlers.register_lazy(
 
564
    'Branch.set_config_option', 'bzrlib.smart.branch',
 
565
    'SmartServerBranchRequestSetConfigOption', info='idem')
 
566
request_handlers.register_lazy(
 
567
    'Branch.set_config_option_dict', 'bzrlib.smart.branch',
 
568
    'SmartServerBranchRequestSetConfigOptionDict', info='idem')
 
569
request_handlers.register_lazy(
 
570
    'Branch.set_last_revision', 'bzrlib.smart.branch',
 
571
    'SmartServerBranchRequestSetLastRevision', info='idem')
 
572
request_handlers.register_lazy(
 
573
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
 
574
    'SmartServerBranchRequestSetLastRevisionInfo', info='idem')
 
575
request_handlers.register_lazy(
 
576
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
 
577
    'SmartServerBranchRequestSetLastRevisionEx', info='idem')
 
578
request_handlers.register_lazy(
 
579
    'Branch.set_parent_location', 'bzrlib.smart.branch',
 
580
    'SmartServerBranchRequestSetParentLocation', info='idem')
 
581
request_handlers.register_lazy(
 
582
    'Branch.unlock', 'bzrlib.smart.branch',
 
583
    'SmartServerBranchRequestUnlock', info='semi')
 
584
request_handlers.register_lazy(
 
585
    'Branch.revision_id_to_revno', 'bzrlib.smart.branch',
 
586
    'SmartServerBranchRequestRevisionIdToRevno', info='read')
 
587
request_handlers.register_lazy(
 
588
    'BzrDir.checkout_metadir', 'bzrlib.smart.bzrdir',
 
589
    'SmartServerBzrDirRequestCheckoutMetaDir', info='read')
 
590
request_handlers.register_lazy(
 
591
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
 
592
    'SmartServerBzrDirRequestCloningMetaDir', info='read')
 
593
request_handlers.register_lazy(
 
594
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
 
595
    'SmartServerRequestCreateBranch', info='semi')
 
596
request_handlers.register_lazy(
 
597
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
 
598
    'SmartServerRequestCreateRepository', info='semi')
 
599
request_handlers.register_lazy(
 
600
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
 
601
    'SmartServerRequestFindRepositoryV1', info='read')
 
602
request_handlers.register_lazy(
 
603
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
 
604
    'SmartServerRequestFindRepositoryV2', info='read')
 
605
request_handlers.register_lazy(
 
606
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
 
607
    'SmartServerRequestFindRepositoryV3', info='read')
 
608
request_handlers.register_lazy(
 
609
    'BzrDir.get_config_file', 'bzrlib.smart.bzrdir',
 
610
    'SmartServerBzrDirRequestConfigFile', info='read')
 
611
request_handlers.register_lazy(
 
612
    'BzrDir.destroy_branch', 'bzrlib.smart.bzrdir',
 
613
    'SmartServerBzrDirRequestDestroyBranch', info='semi')
 
614
request_handlers.register_lazy(
 
615
    'BzrDir.destroy_repository', 'bzrlib.smart.bzrdir',
 
616
    'SmartServerBzrDirRequestDestroyRepository', info='semi')
 
617
request_handlers.register_lazy(
 
618
    'BzrDir.has_workingtree', 'bzrlib.smart.bzrdir',
 
619
    'SmartServerBzrDirRequestHasWorkingTree', info='read')
 
620
request_handlers.register_lazy(
 
621
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
 
622
    'SmartServerRequestInitializeBzrDir', info='semi')
 
623
request_handlers.register_lazy(
 
624
    'BzrDirFormat.initialize_ex_1.16', 'bzrlib.smart.bzrdir',
 
625
    'SmartServerRequestBzrDirInitializeEx', info='semi')
 
626
request_handlers.register_lazy(
 
627
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir',
 
628
    info='read')
 
629
request_handlers.register_lazy(
 
630
    'BzrDir.open_2.1', 'bzrlib.smart.bzrdir',
 
631
    'SmartServerRequestOpenBzrDir_2_1', info='read')
 
632
request_handlers.register_lazy(
 
633
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
 
634
    'SmartServerRequestOpenBranch', info='read')
 
635
request_handlers.register_lazy(
 
636
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
 
637
    'SmartServerRequestOpenBranchV2', info='read')
 
638
request_handlers.register_lazy(
 
639
    'BzrDir.open_branchV3', 'bzrlib.smart.bzrdir',
 
640
    'SmartServerRequestOpenBranchV3', info='read')
 
641
request_handlers.register_lazy(
 
642
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest', info='semivfs')
 
643
request_handlers.register_lazy(
 
644
    'get', 'bzrlib.smart.vfs', 'GetRequest', info='read')
 
645
request_handlers.register_lazy(
 
646
    'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest', info='read')
 
647
request_handlers.register_lazy(
 
648
    'has', 'bzrlib.smart.vfs', 'HasRequest', info='read')
 
649
request_handlers.register_lazy(
 
650
    'hello', 'bzrlib.smart.request', 'HelloRequest', info='read')
 
651
request_handlers.register_lazy(
 
652
    'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest',
 
653
    info='read')
 
654
request_handlers.register_lazy(
 
655
    'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest', info='read')
 
656
request_handlers.register_lazy(
 
657
    'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest', info='semivfs')
 
658
request_handlers.register_lazy(
 
659
    'move', 'bzrlib.smart.vfs', 'MoveRequest', info='semivfs')
 
660
request_handlers.register_lazy(
 
661
    'put', 'bzrlib.smart.vfs', 'PutRequest', info='idem')
 
662
request_handlers.register_lazy(
 
663
    'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest', info='idem')
 
664
request_handlers.register_lazy(
 
665
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest', info='read')
 
666
request_handlers.register_lazy(
 
667
    'rename', 'bzrlib.smart.vfs', 'RenameRequest', info='semivfs')
 
668
request_handlers.register_lazy(
 
669
    'Repository.add_signature_text', 'bzrlib.smart.repository',
 
670
    'SmartServerRepositoryAddSignatureText', info='idem')
 
671
request_handlers.register_lazy(
 
672
    'Repository.all_revision_ids', 'bzrlib.smart.repository',
 
673
    'SmartServerRepositoryAllRevisionIds', info='read')
 
674
request_handlers.register_lazy(
 
675
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
 
676
    'SmartServerPackRepositoryAutopack', info='idem')
 
677
request_handlers.register_lazy(
 
678
    'Repository.break_lock', 'bzrlib.smart.repository',
 
679
    'SmartServerRepositoryBreakLock', info='idem')
 
680
request_handlers.register_lazy(
 
681
    'Repository.gather_stats', 'bzrlib.smart.repository',
 
682
    'SmartServerRepositoryGatherStats', info='read')
 
683
request_handlers.register_lazy(
 
684
    'Repository.get_parent_map', 'bzrlib.smart.repository',
 
685
    'SmartServerRepositoryGetParentMap', info='read')
 
686
request_handlers.register_lazy(
 
687
    'Repository.get_revision_graph', 'bzrlib.smart.repository',
 
688
    'SmartServerRepositoryGetRevisionGraph', info='read')
 
689
request_handlers.register_lazy(
 
690
    'Repository.get_revision_signature_text', 'bzrlib.smart.repository',
 
691
    'SmartServerRepositoryGetRevisionSignatureText', info='read')
 
692
request_handlers.register_lazy(
 
693
    'Repository.has_revision', 'bzrlib.smart.repository',
 
694
    'SmartServerRequestHasRevision', info='read')
 
695
request_handlers.register_lazy(
 
696
    'Repository.has_signature_for_revision_id', 'bzrlib.smart.repository',
 
697
    'SmartServerRequestHasSignatureForRevisionId', info='read')
 
698
request_handlers.register_lazy(
 
699
    'Repository.insert_stream', 'bzrlib.smart.repository',
 
700
    'SmartServerRepositoryInsertStream', info='stream')
 
701
request_handlers.register_lazy(
 
702
    'Repository.insert_stream_1.19', 'bzrlib.smart.repository',
 
703
    'SmartServerRepositoryInsertStream_1_19', info='stream')
 
704
request_handlers.register_lazy(
 
705
    'Repository.insert_stream_locked', 'bzrlib.smart.repository',
 
706
    'SmartServerRepositoryInsertStreamLocked', info='stream')
 
707
request_handlers.register_lazy(
 
708
    'Repository.is_shared', 'bzrlib.smart.repository',
 
709
    'SmartServerRepositoryIsShared', info='read')
 
710
request_handlers.register_lazy(
 
711
    'Repository.iter_files_bytes', 'bzrlib.smart.repository',
 
712
    'SmartServerRepositoryIterFilesBytes', info='read')
 
713
request_handlers.register_lazy(
 
714
    'Repository.lock_write', 'bzrlib.smart.repository',
 
715
    'SmartServerRepositoryLockWrite', info='semi')
 
716
request_handlers.register_lazy(
 
717
    'Repository.make_working_trees', 'bzrlib.smart.repository',
 
718
    'SmartServerRepositoryMakeWorkingTrees', info='read')
 
719
request_handlers.register_lazy(
 
720
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
721
    'SmartServerRepositorySetMakeWorkingTrees', info='idem')
 
722
request_handlers.register_lazy(
 
723
    'Repository.unlock', 'bzrlib.smart.repository',
 
724
    'SmartServerRepositoryUnlock', info='semi')
 
725
request_handlers.register_lazy(
 
726
    'Repository.get_physical_lock_status', 'bzrlib.smart.repository',
 
727
    'SmartServerRepositoryGetPhysicalLockStatus', info='read')
 
728
request_handlers.register_lazy(
 
729
    'Repository.get_rev_id_for_revno', 'bzrlib.smart.repository',
 
730
    'SmartServerRepositoryGetRevIdForRevno', info='read')
 
731
request_handlers.register_lazy(
 
732
    'Repository.get_stream', 'bzrlib.smart.repository',
 
733
    'SmartServerRepositoryGetStream', info='read')
 
734
request_handlers.register_lazy(
 
735
    'Repository.get_stream_1.19', 'bzrlib.smart.repository',
 
736
    'SmartServerRepositoryGetStream_1_19', info='read')
 
737
request_handlers.register_lazy(
 
738
    'Repository.iter_revisions', 'bzrlib.smart.repository',
 
739
    'SmartServerRepositoryIterRevisions', info='read')
 
740
request_handlers.register_lazy(
 
741
    'Repository.pack', 'bzrlib.smart.repository',
 
742
    'SmartServerRepositoryPack', info='idem')
 
743
request_handlers.register_lazy(
 
744
    'Repository.start_write_group', 'bzrlib.smart.repository',
 
745
    'SmartServerRepositoryStartWriteGroup', info='semi')
 
746
request_handlers.register_lazy(
 
747
    'Repository.commit_write_group', 'bzrlib.smart.repository',
 
748
    'SmartServerRepositoryCommitWriteGroup', info='semi')
 
749
request_handlers.register_lazy(
 
750
    'Repository.abort_write_group', 'bzrlib.smart.repository',
 
751
    'SmartServerRepositoryAbortWriteGroup', info='semi')
 
752
request_handlers.register_lazy(
 
753
    'Repository.check_write_group', 'bzrlib.smart.repository',
 
754
    'SmartServerRepositoryCheckWriteGroup', info='read')
 
755
request_handlers.register_lazy(
 
756
    'Repository.reconcile', 'bzrlib.smart.repository',
 
757
    'SmartServerRepositoryReconcile', info='idem')
 
758
request_handlers.register_lazy(
 
759
    'Repository.tarball', 'bzrlib.smart.repository',
 
760
    'SmartServerRepositoryTarball', info='read')
 
761
request_handlers.register_lazy(
 
762
    'VersionedFileRepository.get_serializer_format', 'bzrlib.smart.repository',
 
763
    'SmartServerRepositoryGetSerializerFormat', info='read')
 
764
request_handlers.register_lazy(
 
765
    'VersionedFileRepository.get_inventories', 'bzrlib.smart.repository',
 
766
    'SmartServerRepositoryGetInventories', info='read')
 
767
request_handlers.register_lazy(
 
768
    'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest', info='semivfs')
 
769
request_handlers.register_lazy(
 
770
    'stat', 'bzrlib.smart.vfs', 'StatRequest', info='read')
 
771
request_handlers.register_lazy(
 
772
    'Transport.is_readonly', 'bzrlib.smart.request',
 
773
    'SmartServerIsReadonly', info='read')