~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugins/launchpad/lp_registration.py

  • Committer: Vincent Ladeuil
  • Date: 2010-02-10 15:46:03 UTC
  • mfrom: (4985.3.21 update)
  • mto: This revision was merged to the branch mainline in revision 5021.
  • Revision ID: v.ladeuil+lp@free.fr-20100210154603-k4no1gvfuqpzrw7p
Update performs two merges in a more logical order but stop on conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
 
18
 
from getpass import getpass
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
19
18
import os
 
19
import socket
20
20
from urlparse import urlsplit, urlunsplit
21
21
import urllib
22
22
import xmlrpclib
24
24
from bzrlib import (
25
25
    config,
26
26
    errors,
 
27
    urlutils,
27
28
    __version__ as _bzrlib_version,
28
29
    )
 
30
from bzrlib.transport.http import _urllib2_wrappers
 
31
 
29
32
 
30
33
# for testing, do
31
34
'''
40
43
        errors.BzrError.__init__(self, lp_instance=lp_instance)
41
44
 
42
45
 
 
46
class NotLaunchpadBranch(errors.BzrError):
 
47
 
 
48
    _fmt = "%(url)s is not registered on Launchpad."
 
49
 
 
50
    def __init__(self, url):
 
51
        errors.BzrError.__init__(self, url=url)
 
52
 
 
53
 
 
54
class XMLRPCTransport(xmlrpclib.Transport):
 
55
 
 
56
    def __init__(self, scheme):
 
57
        # In python2.4 xmlrpclib.Transport is a old-style class, and does not
 
58
        # define __init__, so we check first
 
59
        init = getattr(xmlrpclib.Transport, '__init__', None)
 
60
        if init is not None:
 
61
            init(self)
 
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)
 
70
        # FIXME: _urllib2_wrappers will override user-agent with its own
 
71
        # request.add_header("User-Agent", self.user_agent)
 
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
 
43
81
class LaunchpadService(object):
44
82
    """A service to talk to Launchpad via XMLRPC.
45
83
 
46
 
    See http://bazaar-vcs.org/Specs/LaunchpadRpc for the methods we can call.
 
84
    See http://wiki.bazaar.canonical.com/Specs/LaunchpadRpc for the methods we can call.
47
85
    """
48
86
 
 
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
 
49
95
    # NB: these should always end in a slash to avoid xmlrpclib appending
50
96
    # '/RPC2'
 
97
    LAUNCHPAD_INSTANCE = {}
 
98
    for instance, domain in LAUNCHPAD_DOMAINS.iteritems():
 
99
        LAUNCHPAD_INSTANCE[instance] = 'https://xmlrpc.%s/bazaar/' % domain
 
100
 
51
101
    # We use edge as the default because:
52
102
    # Beta users get redirected to it
53
103
    # All users can use it
54
104
    # There is a bug in the launchpad side where redirection causes an OOPS.
55
 
    LAUNCHPAD_INSTANCE = {
56
 
        'production': 'https://xmlrpc.launchpad.net/bazaar/',
57
 
        'edge': 'https://xmlrpc.edge.launchpad.net/bazaar/',
58
 
        'staging': 'https://xmlrpc.staging.launchpad.net/bazaar/',
59
 
        'demo': 'https://xmlrpc.demo.launchpad.net/bazaar/',
60
 
        'dev': 'http://xmlrpc.launchpad.dev/bazaar/',
61
 
        }
62
 
    DEFAULT_SERVICE_URL = LAUNCHPAD_INSTANCE['edge']
 
105
    DEFAULT_INSTANCE = 'edge'
 
106
    DEFAULT_SERVICE_URL = LAUNCHPAD_INSTANCE[DEFAULT_INSTANCE]
63
107
 
64
108
    transport = None
65
109
    registrant_email = None
71
115
        self._lp_instance = lp_instance
72
116
        if transport is None:
73
117
            uri_type = urllib.splittype(self.service_url)[0]
74
 
            if uri_type == 'https':
75
 
                transport = xmlrpclib.SafeTransport()
76
 
            else:
77
 
                transport = xmlrpclib.Transport()
 
118
            transport = XMLRPCTransport(uri_type)
78
119
            transport.user_agent = 'bzr/%s (xmlrpclib/%s)' \
79
120
                    % (_bzrlib_version, xmlrpclib.__version__)
80
121
        self.transport = transport
81
122
 
82
 
 
83
123
    @property
84
124
    def service_url(self):
85
125
        """Return the http or https url for the xmlrpc server.
97
137
        else:
98
138
            return self.DEFAULT_SERVICE_URL
99
139
 
 
140
    @classmethod
 
141
    def for_url(cls, url, **kwargs):
 
142
        """Return the Launchpad service corresponding to the given URL."""
 
143
        result = urlsplit(url)
 
144
        lp_instance = result[1]
 
145
        if lp_instance == '':
 
146
            lp_instance = None
 
147
        elif lp_instance not in cls.LAUNCHPAD_INSTANCE:
 
148
            raise errors.InvalidURL(path=url)
 
149
        return cls(lp_instance=lp_instance, **kwargs)
 
150
 
100
151
    def get_proxy(self, authenticated):
101
152
        """Return the proxy for XMLRPC requests."""
102
153
        if authenticated:
158
209
                # TODO: print more headers to help in tracking down failures
159
210
                raise errors.BzrError("xmlrpc protocol error connecting to %s: %s %s"
160
211
                        % (self.service_url, e.errcode, e.errmsg))
 
212
        except socket.gaierror, e:
 
213
            raise errors.ConnectionError(
 
214
                "Could not resolve '%s'" % self.domain,
 
215
                orig_error=e)
161
216
        return result
162
217
 
 
218
    @property
 
219
    def domain(self):
 
220
        if self._lp_instance is None:
 
221
            instance = self.DEFAULT_INSTANCE
 
222
        else:
 
223
            instance = self._lp_instance
 
224
        return self.LAUNCHPAD_DOMAINS[instance]
 
225
 
 
226
    def _guess_branch_path(self, branch_url, _request_factory=None):
 
227
        scheme, hostinfo, path = urlsplit(branch_url)[:3]
 
228
        if _request_factory is None:
 
229
            _request_factory = ResolveLaunchpadPathRequest
 
230
        if scheme == 'lp':
 
231
            resolve = _request_factory(path)
 
232
            try:
 
233
                result = resolve.submit(self)
 
234
            except xmlrpclib.Fault, fault:
 
235
                raise errors.InvalidURL(branch_url, str(fault))
 
236
            branch_url = result['urls'][0]
 
237
            path = urlsplit(branch_url)[2]
 
238
        else:
 
239
            domains = (
 
240
                'bazaar.%s' % domain
 
241
                for domain in self.LAUNCHPAD_DOMAINS.itervalues())
 
242
            if hostinfo not in domains:
 
243
                raise NotLaunchpadBranch(branch_url)
 
244
        return path.lstrip('/')
 
245
 
 
246
    def get_web_url_from_branch_url(self, branch_url, _request_factory=None):
 
247
        """Get the Launchpad web URL for the given branch URL.
 
248
 
 
249
        :raise errors.InvalidURL: if 'branch_url' cannot be identified as a
 
250
            Launchpad branch URL.
 
251
        :return: The URL of the branch on Launchpad.
 
252
        """
 
253
        path = self._guess_branch_path(branch_url, _request_factory)
 
254
        return urlutils.join('https://code.%s' % self.domain, path)
 
255
 
163
256
 
164
257
class BaseRequest(object):
165
258
    """Base request for talking to a XMLRPC server."""
184
277
 
185
278
class DryRunLaunchpadService(LaunchpadService):
186
279
    """Service that just absorbs requests without sending to server.
187
 
    
 
280
 
188
281
    The dummy service does not need authentication.
189
282
    """
190
283
 
261
354
    def __init__(self, path):
262
355
        if not path:
263
356
            raise errors.InvalidURL(path=path,
264
 
                                    extra="You must specify a product.")
 
357
                                    extra="You must specify a project.")
265
358
        self.path = path
266
359
 
267
360
    def _request_params(self):