~bzr-pqm/bzr/bzr.dev

4988.10.3 by John Arbash Meinel
Merge bzr.dev 5007, resolve conflict, update NEWS
1
# Copyright (C) 2006-2010 Canonical Ltd
0.4.4 by Martin Pool
Start forming xmlrpc requests
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
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
0.4.4 by Martin Pool
Start forming xmlrpc requests
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
0.4.4 by Martin Pool
Start forming xmlrpc requests
16
17
0.4.17 by Martin Pool
Allow xmlrpc service url to be overridden by $BZR_LP_XMLRPC_URL
18
import os
4505.4.1 by Jonathan Lange
Trap gaierror and reraise appropriate ConnectionError.
19
import socket
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
20
from urlparse import urlsplit, urlunsplit
0.4.29 by Martin Pool
(register-branch) override xmlrpc user-agent; move Transport construction
21
import urllib
0.4.4 by Martin Pool
Start forming xmlrpc requests
22
import xmlrpclib
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
23
2900.2.21 by Vincent Ladeuil
Make lp_registration aware of authentication config.
24
from bzrlib import (
25
    config,
26
    errors,
4505.6.3 by Jonathan Lange
Don't bother lazy importing.
27
    urlutils,
2900.2.22 by Vincent Ladeuil
Polishing.
28
    __version__ as _bzrlib_version,
2900.2.21 by Vincent Ladeuil
Make lp_registration aware of authentication config.
29
    )
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
30
from bzrlib.transport.http import _urllib2_wrappers
1668.1.9 by Martin Pool
(launchpad plugin) Better reporting of errors from xmlrpc
31
4505.6.5 by Jonathan Lange
Factor out some code that guesses a branch's URL.
32
1668.1.9 by Martin Pool
(launchpad plugin) Better reporting of errors from xmlrpc
33
# for testing, do
34
'''
35
export BZR_LP_XMLRPC_URL=http://xmlrpc.staging.launchpad.net/bazaar/
36
'''
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
37
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
38
class InvalidLaunchpadInstance(errors.BzrError):
39
40
    _fmt = "%(lp_instance)s is not a valid Launchpad instance."
41
42
    def __init__(self, lp_instance):
43
        errors.BzrError.__init__(self, lp_instance=lp_instance)
44
45
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
46
class NotLaunchpadBranch(errors.BzrError):
47
4031.2.8 by Jonathan Lange
Say "registered on", not "hosted on".
48
    _fmt = "%(url)s is not registered on Launchpad."
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
49
50
    def __init__(self, url):
51
        errors.BzrError.__init__(self, url=url)
52
53
4776.2.6 by Vincent Ladeuil
Fixed as per review comments.
54
class XMLRPCTransport(xmlrpclib.Transport):
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
55
4776.3.1 by Vincent Ladeuil
Fix python2.4 compatibility with xmlrpclib.
56
    def __init__(self, scheme):
4776.3.2 by John Arbash Meinel
Clarify the comment as to why we are checking __init__
57
        # In python2.4 xmlrpclib.Transport is a old-style class, and does not
58
        # define __init__, so we check first
4776.3.1 by Vincent Ladeuil
Fix python2.4 compatibility with xmlrpclib.
59
        init = getattr(xmlrpclib.Transport, '__init__', None)
60
        if init is not None:
61
            init(self)
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
62
        self._scheme = scheme
63
        self._opener = _urllib2_wrappers.Opener()
64
        self.verbose = 0
65
66
    def request(self, host, handler, request_body, verbose=0):
67
        self.verbose = verbose
68
        url = self._scheme + "://" + host + handler
69
        request = _urllib2_wrappers.Request("POST", url, request_body)
4776.2.4 by Vincent Ladeuil
Note that User-Agent is lost (we use the bzrlib one though).
70
        # FIXME: _urllib2_wrappers will override user-agent with its own
71
        # request.add_header("User-Agent", self.user_agent)
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
72
        request.add_header("Content-Type", "text/xml")
73
74
        response = self._opener.open(request)
75
        if response.code != 200:
76
            raise xmlrpclib.ProtocolError(host + handler, response.code,
77
                                          response.msg, response.info())
78
        return self.parse_response(response)
79
80
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
81
class LaunchpadService(object):
0.4.27 by Martin Pool
doc
82
    """A service to talk to Launchpad via XMLRPC.
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
83
4988.4.2 by Martin Pool
Change url to canonical.com or wiki, plus some doc improvements in passing
84
    See http://wiki.bazaar.canonical.com/Specs/LaunchpadRpc for the methods we can call.
0.4.27 by Martin Pool
doc
85
    """
0.4.6 by Martin Pool
Put the rest of the parameters into the registration request.
86
3955.3.1 by Jonathan Lange
Start doing URL stuff, extracting the domain bit out of LaunchpadService,
87
    LAUNCHPAD_DOMAINS = {
88
        'production': 'launchpad.net',
89
        'edge': 'edge.launchpad.net',
90
        'staging': 'staging.launchpad.net',
91
        'demo': 'demo.launchpad.net',
92
        'dev': 'launchpad.dev',
93
        }
94
3211.1.1 by Ian Clatworthy
Extends the launchpad plugin's implementation of lp spec urls (Tim Penhey)
95
    # NB: these should always end in a slash to avoid xmlrpclib appending
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
96
    # '/RPC2'
3955.3.2 by Jonathan Lange
Tighten up the code a little, changing the dev service to use https,
97
    LAUNCHPAD_INSTANCE = {}
98
    for instance, domain in LAUNCHPAD_DOMAINS.iteritems():
99
        LAUNCHPAD_INSTANCE[instance] = 'https://xmlrpc.%s/bazaar/' % domain
100
5243.1.1 by Martin
Use the production server in the launchpad plugin rather than edge
101
    # Previously 'edge' was used to avoid a launchpad bug with redirection
102
    DEFAULT_INSTANCE = 'production'
3955.3.1 by Jonathan Lange
Start doing URL stuff, extracting the domain bit out of LaunchpadService,
103
    DEFAULT_SERVICE_URL = LAUNCHPAD_INSTANCE[DEFAULT_INSTANCE]
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
104
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
105
    transport = None
106
    registrant_email = None
107
    registrant_password = None
108
0.4.29 by Martin Pool
(register-branch) override xmlrpc user-agent; move Transport construction
109
3193.5.1 by Tim Penhey
Mostly working, just need to update the tests for lp://dev
110
    def __init__(self, transport=None, lp_instance=None):
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
111
        """Construct a new service talking to the launchpad rpc server"""
3193.5.1 by Tim Penhey
Mostly working, just need to update the tests for lp://dev
112
        self._lp_instance = lp_instance
0.4.29 by Martin Pool
(register-branch) override xmlrpc user-agent; move Transport construction
113
        if transport is None:
114
            uri_type = urllib.splittype(self.service_url)[0]
4776.2.6 by Vincent Ladeuil
Fixed as per review comments.
115
            transport = XMLRPCTransport(uri_type)
0.4.29 by Martin Pool
(register-branch) override xmlrpc user-agent; move Transport construction
116
            transport.user_agent = 'bzr/%s (xmlrpclib/%s)' \
2900.2.22 by Vincent Ladeuil
Polishing.
117
                    % (_bzrlib_version, xmlrpclib.__version__)
0.4.29 by Martin Pool
(register-branch) override xmlrpc user-agent; move Transport construction
118
        self.transport = transport
119
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
120
    @property
121
    def service_url(self):
122
        """Return the http or https url for the xmlrpc server.
123
124
        This does not include the username/password credentials.
125
        """
126
        key = 'BZR_LP_XMLRPC_URL'
127
        if key in os.environ:
128
            return os.environ[key]
3193.5.1 by Tim Penhey
Mostly working, just need to update the tests for lp://dev
129
        elif self._lp_instance is not None:
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
130
            try:
131
                return self.LAUNCHPAD_INSTANCE[self._lp_instance]
132
            except KeyError:
133
                raise InvalidLaunchpadInstance(self._lp_instance)
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
134
        else:
135
            return self.DEFAULT_SERVICE_URL
136
4505.6.2 by Jonathan Lange
Extract a method that gets a service from a URL.
137
    @classmethod
138
    def for_url(cls, url, **kwargs):
139
        """Return the Launchpad service corresponding to the given URL."""
140
        result = urlsplit(url)
141
        lp_instance = result[1]
142
        if lp_instance == '':
143
            lp_instance = None
144
        elif lp_instance not in cls.LAUNCHPAD_INSTANCE:
145
            raise errors.InvalidURL(path=url)
146
        return cls(lp_instance=lp_instance, **kwargs)
147
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
148
    def get_proxy(self, authenticated):
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
149
        """Return the proxy for XMLRPC requests."""
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
150
        if authenticated:
151
            # auth info must be in url
152
            # TODO: if there's no registrant email perhaps we should
153
            # just connect anonymously?
154
            scheme, hostinfo, path = urlsplit(self.service_url)[:3]
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
155
            if '@' in hostinfo:
156
                raise AssertionError(hostinfo)
157
            if self.registrant_email is None:
158
                raise AssertionError()
159
            if self.registrant_password is None:
160
                raise AssertionError()
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
161
            # TODO: perhaps fully quote the password to make it very slightly
162
            # obscured
163
            # TODO: can we perhaps add extra Authorization headers
164
            # directly to the request, rather than putting this into
165
            # the url?  perhaps a bit more secure against accidentally
166
            # revealing it.  std66 s3.2.1 discourages putting the
167
            # password in the url.
168
            hostinfo = '%s:%s@%s' % (urllib.quote(self.registrant_email),
169
                                     urllib.quote(self.registrant_password),
170
                                     hostinfo)
171
            url = urlunsplit((scheme, hostinfo, path, '', ''))
172
        else:
173
            url = self.service_url
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
174
        return xmlrpclib.ServerProxy(url, transport=self.transport)
175
176
    def gather_user_credentials(self):
177
        """Get the password from the user."""
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
178
        the_config = config.GlobalConfig()
179
        self.registrant_email = the_config.user_email()
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
180
        if self.registrant_password is None:
2900.2.21 by Vincent Ladeuil
Make lp_registration aware of authentication config.
181
            auth = config.AuthenticationConfig()
182
            scheme, hostinfo = urlsplit(self.service_url)[:2]
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
183
            prompt = 'launchpad.net password for %s: ' % \
184
                    self.registrant_email
2900.2.21 by Vincent Ladeuil
Make lp_registration aware of authentication config.
185
            # We will reuse http[s] credentials if we can, prompt user
186
            # otherwise
187
            self.registrant_password = auth.get_password(scheme, hostinfo,
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
188
                                                         self.registrant_email,
2900.2.21 by Vincent Ladeuil
Make lp_registration aware of authentication config.
189
                                                         prompt=prompt)
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
190
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
191
    def send_request(self, method_name, method_params, authenticated):
192
        proxy = self.get_proxy(authenticated)
0.4.21 by Martin Pool
Refactor BaseRequest.submit so details of submission are in the LaunchpadService
193
        method = getattr(proxy, method_name)
1668.1.9 by Martin Pool
(launchpad plugin) Better reporting of errors from xmlrpc
194
        try:
195
            result = method(*method_params)
196
        except xmlrpclib.ProtocolError, e:
197
            if e.errcode == 301:
198
                # TODO: This can give a ProtocolError representing a 301 error, whose
199
                # e.headers['location'] tells where to go and e.errcode==301; should
200
                # probably log something and retry on the new url.
201
                raise NotImplementedError("should resend request to %s, but this isn't implemented"
202
                        % e.headers.get('Location', 'NO-LOCATION-PRESENT'))
203
            else:
204
                # we don't want to print the original message because its
205
                # str representation includes the plaintext password.
206
                # TODO: print more headers to help in tracking down failures
207
                raise errors.BzrError("xmlrpc protocol error connecting to %s: %s %s"
208
                        % (self.service_url, e.errcode, e.errmsg))
4505.4.1 by Jonathan Lange
Trap gaierror and reraise appropriate ConnectionError.
209
        except socket.gaierror, e:
210
            raise errors.ConnectionError(
211
                "Could not resolve '%s'" % self.domain,
212
                orig_error=e)
0.4.21 by Martin Pool
Refactor BaseRequest.submit so details of submission are in the LaunchpadService
213
        return result
214
3955.3.1 by Jonathan Lange
Start doing URL stuff, extracting the domain bit out of LaunchpadService,
215
    @property
216
    def domain(self):
217
        if self._lp_instance is None:
218
            instance = self.DEFAULT_INSTANCE
219
        else:
220
            instance = self._lp_instance
221
        return self.LAUNCHPAD_DOMAINS[instance]
222
4505.6.5 by Jonathan Lange
Factor out some code that guesses a branch's URL.
223
    def _guess_branch_path(self, branch_url, _request_factory=None):
3955.3.1 by Jonathan Lange
Start doing URL stuff, extracting the domain bit out of LaunchpadService,
224
        scheme, hostinfo, path = urlsplit(branch_url)[:3]
3955.3.8 by Jonathan Lange
Support lp URL shortcuts.
225
        if _request_factory is None:
226
            _request_factory = ResolveLaunchpadPathRequest
227
        if scheme == 'lp':
228
            resolve = _request_factory(path)
3955.3.9 by Jonathan Lange
Catch errors.
229
            try:
230
                result = resolve.submit(self)
231
            except xmlrpclib.Fault, fault:
232
                raise errors.InvalidURL(branch_url, str(fault))
3955.3.8 by Jonathan Lange
Support lp URL shortcuts.
233
            branch_url = result['urls'][0]
234
            path = urlsplit(branch_url)[2]
235
        else:
236
            domains = (
237
                'bazaar.%s' % domain
238
                for domain in self.LAUNCHPAD_DOMAINS.itervalues())
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
239
            if hostinfo not in domains:
240
                raise NotLaunchpadBranch(branch_url)
4505.6.5 by Jonathan Lange
Factor out some code that guesses a branch's URL.
241
        return path.lstrip('/')
242
243
    def get_web_url_from_branch_url(self, branch_url, _request_factory=None):
244
        """Get the Launchpad web URL for the given branch URL.
245
246
        :raise errors.InvalidURL: if 'branch_url' cannot be identified as a
247
            Launchpad branch URL.
248
        :return: The URL of the branch on Launchpad.
249
        """
250
        path = self._guess_branch_path(branch_url, _request_factory)
3955.3.7 by Jonathan Lange
Test the launchpad-open command. Fix up some minor bugs.
251
        return urlutils.join('https://code.%s' % self.domain, path)
3955.3.1 by Jonathan Lange
Start doing URL stuff, extracting the domain bit out of LaunchpadService,
252
0.4.21 by Martin Pool
Refactor BaseRequest.submit so details of submission are in the LaunchpadService
253
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
254
class BaseRequest(object):
255
    """Base request for talking to a XMLRPC server."""
256
257
    # Set this to the XMLRPC method name.
258
    _methodname = None
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
259
    _authenticated = True
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
260
261
    def _request_params(self):
262
        """Return the arguments to pass to the method"""
263
        raise NotImplementedError(self._request_params)
264
265
    def submit(self, service):
0.4.21 by Martin Pool
Refactor BaseRequest.submit so details of submission are in the LaunchpadService
266
        """Submit request to Launchpad XMLRPC server.
267
268
        :param service: LaunchpadService indicating where to send
269
            the request and the authentication credentials.
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
270
        """
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
271
        return service.send_request(self._methodname, self._request_params(),
272
                                    self._authenticated)
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
273
274
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
275
class DryRunLaunchpadService(LaunchpadService):
276
    """Service that just absorbs requests without sending to server.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
277
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
278
    The dummy service does not need authentication.
279
    """
280
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
281
    def send_request(self, method_name, method_params, authenticated):
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
282
        pass
283
284
    def gather_user_credentials(self):
285
        pass
286
287
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
288
class BranchRegistrationRequest(BaseRequest):
289
    """Request to tell Launchpad about a bzr branch."""
290
291
    _methodname = 'register_branch'
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
292
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
293
    def __init__(self, branch_url,
0.4.15 by Martin Pool
(register-branch) Add command-line options
294
                 branch_name='',
295
                 branch_title='',
296
                 branch_description='',
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
297
                 author_email='',
0.4.15 by Martin Pool
(register-branch) Add command-line options
298
                 product_name='',
299
                 ):
3246.4.3 by Daniel Watkins
Replaced another assert.
300
        if not branch_url:
301
            raise errors.InvalidURL(branch_url, "You need to specify a non-empty branch URL.")
0.4.4 by Martin Pool
Start forming xmlrpc requests
302
        self.branch_url = branch_url
0.4.15 by Martin Pool
(register-branch) Add command-line options
303
        if branch_name:
304
            self.branch_name = branch_name
0.4.14 by Martin Pool
Update xmlrpc api
305
        else:
0.4.15 by Martin Pool
(register-branch) Add command-line options
306
            self.branch_name = self._find_default_branch_name(self.branch_url)
307
        self.branch_title = branch_title
308
        self.branch_description = branch_description
309
        self.author_email = author_email
310
        self.product_name = product_name
0.4.4 by Martin Pool
Start forming xmlrpc requests
311
312
    def _request_params(self):
313
        """Return xmlrpc request parameters"""
0.4.6 by Martin Pool
Put the rest of the parameters into the registration request.
314
        # This must match the parameter tuple expected by Launchpad for this
315
        # method
0.4.4 by Martin Pool
Start forming xmlrpc requests
316
        return (self.branch_url,
0.4.15 by Martin Pool
(register-branch) Add command-line options
317
                self.branch_name,
0.4.14 by Martin Pool
Update xmlrpc api
318
                self.branch_title,
0.4.6 by Martin Pool
Put the rest of the parameters into the registration request.
319
                self.branch_description,
0.4.14 by Martin Pool
Update xmlrpc api
320
                self.author_email,
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
321
                self.product_name,
0.4.4 by Martin Pool
Start forming xmlrpc requests
322
               )
323
0.4.15 by Martin Pool
(register-branch) Add command-line options
324
    def _find_default_branch_name(self, branch_url):
0.4.14 by Martin Pool
Update xmlrpc api
325
        i = branch_url.rfind('/')
326
        return branch_url[i+1:]
327
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
328
329
class BranchBugLinkRequest(BaseRequest):
330
    """Request to link a bzr branch in Launchpad to a bug."""
331
332
    _methodname = 'link_branch_to_bug'
333
334
    def __init__(self, branch_url, bug_id):
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
335
        self.bug_id = bug_id
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
336
        self.branch_url = branch_url
337
338
    def _request_params(self):
339
        """Return xmlrpc request parameters"""
340
        # This must match the parameter tuple expected by Launchpad for this
341
        # method
342
        return (self.branch_url, self.bug_id, '')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
343
344
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
345
class ResolveLaunchpadPathRequest(BaseRequest):
346
    """Request to resolve the path component of an lp: URL."""
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
347
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
348
    _methodname = 'resolve_lp_path'
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
349
    _authenticated = False
350
351
    def __init__(self, path):
3246.4.1 by Daniel Watkins
Replaced problematic assertion with exception call.
352
        if not path:
353
            raise errors.InvalidURL(path=path,
4416.7.1 by Neil Martinsen-Burrell
Fix 238764 refer to projects rather than products in launchpad plugin
354
                                    extra="You must specify a project.")
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
355
        self.path = path
356
357
    def _request_params(self):
358
        """Return xmlrpc request parameters"""
359
        return (self.path,)