~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/client.py

  • Committer: Andrew Bennetts
  • Date: 2007-03-26 06:24:01 UTC
  • mto: This revision was merged to the branch mainline in revision 2376.
  • Revision ID: andrew.bennetts@canonical.com-20070326062401-k3nbefzje5332jaf
Deal with review comments from Robert:

  * Add my name to the NEWS file
  * Move the test case to a new module in branch_implementations
  * Remove revision_history cruft from identitymap and test_identitymap
  * Improve some docstrings

Also, this fixes a bug where revision_history was not returning a copy of the
cached data, allowing the cache to be corrupted.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2008 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
import bzrlib
18
 
from bzrlib.smart import message, protocol
19
 
from bzrlib.trace import warning
20
 
from bzrlib import errors
21
 
 
22
 
 
23
 
class _SmartClient(object):
24
 
 
25
 
    def __init__(self, medium, headers=None):
26
 
        """Constructor.
27
 
 
28
 
        :param medium: a SmartClientMedium
29
 
        """
30
 
        self._medium = medium
31
 
        if headers is None:
32
 
            self._headers = {'Software version': bzrlib.__version__}
33
 
        else:
34
 
            self._headers = dict(headers)
35
 
 
36
 
    def _send_request(self, protocol_version, method, args, body=None,
37
 
                      readv_body=None):
38
 
        encoder, response_handler = self._construct_protocol(
39
 
            protocol_version)
40
 
        encoder.set_headers(self._headers)
41
 
        if body is not None:
42
 
            if readv_body is not None:
43
 
                raise AssertionError(
44
 
                    "body and readv_body are mutually exclusive.")
45
 
            encoder.call_with_body_bytes((method, ) + args, body)
46
 
        elif readv_body is not None:
47
 
            encoder.call_with_body_readv_array((method, ) + args,
48
 
                    readv_body)
49
 
        else:
50
 
            encoder.call(method, *args)
51
 
        return response_handler
52
 
 
53
 
    def _call_and_read_response(self, method, args, body=None, readv_body=None,
54
 
            expect_response_body=True):
55
 
        if self._medium._protocol_version is not None:
56
 
            response_handler = self._send_request(
57
 
                self._medium._protocol_version, method, args, body=body,
58
 
                readv_body=readv_body)
59
 
            return (response_handler.read_response_tuple(
60
 
                        expect_body=expect_response_body),
61
 
                    response_handler)
62
 
        else:
63
 
            for protocol_version in [3, 2]:
64
 
                if protocol_version == 2:
65
 
                    # If v3 doesn't work, the remote side is older than 1.6.
66
 
                    self._medium._remember_remote_is_before((1, 6))
67
 
                response_handler = self._send_request(
68
 
                    protocol_version, method, args, body=body,
69
 
                    readv_body=readv_body)
70
 
                try:
71
 
                    response_tuple = response_handler.read_response_tuple(
72
 
                        expect_body=expect_response_body)
73
 
                except errors.UnexpectedProtocolVersionMarker, err:
74
 
                    # TODO: We could recover from this without disconnecting if
75
 
                    # we recognise the protocol version.
76
 
                    warning(
77
 
                        'Server does not understand Bazaar network protocol %d,'
78
 
                        ' reconnecting.  (Upgrade the server to avoid this.)'
79
 
                        % (protocol_version,))
80
 
                    self._medium.disconnect()
81
 
                    continue
82
 
                except errors.ErrorFromSmartServer:
83
 
                    # If we received an error reply from the server, then it
84
 
                    # must be ok with this protocol version.
85
 
                    self._medium._protocol_version = protocol_version
86
 
                    raise
87
 
                else:
88
 
                    self._medium._protocol_version = protocol_version
89
 
                    return response_tuple, response_handler
90
 
            raise errors.SmartProtocolError(
91
 
                'Server is not a Bazaar server: ' + str(err))
92
 
 
93
 
    def _construct_protocol(self, version):
94
 
        request = self._medium.get_request()
95
 
        if version == 3:
96
 
            request_encoder = protocol.ProtocolThreeRequester(request)
97
 
            response_handler = message.ConventionalResponseHandler()
98
 
            response_proto = protocol.ProtocolThreeDecoder(
99
 
                response_handler, expect_version_marker=True)
100
 
            response_handler.setProtoAndMediumRequest(response_proto, request)
101
 
        elif version == 2:
102
 
            request_encoder = protocol.SmartClientRequestProtocolTwo(request)
103
 
            response_handler = request_encoder
104
 
        else:
105
 
            request_encoder = protocol.SmartClientRequestProtocolOne(request)
106
 
            response_handler = request_encoder
107
 
        return request_encoder, response_handler
108
 
 
109
 
    def call(self, method, *args):
110
 
        """Call a method on the remote server."""
111
 
        result, protocol = self.call_expecting_body(method, *args)
112
 
        protocol.cancel_read_body()
113
 
        return result
114
 
 
115
 
    def call_expecting_body(self, method, *args):
116
 
        """Call a method and return the result and the protocol object.
117
 
        
118
 
        The body can be read like so::
119
 
 
120
 
            result, smart_protocol = smart_client.call_expecting_body(...)
121
 
            body = smart_protocol.read_body_bytes()
122
 
        """
123
 
        return self._call_and_read_response(
124
 
            method, args, expect_response_body=True)
125
 
 
126
 
    def call_with_body_bytes(self, method, args, body):
127
 
        """Call a method on the remote server with body bytes."""
128
 
        if type(method) is not str:
129
 
            raise TypeError('method must be a byte string, not %r' % (method,))
130
 
        for arg in args:
131
 
            if type(arg) is not str:
132
 
                raise TypeError('args must be byte strings, not %r' % (args,))
133
 
        if type(body) is not str:
134
 
            raise TypeError('body must be byte string, not %r' % (body,))
135
 
        response, response_handler = self._call_and_read_response(
136
 
            method, args, body=body, expect_response_body=False)
137
 
        return response
138
 
 
139
 
    def call_with_body_bytes_expecting_body(self, method, args, body):
140
 
        """Call a method on the remote server with body bytes."""
141
 
        if type(method) is not str:
142
 
            raise TypeError('method must be a byte string, not %r' % (method,))
143
 
        for arg in args:
144
 
            if type(arg) is not str:
145
 
                raise TypeError('args must be byte strings, not %r' % (args,))
146
 
        if type(body) is not str:
147
 
            raise TypeError('body must be byte string, not %r' % (body,))
148
 
        response, response_handler = self._call_and_read_response(
149
 
            method, args, body=body, expect_response_body=True)
150
 
        return (response, response_handler)
151
 
 
152
 
    def call_with_body_readv_array(self, args, body):
153
 
        response, response_handler = self._call_and_read_response(
154
 
                args[0], args[1:], readv_body=body, expect_response_body=True)
155
 
        return (response, response_handler)
156
 
 
157
 
    def remote_path_from_transport(self, transport):
158
 
        """Convert transport into a path suitable for using in a request.
159
 
        
160
 
        Note that the resulting remote path doesn't encode the host name or
161
 
        anything but path, so it is only safe to use it in requests sent over
162
 
        the medium from the matching transport.
163
 
        """
164
 
        return self._medium.remote_path_from_transport(transport)
165