~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2007-05-04 18:59:36 UTC
  • mto: This revision was merged to the branch mainline in revision 2643.
  • Revision ID: john@arbash-meinel.com-20070504185936-1mjdoqmtz74xe5mg
A C implementation of _fields_to_entry_0_parents drops the time from 400ms to 330ms for a 21k-entry tree

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import urllib
22
22
import xmlrpclib
23
23
 
24
 
from bzrlib import (
25
 
    config,
26
 
    errors,
27
 
    __version__ as _bzrlib_version,
28
 
    )
 
24
import bzrlib.config
 
25
import bzrlib.errors as errors
29
26
 
30
27
# for testing, do
31
28
'''
32
29
export BZR_LP_XMLRPC_URL=http://xmlrpc.staging.launchpad.net/bazaar/
33
30
'''
34
31
 
35
 
class InvalidLaunchpadInstance(errors.BzrError):
36
 
 
37
 
    _fmt = "%(lp_instance)s is not a valid Launchpad instance."
38
 
 
39
 
    def __init__(self, lp_instance):
40
 
        errors.BzrError.__init__(self, lp_instance=lp_instance)
41
 
 
42
 
 
43
32
class LaunchpadService(object):
44
33
    """A service to talk to Launchpad via XMLRPC.
45
 
 
 
34
    
46
35
    See http://bazaar-vcs.org/Specs/LaunchpadRpc for the methods we can call.
47
36
    """
48
37
 
49
 
    # NB: these should always end in a slash to avoid xmlrpclib appending
 
38
    # NB: this should always end in a slash to avoid xmlrpclib appending
50
39
    # '/RPC2'
51
 
    # We use edge as the default because:
52
 
    # Beta users get redirected to it
53
 
    # All users can use it
54
 
    # 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']
 
40
    DEFAULT_SERVICE_URL = 'https://xmlrpc.launchpad.net/bazaar/'
63
41
 
64
42
    transport = None
65
43
    registrant_email = None
66
44
    registrant_password = None
67
45
 
68
46
 
69
 
    def __init__(self, transport=None, lp_instance=None):
 
47
    def __init__(self, transport=None):
70
48
        """Construct a new service talking to the launchpad rpc server"""
71
 
        self._lp_instance = lp_instance
72
49
        if transport is None:
73
50
            uri_type = urllib.splittype(self.service_url)[0]
74
51
            if uri_type == 'https':
76
53
            else:
77
54
                transport = xmlrpclib.Transport()
78
55
            transport.user_agent = 'bzr/%s (xmlrpclib/%s)' \
79
 
                    % (_bzrlib_version, xmlrpclib.__version__)
 
56
                    % (bzrlib.__version__, xmlrpclib.__version__)
80
57
        self.transport = transport
81
58
 
82
59
 
89
66
        key = 'BZR_LP_XMLRPC_URL'
90
67
        if key in os.environ:
91
68
            return os.environ[key]
92
 
        elif self._lp_instance is not None:
93
 
            try:
94
 
                return self.LAUNCHPAD_INSTANCE[self._lp_instance]
95
 
            except KeyError:
96
 
                raise InvalidLaunchpadInstance(self._lp_instance)
97
69
        else:
98
70
            return self.DEFAULT_SERVICE_URL
99
71
 
100
 
    def get_proxy(self, authenticated):
 
72
    def get_proxy(self):
101
73
        """Return the proxy for XMLRPC requests."""
102
 
        if authenticated:
103
 
            # auth info must be in url
104
 
            # TODO: if there's no registrant email perhaps we should
105
 
            # just connect anonymously?
106
 
            scheme, hostinfo, path = urlsplit(self.service_url)[:3]
107
 
            assert '@' not in hostinfo
108
 
            assert self.registrant_email is not None
109
 
            assert self.registrant_password is not None
110
 
            # TODO: perhaps fully quote the password to make it very slightly
111
 
            # obscured
112
 
            # TODO: can we perhaps add extra Authorization headers
113
 
            # directly to the request, rather than putting this into
114
 
            # the url?  perhaps a bit more secure against accidentally
115
 
            # revealing it.  std66 s3.2.1 discourages putting the
116
 
            # password in the url.
117
 
            hostinfo = '%s:%s@%s' % (urllib.quote(self.registrant_email),
118
 
                                     urllib.quote(self.registrant_password),
119
 
                                     hostinfo)
120
 
            url = urlunsplit((scheme, hostinfo, path, '', ''))
121
 
        else:
122
 
            url = self.service_url
 
74
        # auth info must be in url
 
75
        # TODO: if there's no registrant email perhaps we should just connect
 
76
        # anonymously?
 
77
        scheme, hostinfo, path = urlsplit(self.service_url)[:3]
 
78
        assert '@' not in hostinfo
 
79
        assert self.registrant_email is not None
 
80
        assert self.registrant_password is not None
 
81
        # TODO: perhaps fully quote the password to make it very slightly
 
82
        # obscured
 
83
        # TODO: can we perhaps add extra Authorization headers directly to the 
 
84
        # request, rather than putting this into the url?  perhaps a bit more 
 
85
        # secure against accidentally revealing it.  std66 s3.2.1 discourages putting
 
86
        # the password in the url.
 
87
        hostinfo = '%s:%s@%s' % (urllib.quote(self.registrant_email),
 
88
                                 urllib.quote(self.registrant_password),
 
89
                                 hostinfo)
 
90
        url = urlunsplit((scheme, hostinfo, path, '', ''))
123
91
        return xmlrpclib.ServerProxy(url, transport=self.transport)
124
92
 
125
93
    def gather_user_credentials(self):
126
94
        """Get the password from the user."""
127
 
        the_config = config.GlobalConfig()
128
 
        self.registrant_email = the_config.user_email()
 
95
        config = bzrlib.config.GlobalConfig()
 
96
        self.registrant_email = config.user_email()
129
97
        if self.registrant_password is None:
130
 
            auth = config.AuthenticationConfig()
131
 
            scheme, hostinfo = urlsplit(self.service_url)[:2]
132
98
            prompt = 'launchpad.net password for %s: ' % \
133
99
                    self.registrant_email
134
 
            # We will reuse http[s] credentials if we can, prompt user
135
 
            # otherwise
136
 
            self.registrant_password = auth.get_password(scheme, hostinfo,
137
 
                                                         self.registrant_email,
138
 
                                                         prompt=prompt)
 
100
            self.registrant_password = getpass(prompt)
139
101
 
140
 
    def send_request(self, method_name, method_params, authenticated):
141
 
        proxy = self.get_proxy(authenticated)
 
102
    def send_request(self, method_name, method_params):
 
103
        proxy = self.get_proxy()
142
104
        assert method_name
143
105
        method = getattr(proxy, method_name)
144
106
        try:
164
126
 
165
127
    # Set this to the XMLRPC method name.
166
128
    _methodname = None
167
 
    _authenticated = True
168
129
 
169
130
    def _request_params(self):
170
131
        """Return the arguments to pass to the method"""
176
137
        :param service: LaunchpadService indicating where to send
177
138
            the request and the authentication credentials.
178
139
        """
179
 
        return service.send_request(self._methodname, self._request_params(),
180
 
                                    self._authenticated)
 
140
        return service.send_request(self._methodname, self._request_params())
181
141
 
182
142
 
183
143
class DryRunLaunchpadService(LaunchpadService):
186
146
    The dummy service does not need authentication.
187
147
    """
188
148
 
189
 
    def send_request(self, method_name, method_params, authenticated):
 
149
    def send_request(self, method_name, method_params):
190
150
        pass
191
151
 
192
152
    def gather_user_credentials(self):
205
165
                 author_email='',
206
166
                 product_name='',
207
167
                 ):
208
 
        if not branch_url:
209
 
            raise errors.InvalidURL(branch_url, "You need to specify a non-empty branch URL.")
 
168
        assert branch_url
210
169
        self.branch_url = branch_url
211
170
        if branch_name:
212
171
            self.branch_name = branch_name
249
208
        # This must match the parameter tuple expected by Launchpad for this
250
209
        # method
251
210
        return (self.branch_url, self.bug_id, '')
252
 
 
253
 
 
254
 
class ResolveLaunchpadPathRequest(BaseRequest):
255
 
    """Request to resolve the path component of an lp: URL."""
256
 
 
257
 
    _methodname = 'resolve_lp_path'
258
 
    _authenticated = False
259
 
 
260
 
    def __init__(self, path):
261
 
        if not path:
262
 
            raise errors.InvalidURL(path=path,
263
 
                                    extra="You must specify a product.")
264
 
        self.path = path
265
 
 
266
 
    def _request_params(self):
267
 
        """Return xmlrpc request parameters"""
268
 
        return (self.path,)