Merge lp:~jelmer/bzr/lp-plugin-lazy into lp:bzr
- lp-plugin-lazy
- Merge into bzr.dev
Status: | Merged |
---|---|
Approved by: | Martin Packman |
Approved revision: | no longer in the source branch. |
Merged at revision: | 6496 |
Proposed branch: | lp:~jelmer/bzr/lp-plugin-lazy |
Merge into: | lp:bzr |
Diff against target: |
851 lines (+422/-403) 2 files modified
bzrlib/plugins/launchpad/__init__.py (+12/-403) bzrlib/plugins/launchpad/cmds.py (+410/-0) |
To merge this branch: | bzr merge lp:~jelmer/bzr/lp-plugin-lazy |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Packman (community) | Approve | ||
Review via email: mp+96892@code.launchpad.net |
Commit message
Lazily load launchpad plugin commands.
Description of the change
Lazily load launchpad plugin commands.
Jelmer Vernooij (jelmer) wrote : | # |
Am 12/03/12 00:46, schrieb Martin Packman:
> Review: Needs Information
>
> Looks fine, though unlikely to have a big impact.
>
> Seems like _register_hooks should be replaced with a install_
I guess, although branch gets imported anyway by other bits of the code
so I don't think that would be all that useful.
>
> # Since we are a built-in plugin we share the bzrlib version
> + trace,
> version_info,
>
> Addition split the comment from the version_info line it's talking about. Mixing up module and object imports is a bit ick, but that's python.
Whoops, thanks.
Cheers,
jelmer
Martin Packman (gz) wrote : | # |
Sort of underlines the number of different lazy registration things we have...
Preview Diff
1 | === modified file 'bzrlib/plugins/launchpad/__init__.py' |
2 | --- bzrlib/plugins/launchpad/__init__.py 2012-03-06 17:17:27 +0000 |
3 | +++ bzrlib/plugins/launchpad/__init__.py 2012-03-12 14:38:29 +0000 |
4 | @@ -42,420 +42,29 @@ |
5 | |
6 | # see http://wiki.bazaar.canonical.com/Specs/BranchRegistrationTool |
7 | |
8 | -from bzrlib.lazy_import import lazy_import |
9 | -lazy_import(globals(), """ |
10 | -from bzrlib import ( |
11 | - ui, |
12 | - trace, |
13 | - ) |
14 | -from bzrlib.i18n import gettext |
15 | -""") |
16 | - |
17 | from bzrlib import ( |
18 | branch as _mod_branch, |
19 | config as _mod_config, |
20 | - controldir, |
21 | lazy_regex, |
22 | # Since we are a built-in plugin we share the bzrlib version |
23 | + trace, |
24 | version_info, |
25 | ) |
26 | from bzrlib.commands import ( |
27 | - Command, |
28 | - register_command, |
29 | + plugin_cmds, |
30 | ) |
31 | from bzrlib.directory_service import directories |
32 | -from bzrlib.errors import ( |
33 | - BzrCommandError, |
34 | - InvalidRevisionSpec, |
35 | - InvalidURL, |
36 | - NoPublicBranch, |
37 | - NotBranchError, |
38 | - ) |
39 | from bzrlib.help_topics import topic_registry |
40 | -from bzrlib.option import ( |
41 | - Option, |
42 | - ListOption, |
43 | - ) |
44 | - |
45 | - |
46 | -class cmd_register_branch(Command): |
47 | - __doc__ = """Register a branch with launchpad.net. |
48 | - |
49 | - This command lists a bzr branch in the directory of branches on |
50 | - launchpad.net. Registration allows the branch to be associated with |
51 | - bugs or specifications. |
52 | - |
53 | - Before using this command you must register the project to which the |
54 | - branch belongs, and create an account for yourself on launchpad.net. |
55 | - |
56 | - arguments: |
57 | - public_url: The publicly visible url for the branch to register. |
58 | - This must be an http or https url (which Launchpad can read |
59 | - from to access the branch). Local file urls, SFTP urls, and |
60 | - bzr+ssh urls will not work. |
61 | - If no public_url is provided, bzr will use the configured |
62 | - public_url if there is one for the current branch, and |
63 | - otherwise error. |
64 | - |
65 | - example: |
66 | - bzr register-branch http://foo.com/bzr/fooproject.mine \\ |
67 | - --project fooproject |
68 | - """ |
69 | - takes_args = ['public_url?'] |
70 | - takes_options = [ |
71 | - Option('project', |
72 | - 'Launchpad project short name to associate with the branch.', |
73 | - unicode), |
74 | - Option('product', |
75 | - 'Launchpad product short name to associate with the branch.', |
76 | - unicode, |
77 | - hidden=True), |
78 | - Option('branch-name', |
79 | - 'Short name for the branch; ' |
80 | - 'by default taken from the last component of the url.', |
81 | - unicode), |
82 | - Option('branch-title', |
83 | - 'One-sentence description of the branch.', |
84 | - unicode), |
85 | - Option('branch-description', |
86 | - 'Longer description of the purpose or contents of the branch.', |
87 | - unicode), |
88 | - Option('author', |
89 | - "Branch author's email address, if not yourself.", |
90 | - unicode), |
91 | - Option('link-bug', |
92 | - 'The bug this branch fixes.', |
93 | - int), |
94 | - Option('dry-run', |
95 | - 'Prepare the request but don\'t actually send it.') |
96 | - ] |
97 | - |
98 | - |
99 | - def run(self, |
100 | - public_url=None, |
101 | - project='', |
102 | - product=None, |
103 | - branch_name='', |
104 | - branch_title='', |
105 | - branch_description='', |
106 | - author='', |
107 | - link_bug=None, |
108 | - dry_run=False): |
109 | - from bzrlib.plugins.launchpad.lp_registration import ( |
110 | - BranchRegistrationRequest, BranchBugLinkRequest, |
111 | - DryRunLaunchpadService, LaunchpadService) |
112 | - if public_url is None: |
113 | - try: |
114 | - b = _mod_branch.Branch.open_containing('.')[0] |
115 | - except NotBranchError: |
116 | - raise BzrCommandError(gettext( |
117 | - 'register-branch requires a public ' |
118 | - 'branch url - see bzr help register-branch.')) |
119 | - public_url = b.get_public_branch() |
120 | - if public_url is None: |
121 | - raise NoPublicBranch(b) |
122 | - if product is not None: |
123 | - project = product |
124 | - trace.note(gettext( |
125 | - '--product is deprecated; please use --project.')) |
126 | - |
127 | - |
128 | - rego = BranchRegistrationRequest(branch_url=public_url, |
129 | - branch_name=branch_name, |
130 | - branch_title=branch_title, |
131 | - branch_description=branch_description, |
132 | - product_name=project, |
133 | - author_email=author, |
134 | - ) |
135 | - linko = BranchBugLinkRequest(branch_url=public_url, |
136 | - bug_id=link_bug) |
137 | - if not dry_run: |
138 | - service = LaunchpadService() |
139 | - # This gives back the xmlrpc url that can be used for future |
140 | - # operations on the branch. It's not so useful to print to the |
141 | - # user since they can't do anything with it from a web browser; it |
142 | - # might be nice for the server to tell us about an html url as |
143 | - # well. |
144 | - else: |
145 | - # Run on service entirely in memory |
146 | - service = DryRunLaunchpadService() |
147 | - service.gather_user_credentials() |
148 | - rego.submit(service) |
149 | - if link_bug: |
150 | - linko.submit(service) |
151 | - print 'Branch registered.' |
152 | - |
153 | -register_command(cmd_register_branch) |
154 | - |
155 | - |
156 | -class cmd_launchpad_open(Command): |
157 | - __doc__ = """Open a Launchpad branch page in your web browser.""" |
158 | - |
159 | - aliases = ['lp-open'] |
160 | - takes_options = [ |
161 | - Option('dry-run', |
162 | - 'Do not actually open the browser. Just say the URL we would ' |
163 | - 'use.'), |
164 | - ] |
165 | - takes_args = ['location?'] |
166 | - |
167 | - def _possible_locations(self, location): |
168 | - """Yield possible external locations for the branch at 'location'.""" |
169 | - yield location |
170 | - try: |
171 | - branch = _mod_branch.Branch.open_containing(location)[0] |
172 | - except NotBranchError: |
173 | - return |
174 | - branch_url = branch.get_public_branch() |
175 | - if branch_url is not None: |
176 | - yield branch_url |
177 | - branch_url = branch.get_push_location() |
178 | - if branch_url is not None: |
179 | - yield branch_url |
180 | - |
181 | - def _get_web_url(self, service, location): |
182 | - from bzrlib.plugins.launchpad.lp_registration import ( |
183 | - NotLaunchpadBranch) |
184 | - for branch_url in self._possible_locations(location): |
185 | - try: |
186 | - return service.get_web_url_from_branch_url(branch_url) |
187 | - except (NotLaunchpadBranch, InvalidURL): |
188 | - pass |
189 | - raise NotLaunchpadBranch(branch_url) |
190 | - |
191 | - def run(self, location=None, dry_run=False): |
192 | - from bzrlib.plugins.launchpad.lp_registration import ( |
193 | - LaunchpadService) |
194 | - if location is None: |
195 | - location = u'.' |
196 | - web_url = self._get_web_url(LaunchpadService(), location) |
197 | - trace.note(gettext('Opening %s in web browser') % web_url) |
198 | - if not dry_run: |
199 | - import webbrowser # this import should not be lazy |
200 | - # otherwise bzr.exe lacks this module |
201 | - webbrowser.open(web_url) |
202 | - |
203 | -register_command(cmd_launchpad_open) |
204 | - |
205 | - |
206 | -class cmd_launchpad_login(Command): |
207 | - __doc__ = """Show or set the Launchpad user ID. |
208 | - |
209 | - When communicating with Launchpad, some commands need to know your |
210 | - Launchpad user ID. This command can be used to set or show the |
211 | - user ID that Bazaar will use for such communication. |
212 | - |
213 | - :Examples: |
214 | - Show the Launchpad ID of the current user:: |
215 | - |
216 | - bzr launchpad-login |
217 | - |
218 | - Set the Launchpad ID of the current user to 'bob':: |
219 | - |
220 | - bzr launchpad-login bob |
221 | - """ |
222 | - aliases = ['lp-login'] |
223 | - takes_args = ['name?'] |
224 | - takes_options = [ |
225 | - 'verbose', |
226 | - Option('no-check', |
227 | - "Don't check that the user name is valid."), |
228 | - ] |
229 | - |
230 | - def run(self, name=None, no_check=False, verbose=False): |
231 | - # This is totally separate from any launchpadlib login system. |
232 | - from bzrlib.plugins.launchpad import account |
233 | - check_account = not no_check |
234 | - |
235 | - if name is None: |
236 | - username = account.get_lp_login() |
237 | - if username: |
238 | - if check_account: |
239 | - account.check_lp_login(username) |
240 | - if verbose: |
241 | - self.outf.write(gettext( |
242 | - "Launchpad user ID exists and has SSH keys.\n")) |
243 | - self.outf.write(username + '\n') |
244 | - else: |
245 | - self.outf.write(gettext('No Launchpad user ID configured.\n')) |
246 | - return 1 |
247 | - else: |
248 | - name = name.lower() |
249 | - if check_account: |
250 | - account.check_lp_login(name) |
251 | - if verbose: |
252 | - self.outf.write(gettext( |
253 | - "Launchpad user ID exists and has SSH keys.\n")) |
254 | - account.set_lp_login(name) |
255 | - if verbose: |
256 | - self.outf.write(gettext("Launchpad user ID set to '%s'.\n") % |
257 | - (name,)) |
258 | - |
259 | -register_command(cmd_launchpad_login) |
260 | - |
261 | - |
262 | -# XXX: cmd_launchpad_mirror is untested |
263 | -class cmd_launchpad_mirror(Command): |
264 | - __doc__ = """Ask Launchpad to mirror a branch now.""" |
265 | - |
266 | - aliases = ['lp-mirror'] |
267 | - takes_args = ['location?'] |
268 | - |
269 | - def run(self, location='.'): |
270 | - from bzrlib.plugins.launchpad import lp_api |
271 | - from bzrlib.plugins.launchpad.lp_registration import LaunchpadService |
272 | - branch, _ = _mod_branch.Branch.open_containing(location) |
273 | - service = LaunchpadService() |
274 | - launchpad = lp_api.login(service) |
275 | - lp_branch = lp_api.LaunchpadBranch.from_bzr(launchpad, branch, |
276 | - create_missing=False) |
277 | - lp_branch.lp.requestMirror() |
278 | - |
279 | - |
280 | -register_command(cmd_launchpad_mirror) |
281 | - |
282 | - |
283 | -class cmd_lp_propose_merge(Command): |
284 | - __doc__ = """Propose merging a branch on Launchpad. |
285 | - |
286 | - This will open your usual editor to provide the initial comment. When it |
287 | - has created the proposal, it will open it in your default web browser. |
288 | - |
289 | - The branch will be proposed to merge into SUBMIT_BRANCH. If SUBMIT_BRANCH |
290 | - is not supplied, the remembered submit branch will be used. If no submit |
291 | - branch is remembered, the development focus will be used. |
292 | - |
293 | - By default, the SUBMIT_BRANCH's review team will be requested to review |
294 | - the merge proposal. This can be overriden by specifying --review (-R). |
295 | - The parameter the launchpad account name of the desired reviewer. This |
296 | - may optionally be followed by '=' and the review type. For example: |
297 | - |
298 | - bzr lp-propose-merge --review jrandom --review review-team=qa |
299 | - |
300 | - This will propose a merge, request "jrandom" to perform a review of |
301 | - unspecified type, and request "review-team" to perform a "qa" review. |
302 | - """ |
303 | - |
304 | - takes_options = [Option('staging', |
305 | - help='Propose the merge on staging.'), |
306 | - Option('message', short_name='m', type=unicode, |
307 | - help='Commit message.'), |
308 | - Option('approve', |
309 | - help='Mark the proposal as approved immediately.'), |
310 | - Option('fixes', 'The bug this proposal fixes.', str), |
311 | - ListOption('review', short_name='R', type=unicode, |
312 | - help='Requested reviewer and optional type.')] |
313 | - |
314 | - takes_args = ['submit_branch?'] |
315 | - |
316 | - aliases = ['lp-submit', 'lp-propose'] |
317 | - |
318 | - def run(self, submit_branch=None, review=None, staging=False, |
319 | - message=None, approve=False, fixes=None): |
320 | - from bzrlib.plugins.launchpad import lp_propose |
321 | - tree, branch, relpath = controldir.ControlDir.open_containing_tree_or_branch( |
322 | - '.') |
323 | - if review is None: |
324 | - reviews = None |
325 | - else: |
326 | - reviews = [] |
327 | - for review in review: |
328 | - if '=' in review: |
329 | - reviews.append(review.split('=', 2)) |
330 | - else: |
331 | - reviews.append((review, '')) |
332 | - if submit_branch is None: |
333 | - submit_branch = branch.get_submit_branch() |
334 | - if submit_branch is None: |
335 | - target = None |
336 | - else: |
337 | - target = _mod_branch.Branch.open(submit_branch) |
338 | - proposer = lp_propose.Proposer(tree, branch, target, message, |
339 | - reviews, staging, approve=approve, |
340 | - fixes=fixes) |
341 | - proposer.check_proposal() |
342 | - proposer.create_proposal() |
343 | - |
344 | - |
345 | -register_command(cmd_lp_propose_merge) |
346 | - |
347 | - |
348 | -class cmd_lp_find_proposal(Command): |
349 | - |
350 | - __doc__ = """Find the proposal to merge this revision. |
351 | - |
352 | - Finds the merge proposal(s) that discussed landing the specified revision. |
353 | - This works only if the selected branch was the merge proposal target, and |
354 | - if the merged_revno is recorded for the merge proposal. The proposal(s) |
355 | - are opened in a web browser. |
356 | - |
357 | - Any revision involved in the merge may be specified-- the revision in |
358 | - which the merge was performed, or one of the revisions that was merged. |
359 | - |
360 | - So, to find the merge proposal that reviewed line 1 of README:: |
361 | - |
362 | - bzr lp-find-proposal -r annotate:README:1 |
363 | - """ |
364 | - |
365 | - takes_options = ['revision'] |
366 | - |
367 | - def run(self, revision=None): |
368 | - from bzrlib.plugins.launchpad import lp_api |
369 | - import webbrowser |
370 | - b = _mod_branch.Branch.open_containing('.')[0] |
371 | - pb = ui.ui_factory.nested_progress_bar() |
372 | - b.lock_read() |
373 | - try: |
374 | - revno = self._find_merged_revno(revision, b, pb) |
375 | - merged = self._find_proposals(revno, b, pb) |
376 | - if len(merged) == 0: |
377 | - raise BzrCommandError(gettext('No review found.')) |
378 | - trace.note(gettext('%d proposals(s) found.') % len(merged)) |
379 | - for mp in merged: |
380 | - webbrowser.open(lp_api.canonical_url(mp)) |
381 | - finally: |
382 | - b.unlock() |
383 | - pb.finished() |
384 | - |
385 | - def _find_merged_revno(self, revision, b, pb): |
386 | - if revision is None: |
387 | - return b.revno() |
388 | - pb.update(gettext('Finding revision-id')) |
389 | - revision_id = revision[0].as_revision_id(b) |
390 | - # a revno spec is necessarily on the mainline. |
391 | - if self._is_revno_spec(revision[0]): |
392 | - merging_revision = revision_id |
393 | - else: |
394 | - graph = b.repository.get_graph() |
395 | - pb.update(gettext('Finding merge')) |
396 | - merging_revision = graph.find_lefthand_merger( |
397 | - revision_id, b.last_revision()) |
398 | - if merging_revision is None: |
399 | - raise InvalidRevisionSpec(revision[0].user_spec, b) |
400 | - pb.update(gettext('Finding revno')) |
401 | - return b.revision_id_to_revno(merging_revision) |
402 | - |
403 | - def _find_proposals(self, revno, b, pb): |
404 | - launchpad = lp_api.login(lp_registration.LaunchpadService()) |
405 | - pb.update(gettext('Finding Launchpad branch')) |
406 | - lpb = lp_api.LaunchpadBranch.from_bzr(launchpad, b, |
407 | - create_missing=False) |
408 | - pb.update(gettext('Finding proposals')) |
409 | - return list(lpb.lp.getMergeProposals(status=['Merged'], |
410 | - merged_revnos=[revno])) |
411 | - |
412 | - |
413 | - @staticmethod |
414 | - def _is_revno_spec(spec): |
415 | - try: |
416 | - int(spec.user_spec) |
417 | - except ValueError: |
418 | - return False |
419 | - else: |
420 | - return True |
421 | - |
422 | - |
423 | -register_command(cmd_lp_find_proposal) |
424 | + |
425 | +for klsname, aliases in [ |
426 | + ("cmd_register_branch", []), |
427 | + ("cmd_launchpad_open", ["lp-open"]), |
428 | + ("cmd_launchpad_login", ["lp-login"]), |
429 | + ("cmd_launchpad_mirror", ["lp-mirror"]), |
430 | + ("cmd_lp_propose_merge", ["lp-submit", "lp-propose"]), |
431 | + ("cmd_lp_find_proposal", [])]: |
432 | + plugin_cmds.register_lazy(klsname, aliases, |
433 | + "bzrlib.plugins.launchpad.cmds") |
434 | |
435 | |
436 | def _register_directory(): |
437 | |
438 | === added file 'bzrlib/plugins/launchpad/cmds.py' |
439 | --- bzrlib/plugins/launchpad/cmds.py 1970-01-01 00:00:00 +0000 |
440 | +++ bzrlib/plugins/launchpad/cmds.py 2012-03-12 14:38:29 +0000 |
441 | @@ -0,0 +1,410 @@ |
442 | +# Copyright (C) 2006-2012 Canonical Ltd |
443 | +# |
444 | +# This program is free software; you can redistribute it and/or modify |
445 | +# it under the terms of the GNU General Public License as published by |
446 | +# the Free Software Foundation; either version 2 of the License, or |
447 | +# (at your option) any later version. |
448 | +# |
449 | +# This program is distributed in the hope that it will be useful, |
450 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
451 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
452 | +# GNU General Public License for more details. |
453 | +# |
454 | +# You should have received a copy of the GNU General Public License |
455 | +# along with this program; if not, write to the Free Software |
456 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
457 | + |
458 | +"""Launchpad plugin commands.""" |
459 | + |
460 | +from __future__ import absolute_import |
461 | + |
462 | +from bzrlib import ( |
463 | + branch as _mod_branch, |
464 | + controldir, |
465 | + trace, |
466 | + ) |
467 | +from bzrlib.commands import ( |
468 | + Command, |
469 | + ) |
470 | +from bzrlib.errors import ( |
471 | + BzrCommandError, |
472 | + InvalidRevisionSpec, |
473 | + InvalidURL, |
474 | + NoPublicBranch, |
475 | + NotBranchError, |
476 | + ) |
477 | +from bzrlib.i18n import gettext |
478 | +from bzrlib.option import ( |
479 | + Option, |
480 | + ListOption, |
481 | + ) |
482 | + |
483 | + |
484 | +class cmd_register_branch(Command): |
485 | + __doc__ = """Register a branch with launchpad.net. |
486 | + |
487 | + This command lists a bzr branch in the directory of branches on |
488 | + launchpad.net. Registration allows the branch to be associated with |
489 | + bugs or specifications. |
490 | + |
491 | + Before using this command you must register the project to which the |
492 | + branch belongs, and create an account for yourself on launchpad.net. |
493 | + |
494 | + arguments: |
495 | + public_url: The publicly visible url for the branch to register. |
496 | + This must be an http or https url (which Launchpad can read |
497 | + from to access the branch). Local file urls, SFTP urls, and |
498 | + bzr+ssh urls will not work. |
499 | + If no public_url is provided, bzr will use the configured |
500 | + public_url if there is one for the current branch, and |
501 | + otherwise error. |
502 | + |
503 | + example: |
504 | + bzr register-branch http://foo.com/bzr/fooproject.mine \\ |
505 | + --project fooproject |
506 | + """ |
507 | + takes_args = ['public_url?'] |
508 | + takes_options = [ |
509 | + Option('project', |
510 | + 'Launchpad project short name to associate with the branch.', |
511 | + unicode), |
512 | + Option('product', |
513 | + 'Launchpad product short name to associate with the branch.', |
514 | + unicode, |
515 | + hidden=True), |
516 | + Option('branch-name', |
517 | + 'Short name for the branch; ' |
518 | + 'by default taken from the last component of the url.', |
519 | + unicode), |
520 | + Option('branch-title', |
521 | + 'One-sentence description of the branch.', |
522 | + unicode), |
523 | + Option('branch-description', |
524 | + 'Longer description of the purpose or contents of the branch.', |
525 | + unicode), |
526 | + Option('author', |
527 | + "Branch author's email address, if not yourself.", |
528 | + unicode), |
529 | + Option('link-bug', |
530 | + 'The bug this branch fixes.', |
531 | + int), |
532 | + Option('dry-run', |
533 | + 'Prepare the request but don\'t actually send it.') |
534 | + ] |
535 | + |
536 | + |
537 | + def run(self, |
538 | + public_url=None, |
539 | + project='', |
540 | + product=None, |
541 | + branch_name='', |
542 | + branch_title='', |
543 | + branch_description='', |
544 | + author='', |
545 | + link_bug=None, |
546 | + dry_run=False): |
547 | + from bzrlib.plugins.launchpad.lp_registration import ( |
548 | + BranchRegistrationRequest, BranchBugLinkRequest, |
549 | + DryRunLaunchpadService, LaunchpadService) |
550 | + if public_url is None: |
551 | + try: |
552 | + b = _mod_branch.Branch.open_containing('.')[0] |
553 | + except NotBranchError: |
554 | + raise BzrCommandError(gettext( |
555 | + 'register-branch requires a public ' |
556 | + 'branch url - see bzr help register-branch.')) |
557 | + public_url = b.get_public_branch() |
558 | + if public_url is None: |
559 | + raise NoPublicBranch(b) |
560 | + if product is not None: |
561 | + project = product |
562 | + trace.note(gettext( |
563 | + '--product is deprecated; please use --project.')) |
564 | + |
565 | + |
566 | + rego = BranchRegistrationRequest(branch_url=public_url, |
567 | + branch_name=branch_name, |
568 | + branch_title=branch_title, |
569 | + branch_description=branch_description, |
570 | + product_name=project, |
571 | + author_email=author, |
572 | + ) |
573 | + linko = BranchBugLinkRequest(branch_url=public_url, |
574 | + bug_id=link_bug) |
575 | + if not dry_run: |
576 | + service = LaunchpadService() |
577 | + # This gives back the xmlrpc url that can be used for future |
578 | + # operations on the branch. It's not so useful to print to the |
579 | + # user since they can't do anything with it from a web browser; it |
580 | + # might be nice for the server to tell us about an html url as |
581 | + # well. |
582 | + else: |
583 | + # Run on service entirely in memory |
584 | + service = DryRunLaunchpadService() |
585 | + service.gather_user_credentials() |
586 | + rego.submit(service) |
587 | + if link_bug: |
588 | + linko.submit(service) |
589 | + self.outf.write('Branch registered.\n') |
590 | + |
591 | + |
592 | +class cmd_launchpad_open(Command): |
593 | + __doc__ = """Open a Launchpad branch page in your web browser.""" |
594 | + |
595 | + aliases = ['lp-open'] |
596 | + takes_options = [ |
597 | + Option('dry-run', |
598 | + 'Do not actually open the browser. Just say the URL we would ' |
599 | + 'use.'), |
600 | + ] |
601 | + takes_args = ['location?'] |
602 | + |
603 | + def _possible_locations(self, location): |
604 | + """Yield possible external locations for the branch at 'location'.""" |
605 | + yield location |
606 | + try: |
607 | + branch = _mod_branch.Branch.open_containing(location)[0] |
608 | + except NotBranchError: |
609 | + return |
610 | + branch_url = branch.get_public_branch() |
611 | + if branch_url is not None: |
612 | + yield branch_url |
613 | + branch_url = branch.get_push_location() |
614 | + if branch_url is not None: |
615 | + yield branch_url |
616 | + |
617 | + def _get_web_url(self, service, location): |
618 | + from bzrlib.plugins.launchpad.lp_registration import ( |
619 | + NotLaunchpadBranch) |
620 | + for branch_url in self._possible_locations(location): |
621 | + try: |
622 | + return service.get_web_url_from_branch_url(branch_url) |
623 | + except (NotLaunchpadBranch, InvalidURL): |
624 | + pass |
625 | + raise NotLaunchpadBranch(branch_url) |
626 | + |
627 | + def run(self, location=None, dry_run=False): |
628 | + from bzrlib.plugins.launchpad.lp_registration import ( |
629 | + LaunchpadService) |
630 | + if location is None: |
631 | + location = u'.' |
632 | + web_url = self._get_web_url(LaunchpadService(), location) |
633 | + trace.note(gettext('Opening %s in web browser') % web_url) |
634 | + if not dry_run: |
635 | + import webbrowser # this import should not be lazy |
636 | + # otherwise bzr.exe lacks this module |
637 | + webbrowser.open(web_url) |
638 | + |
639 | + |
640 | +class cmd_launchpad_login(Command): |
641 | + __doc__ = """Show or set the Launchpad user ID. |
642 | + |
643 | + When communicating with Launchpad, some commands need to know your |
644 | + Launchpad user ID. This command can be used to set or show the |
645 | + user ID that Bazaar will use for such communication. |
646 | + |
647 | + :Examples: |
648 | + Show the Launchpad ID of the current user:: |
649 | + |
650 | + bzr launchpad-login |
651 | + |
652 | + Set the Launchpad ID of the current user to 'bob':: |
653 | + |
654 | + bzr launchpad-login bob |
655 | + """ |
656 | + aliases = ['lp-login'] |
657 | + takes_args = ['name?'] |
658 | + takes_options = [ |
659 | + 'verbose', |
660 | + Option('no-check', |
661 | + "Don't check that the user name is valid."), |
662 | + ] |
663 | + |
664 | + def run(self, name=None, no_check=False, verbose=False): |
665 | + # This is totally separate from any launchpadlib login system. |
666 | + from bzrlib.plugins.launchpad import account |
667 | + check_account = not no_check |
668 | + |
669 | + if name is None: |
670 | + username = account.get_lp_login() |
671 | + if username: |
672 | + if check_account: |
673 | + account.check_lp_login(username) |
674 | + if verbose: |
675 | + self.outf.write(gettext( |
676 | + "Launchpad user ID exists and has SSH keys.\n")) |
677 | + self.outf.write(username + '\n') |
678 | + else: |
679 | + self.outf.write(gettext('No Launchpad user ID configured.\n')) |
680 | + return 1 |
681 | + else: |
682 | + name = name.lower() |
683 | + if check_account: |
684 | + account.check_lp_login(name) |
685 | + if verbose: |
686 | + self.outf.write(gettext( |
687 | + "Launchpad user ID exists and has SSH keys.\n")) |
688 | + account.set_lp_login(name) |
689 | + if verbose: |
690 | + self.outf.write(gettext("Launchpad user ID set to '%s'.\n") % |
691 | + (name,)) |
692 | + |
693 | + |
694 | +# XXX: cmd_launchpad_mirror is untested |
695 | +class cmd_launchpad_mirror(Command): |
696 | + __doc__ = """Ask Launchpad to mirror a branch now.""" |
697 | + |
698 | + aliases = ['lp-mirror'] |
699 | + takes_args = ['location?'] |
700 | + |
701 | + def run(self, location='.'): |
702 | + from bzrlib.plugins.launchpad import lp_api |
703 | + from bzrlib.plugins.launchpad.lp_registration import LaunchpadService |
704 | + branch, _ = _mod_branch.Branch.open_containing(location) |
705 | + service = LaunchpadService() |
706 | + launchpad = lp_api.login(service) |
707 | + lp_branch = lp_api.LaunchpadBranch.from_bzr(launchpad, branch, |
708 | + create_missing=False) |
709 | + lp_branch.lp.requestMirror() |
710 | + |
711 | + |
712 | +class cmd_lp_propose_merge(Command): |
713 | + __doc__ = """Propose merging a branch on Launchpad. |
714 | + |
715 | + This will open your usual editor to provide the initial comment. When it |
716 | + has created the proposal, it will open it in your default web browser. |
717 | + |
718 | + The branch will be proposed to merge into SUBMIT_BRANCH. If SUBMIT_BRANCH |
719 | + is not supplied, the remembered submit branch will be used. If no submit |
720 | + branch is remembered, the development focus will be used. |
721 | + |
722 | + By default, the SUBMIT_BRANCH's review team will be requested to review |
723 | + the merge proposal. This can be overriden by specifying --review (-R). |
724 | + The parameter the launchpad account name of the desired reviewer. This |
725 | + may optionally be followed by '=' and the review type. For example: |
726 | + |
727 | + bzr lp-propose-merge --review jrandom --review review-team=qa |
728 | + |
729 | + This will propose a merge, request "jrandom" to perform a review of |
730 | + unspecified type, and request "review-team" to perform a "qa" review. |
731 | + """ |
732 | + |
733 | + takes_options = [Option('staging', |
734 | + help='Propose the merge on staging.'), |
735 | + Option('message', short_name='m', type=unicode, |
736 | + help='Commit message.'), |
737 | + Option('approve', |
738 | + help='Mark the proposal as approved immediately.'), |
739 | + Option('fixes', 'The bug this proposal fixes.', str), |
740 | + ListOption('review', short_name='R', type=unicode, |
741 | + help='Requested reviewer and optional type.')] |
742 | + |
743 | + takes_args = ['submit_branch?'] |
744 | + |
745 | + aliases = ['lp-submit', 'lp-propose'] |
746 | + |
747 | + def run(self, submit_branch=None, review=None, staging=False, |
748 | + message=None, approve=False, fixes=None): |
749 | + from bzrlib.plugins.launchpad import lp_propose |
750 | + tree, branch, relpath = controldir.ControlDir.open_containing_tree_or_branch( |
751 | + '.') |
752 | + if review is None: |
753 | + reviews = None |
754 | + else: |
755 | + reviews = [] |
756 | + for review in review: |
757 | + if '=' in review: |
758 | + reviews.append(review.split('=', 2)) |
759 | + else: |
760 | + reviews.append((review, '')) |
761 | + if submit_branch is None: |
762 | + submit_branch = branch.get_submit_branch() |
763 | + if submit_branch is None: |
764 | + target = None |
765 | + else: |
766 | + target = _mod_branch.Branch.open(submit_branch) |
767 | + proposer = lp_propose.Proposer(tree, branch, target, message, |
768 | + reviews, staging, approve=approve, |
769 | + fixes=fixes) |
770 | + proposer.check_proposal() |
771 | + proposer.create_proposal() |
772 | + |
773 | + |
774 | +class cmd_lp_find_proposal(Command): |
775 | + |
776 | + __doc__ = """Find the proposal to merge this revision. |
777 | + |
778 | + Finds the merge proposal(s) that discussed landing the specified revision. |
779 | + This works only if the selected branch was the merge proposal target, and |
780 | + if the merged_revno is recorded for the merge proposal. The proposal(s) |
781 | + are opened in a web browser. |
782 | + |
783 | + Any revision involved in the merge may be specified-- the revision in |
784 | + which the merge was performed, or one of the revisions that was merged. |
785 | + |
786 | + So, to find the merge proposal that reviewed line 1 of README:: |
787 | + |
788 | + bzr lp-find-proposal -r annotate:README:1 |
789 | + """ |
790 | + |
791 | + takes_options = ['revision'] |
792 | + |
793 | + def run(self, revision=None): |
794 | + from bzrlib import ui |
795 | + from bzrlib.plugins.launchpad import lp_api |
796 | + import webbrowser |
797 | + b = _mod_branch.Branch.open_containing('.')[0] |
798 | + pb = ui.ui_factory.nested_progress_bar() |
799 | + b.lock_read() |
800 | + try: |
801 | + revno = self._find_merged_revno(revision, b, pb) |
802 | + merged = self._find_proposals(revno, b, pb) |
803 | + if len(merged) == 0: |
804 | + raise BzrCommandError(gettext('No review found.')) |
805 | + trace.note(gettext('%d proposals(s) found.') % len(merged)) |
806 | + for mp in merged: |
807 | + webbrowser.open(lp_api.canonical_url(mp)) |
808 | + finally: |
809 | + b.unlock() |
810 | + pb.finished() |
811 | + |
812 | + def _find_merged_revno(self, revision, b, pb): |
813 | + if revision is None: |
814 | + return b.revno() |
815 | + pb.update(gettext('Finding revision-id')) |
816 | + revision_id = revision[0].as_revision_id(b) |
817 | + # a revno spec is necessarily on the mainline. |
818 | + if self._is_revno_spec(revision[0]): |
819 | + merging_revision = revision_id |
820 | + else: |
821 | + graph = b.repository.get_graph() |
822 | + pb.update(gettext('Finding merge')) |
823 | + merging_revision = graph.find_lefthand_merger( |
824 | + revision_id, b.last_revision()) |
825 | + if merging_revision is None: |
826 | + raise InvalidRevisionSpec(revision[0].user_spec, b) |
827 | + pb.update(gettext('Finding revno')) |
828 | + return b.revision_id_to_revno(merging_revision) |
829 | + |
830 | + def _find_proposals(self, revno, b, pb): |
831 | + from bzrlib.plugins.launchpad import (lp_api, lp_registration) |
832 | + launchpad = lp_api.login(lp_registration.LaunchpadService()) |
833 | + pb.update(gettext('Finding Launchpad branch')) |
834 | + lpb = lp_api.LaunchpadBranch.from_bzr(launchpad, b, |
835 | + create_missing=False) |
836 | + pb.update(gettext('Finding proposals')) |
837 | + return list(lpb.lp.getMergeProposals(status=['Merged'], |
838 | + merged_revnos=[revno])) |
839 | + |
840 | + |
841 | + @staticmethod |
842 | + def _is_revno_spec(spec): |
843 | + try: |
844 | + int(spec.user_spec) |
845 | + except ValueError: |
846 | + return False |
847 | + else: |
848 | + return True |
849 | + |
850 | + |
851 | + |
Looks fine, though unlikely to have a big impact.
Seems like _register_hooks should be replaced with a install_ lazy_named_ hook call, which would mean the _mod_branch import could go as well?
# Since we are a built-in plugin we share the bzrlib version
+ trace,
version_info,
Addition split the comment from the version_info line it's talking about. Mixing up module and object imports is a bit ick, but that's python.