/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
7408.3.1 by Jelmer Vernooij
Move propose module into core.
1
# Copyright (C) 2018-2019 Breezy Developers
0.431.1 by Jelmer Vernooij
Start work on propose command.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Helper functions for proposing merges."""
18
7490.146.1 by Jelmer Vernooij
Don't split at simply the first dot.
19
import re
20
7408.3.1 by Jelmer Vernooij
Move propose module into core.
21
from . import (
0.431.1 by Jelmer Vernooij
Start work on propose command.
22
    errors,
23
    hooks,
24
    registry,
7268.12.3 by Jelmer Vernooij
Add missing import.
25
    urlutils,
0.431.1 by Jelmer Vernooij
Start work on propose command.
26
    )
27
28
0.431.38 by Jelmer Vernooij
Add NoSuchProject.
29
class NoSuchProject(errors.BzrError):
30
31
    _fmt = "Project does not exist: %(project)s."
32
33
    def __init__(self, project):
34
        errors.BzrError.__init__(self)
35
        self.project = project
36
37
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
38
class MergeProposalExists(errors.BzrError):
39
40
    _fmt = "A merge proposal already exists: %(url)s."
41
7490.144.1 by Jelmer Vernooij
Support providing the actual proposal to MergeProposalExists.
42
    def __init__(self, url, existing_proposal=None):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
43
        errors.BzrError.__init__(self)
44
        self.url = url
7490.144.1 by Jelmer Vernooij
Support providing the actual proposal to MergeProposalExists.
45
        self.existing_proposal = existing_proposal
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
46
47
0.432.2 by Jelmer Vernooij
Publish command sort of works.
48
class UnsupportedHoster(errors.BzrError):
49
50
    _fmt = "No supported hoster for %(branch)s."
51
52
    def __init__(self, branch):
53
        errors.BzrError.__init__(self)
54
        self.branch = branch
55
56
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
57
class ReopenFailed(errors.BzrError):
58
59
    _fmt = "Reopening the merge proposal failed: %(error)s."
60
61
0.431.1 by Jelmer Vernooij
Start work on propose command.
62
class ProposeMergeHooks(hooks.Hooks):
63
    """Hooks for proposing a merge on Launchpad."""
64
65
    def __init__(self):
66
        hooks.Hooks.__init__(self, __name__, "Proposer.hooks")
0.431.57 by Jelmer Vernooij
Cleanups.
67
        self.add_hook(
68
            'get_prerequisite',
0.431.1 by Jelmer Vernooij
Start work on propose command.
69
            "Return the prerequisite branch for proposing as merge.", (3, 0))
0.431.57 by Jelmer Vernooij
Cleanups.
70
        self.add_hook(
71
            'merge_proposal_body',
0.431.1 by Jelmer Vernooij
Start work on propose command.
72
            "Return an initial body for the merge proposal message.", (3, 0))
73
74
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
75
class LabelsUnsupported(errors.BzrError):
76
    """Labels not supported by this hoster."""
77
78
    _fmt = "Labels are not supported by %(hoster)r."
79
80
    def __init__(self, hoster):
81
        errors.BzrError.__init__(self)
82
        self.hoster = hoster
83
84
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
85
class PrerequisiteBranchUnsupported(errors.BzrError):
86
    """Prerequisite branch not supported by this hoster."""
87
88
    def __init__(self, hoster):
89
        errors.BzrError.__init__(self)
90
        self.hoster = hoster
91
92
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
93
class HosterLoginRequired(errors.BzrError):
94
    """Action requires hoster login credentials."""
95
7268.8.2 by Jelmer Vernooij
Handle GitHub errors.
96
    _fmt = "Action requires credentials for hosting site %(hoster)r."""
97
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
98
    def __init__(self, hoster):
99
        errors.BzrError.__init__(self)
100
        self.hoster = hoster
101
102
7490.117.3 by Jelmer Vernooij
Raise detailed error when source branch is not derived from target branch.
103
class SourceNotDerivedFromTarget(errors.BzrError):
104
    """Source branch is not derived from target branch."""
105
106
    _fmt = ("Source %(source_branch)r not derived from "
107
            "target %(target_branch)r.")
108
109
    def __init__(self, source_branch, target_branch):
110
        errors.BzrError.__init__(
111
            self, source_branch=source_branch,
112
            target_branch=target_branch)
113
114
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
115
class MergeProposal(object):
116
    """A merge proposal.
117
118
    :ivar url: URL for the merge proposal
119
    """
120
121
    def __init__(self, url=None):
122
        self.url = url
123
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
124
    def get_description(self):
125
        """Get the description of the merge proposal."""
126
        raise NotImplementedError(self.get_description)
127
128
    def set_description(self, description):
129
        """Set the description of the merge proposal."""
130
        raise NotImplementedError(self.set_description)
131
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
132
    def get_commit_message(self):
133
        """Get the proposed commit message."""
134
        raise NotImplementedError(self.get_commit_message)
135
136
    def set_commit_message(self, commit_message):
137
        """Set the propose commit message."""
138
        raise NotImplementedError(self.set_commit_message)
139
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
140
    def get_source_branch_url(self):
141
        """Return the source branch."""
142
        raise NotImplementedError(self.get_source_branch_url)
143
7490.65.1 by Jelmer Vernooij
Add functions fo retrieving merge proposal source revision.
144
    def get_source_revision(self):
145
        """Return the latest revision for the source branch."""
146
        raise NotImplementedError(self.get_source_revision)
147
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
148
    def get_target_branch_url(self):
149
        """Return the target branch."""
150
        raise NotImplementedError(self.get_target_branch_url)
151
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
152
    def get_source_project(self):
153
        raise NotImplementedError(self.get_source_project)
154
155
    def get_target_project(self):
156
        raise NotImplementedError(self.get_target_project)
157
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
158
    def close(self):
159
        """Close the merge proposal (without merging it)."""
160
        raise NotImplementedError(self.close)
161
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
162
    def is_merged(self):
163
        """Check whether this merge proposal has been merged."""
164
        raise NotImplementedError(self.is_merged)
165
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
166
    def is_closed(self):
167
        """Check whether this merge proposal is closed
168
169
        This can either mean that it is merged or rejected.
170
        """
171
        raise NotImplementedError(self.is_closed)
172
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
173
    def merge(self, commit_message=None):
174
        """Merge this merge proposal."""
175
        raise NotImplementedError(self.merge)
176
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
177
    def can_be_merged(self):
178
        """Can this merge proposal be merged?
179
180
        The answer to this can be no if e.g. it has conflics.
181
        """
182
        raise NotImplementedError(self.can_be_merged)
183
7414.4.1 by Jelmer Vernooij
Add a MergeProposal.get_merged_by method.
184
    def get_merged_by(self):
185
        """If this proposal was merged, who merged it.
186
        """
187
        raise NotImplementedError(self.get_merged_by)
188
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
189
    def get_merged_at(self):
190
        """If this proposal was merged, when it was merged.
191
        """
192
        raise NotImplementedError(self.get_merged_at)
193
7490.52.1 by Jelmer Vernooij
Add MergeProposal.post_comment.
194
    def post_comment(self, body):
195
        """Post a comment on the merge proposal.
196
197
        Args:
198
          body: Body of the comment
199
        """
200
        raise NotImplementedError(self.post_comment)
201
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
202
0.432.2 by Jelmer Vernooij
Publish command sort of works.
203
class MergeProposalBuilder(object):
0.431.1 by Jelmer Vernooij
Start work on propose command.
204
    """Merge proposal creator.
205
206
    :param source_branch: Branch to propose for merging
207
    :param target_branch: Target branch
208
    """
209
210
    hooks = ProposeMergeHooks()
211
212
    def __init__(self, source_branch, target_branch):
213
        self.source_branch = source_branch
214
        self.target_branch = target_branch
215
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
216
    def get_initial_body(self):
217
        """Get a body for the proposal for the user to modify.
218
219
        :return: a str or None.
220
        """
221
        raise NotImplementedError(self.get_initial_body)
222
223
    def get_infotext(self):
224
        """Determine the initial comment for the merge proposal.
225
        """
226
        raise NotImplementedError(self.get_infotext)
227
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
228
    def create_proposal(self, description, reviewers=None, labels=None,
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
229
                        prerequisite_branch=None, commit_message=None,
7490.6.1 by Jelmer Vernooij
Add allow-collaboration flag.
230
                        work_in_progress=False, allow_collaboration=False):
0.431.1 by Jelmer Vernooij
Start work on propose command.
231
        """Create a proposal to merge a branch for merging.
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
232
233
        :param description: Description for the merge proposal
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
234
        :param reviewers: Optional list of people to ask reviews from
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
235
        :param labels: Labels to attach to the proposal
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
236
        :param prerequisite_branch: Optional prerequisite branch
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
237
        :param commit_message: Optional commit message
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
238
        :param work_in_progress:
239
            Whether this merge proposal is still a work-in-progress
7490.6.1 by Jelmer Vernooij
Add allow-collaboration flag.
240
        :param allow_collaboration:
241
            Whether to allow changes to the branch from the target branch
242
            maintainer(s)
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
243
        :return: A `MergeProposal` object
0.431.1 by Jelmer Vernooij
Start work on propose command.
244
        """
245
        raise NotImplementedError(self.create_proposal)
246
247
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
248
class Hoster(object):
249
    """A hosting site manager.
250
    """
251
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
252
    # Does this hoster support arbitrary labels being attached to merge
253
    # proposals?
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
254
    supports_merge_proposal_labels = None
255
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
256
    @property
257
    def name(self):
258
        """Name of this instance."""
259
        return "%s at %s" % (type(self).__name__, self.base_url)
260
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
261
    # Does this hoster support suggesting a commit message in the
262
    # merge proposal?
263
    supports_merge_proposal_commit_message = None
264
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
265
    # The base_url that would be visible to users. I.e. https://github.com/
266
    # rather than https://api.github.com/
267
    base_url = None
268
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
269
    # The syntax to use for formatting merge proposal descriptions.
270
    # Common values: 'plain', 'markdown'
271
    merge_proposal_description_format = None
272
7490.3.9 by Jelmer Vernooij
Add supports_allow_collaboration flag.
273
    # Does this hoster support the allow_collaboration flag?
274
    supports_allow_collaboration = False
275
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
276
    def publish_derived(self, new_branch, base_branch, name, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
277
                        owner=None, revision_id=None, overwrite=False,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
278
                        allow_lossy=True, tag_selector=None):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
279
        """Publish a branch to the site, derived from base_branch.
280
281
        :param base_branch: branch to derive the new branch from
282
        :param new_branch: branch to publish
0.432.3 by Jelmer Vernooij
Publish command works for github.
283
        :return: resulting branch, public URL
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
284
        :raise HosterLoginRequired: Action requires a hoster login, but none is
285
            known.
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
286
        """
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
287
        raise NotImplementedError(self.publish_derived)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
288
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
289
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
0.431.31 by Jelmer Vernooij
Drop autopropose command.
290
        """Get a derived branch ('a fork').
291
        """
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
292
        raise NotImplementedError(self.get_derived_branch)
293
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
294
    def get_push_url(self, branch):
295
        """Get the push URL for a branch."""
296
        raise NotImplementedError(self.get_push_url)
297
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
298
    def get_proposer(self, source_branch, target_branch):
299
        """Get a merge proposal creator.
300
0.431.31 by Jelmer Vernooij
Drop autopropose command.
301
        :note: source_branch does not have to be hosted by the hoster.
302
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
303
        :param source_branch: Source branch
304
        :param target_branch: Target branch
0.432.2 by Jelmer Vernooij
Publish command sort of works.
305
        :return: A MergeProposalBuilder object
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
306
        """
307
        raise NotImplementedError(self.get_proposer)
308
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
309
    def iter_proposals(self, source_branch, target_branch, status='open'):
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
310
        """Get the merge proposals for a specified branch tuple.
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
311
312
        :param source_branch: Source branch
313
        :param target_branch: Target branch
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
314
        :param status: Status of proposals to iterate over
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
315
        :return: Iterate over MergeProposal object
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
316
        """
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
317
        raise NotImplementedError(self.iter_proposals)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
318
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
319
    def get_proposal_by_url(self, url):
320
        """Retrieve a branch proposal by URL.
321
322
        :param url: Merge proposal URL.
323
        :return: MergeProposal object
324
        :raise UnsupportedHoster: Hoster does not support this URL
325
        """
326
        raise NotImplementedError(self.get_proposal_by_url)
327
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
328
    def hosts(self, branch):
329
        """Return true if this hoster hosts given branch."""
330
        raise NotImplementedError(self.hosts)
331
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
332
    @classmethod
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
333
    def probe_from_branch(cls, branch):
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
334
        """Create a Hoster object if this hoster knows about a branch."""
7441.1.1 by Jelmer Vernooij
Add strip_segment_parameters function.
335
        url = urlutils.strip_segment_parameters(branch.user_url)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
336
        return cls.probe_from_url(
7296.10.2 by Jelmer Vernooij
More fixes.
337
            url, possible_transports=[branch.control_transport])
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
338
339
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
340
    def probe_from_url(cls, url, possible_hosters=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
341
        """Create a Hoster object if this hoster knows about a URL."""
342
        raise NotImplementedError(cls.probe_from_url)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
343
7490.141.1 by Jelmer Vernooij
Expose owner/author in API.
344
    def iter_my_proposals(self, status='open', author=None):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
345
        """Iterate over the proposals created by the currently logged in user.
346
0.431.66 by Jelmer Vernooij
Add support for status argument.
347
        :param status: Only yield proposals with this status
348
            (one of: 'open', 'closed', 'merged', 'all')
7490.141.1 by Jelmer Vernooij
Expose owner/author in API.
349
        :param author: Name of author to query (defaults to current user)
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
350
        :return: Iterator over MergeProposal objects
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
351
        :raise HosterLoginRequired: Action requires a hoster login, but none is
352
            known.
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
353
        """
354
        raise NotImplementedError(self.iter_my_proposals)
355
7490.141.1 by Jelmer Vernooij
Expose owner/author in API.
356
    def iter_my_forks(self, owner=None):
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
357
        """Iterate over the currently logged in users' forks.
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
358
7490.141.1 by Jelmer Vernooij
Expose owner/author in API.
359
        :param owner: Name of owner to query (defaults to current user)
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
360
        :return: Iterator over project_name
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
361
        """
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
362
        raise NotImplementedError(self.iter_my_forks)
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
363
364
    def delete_project(self, name):
365
        """Delete a project.
366
        """
367
        raise NotImplementedError(self.delete_project)
368
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
369
    @classmethod
370
    def iter_instances(cls):
371
        """Iterate instances.
372
373
        :return: Hoster instances
374
        """
375
        raise NotImplementedError(cls.iter_instances)
376
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
377
    def get_current_user(self):
378
        """Retrieve the name of the currently logged in user.
379
380
        :return: Username or None if not logged in
381
        """
382
        raise NotImplementedError(self.get_current_user)
383
384
    def get_user_url(self, user):
385
        """Rerieve the web URL for a user."""
386
        raise NotImplementedError(self.get_user_url)
387
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
388
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
389
def determine_title(description):
390
    """Determine the title for a merge proposal based on full description."""
7490.146.1 by Jelmer Vernooij
Don't split at simply the first dot.
391
    firstline = description.splitlines()[0]
392
    try:
393
        i = firstline.index('. ')
394
    except ValueError:
395
        return firstline.rstrip('.')
396
    else:
397
        return firstline[:i]
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
398
399
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
400
def get_hoster(branch, possible_hosters=None):
7408.3.2 by Jelmer Vernooij
Add some tests.
401
    """Find the hoster for a branch.
402
403
    :param branch: Branch to find hoster for
404
    :param possible_hosters: Optional list of hosters to reuse
405
    :raise UnsupportedHoster: if there is no hoster that supports `branch`
406
    :return: A `Hoster` object
407
    """
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
408
    if possible_hosters:
409
        for hoster in possible_hosters:
410
            if hoster.hosts(branch):
411
                return hoster
0.432.2 by Jelmer Vernooij
Publish command sort of works.
412
    for name, hoster_cls in hosters.items():
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
413
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
414
            hoster = hoster_cls.probe_from_branch(branch)
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
415
        except UnsupportedHoster:
416
            pass
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
417
        else:
418
            if possible_hosters is not None:
419
                possible_hosters.append(hoster)
420
            return hoster
0.432.2 by Jelmer Vernooij
Publish command sort of works.
421
    raise UnsupportedHoster(branch)
422
423
7490.138.2 by Jelmer Vernooij
Add Proposal.update.
424
def iter_hoster_instances(hoster=None):
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
425
    """Iterate over all known hoster instances.
426
427
    :return: Iterator over Hoster instances
428
    """
7490.138.2 by Jelmer Vernooij
Add Proposal.update.
429
    if hoster is None:
430
        hoster_clses = [hoster_cls for name, hoster_cls in hosters.items()]
431
    else:
432
        hoster_clses = [hoster]
433
    for hoster_cls in hoster_clses:
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
434
        for instance in hoster_cls.iter_instances():
435
            yield instance
436
437
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
438
def get_proposal_by_url(url):
7408.3.2 by Jelmer Vernooij
Add some tests.
439
    """Get the proposal object associated with a URL.
440
441
    :param url: URL of the proposal
442
    :raise UnsupportedHoster: if there is no hoster that supports the URL
443
    :return: A `MergeProposal` object
444
    """
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
445
    for instance in iter_hoster_instances():
446
        try:
447
            return instance.get_proposal_by_url(url)
448
        except UnsupportedHoster:
449
            pass
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
450
    raise UnsupportedHoster(url)
451
452
0.432.2 by Jelmer Vernooij
Publish command sort of works.
453
hosters = registry.Registry()