Merge lp:~james-w/launchpad/register-code-import into lp:launchpad

Proposed by James Westby
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~james-w/launchpad/register-code-import
Merge into: lp:launchpad
Prerequisite: lp:~james-w/launchpad/code-imports-for-source-packages
Diff against target: 732 lines (+302/-35)
18 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+5/-1)
lib/lp/code/errors.py (+1/-0)
lib/lp/code/interfaces/branch.py (+8/-1)
lib/lp/code/interfaces/branchtarget.py (+3/-1)
lib/lp/code/interfaces/codeimport.py (+5/-1)
lib/lp/code/interfaces/hasbranches.py (+49/-5)
lib/lp/code/interfaces/webservice.py (+6/-1)
lib/lp/code/mail/tests/test_codeimport.py (+1/-1)
lib/lp/code/model/branchtarget.py (+20/-2)
lib/lp/code/model/codeimport.py (+6/-3)
lib/lp/code/model/hasbranches.py (+15/-1)
lib/lp/code/model/tests/test_codeimport.py (+40/-0)
lib/lp/code/stories/webservice/xx-branchmergeproposal.txt (+1/-1)
lib/lp/code/stories/webservice/xx-code-import.txt (+129/-7)
lib/lp/registry/interfaces/product.py (+4/-2)
lib/lp/registry/interfaces/sourcepackage.py (+3/-2)
lib/lp/registry/model/product.py (+3/-3)
lib/lp/registry/model/sourcepackage.py (+3/-3)
To merge this branch: bzr merge lp:~james-w/launchpad/register-code-import
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+22668@code.launchpad.net

Commit message

There is now a newCodeImport method on Product and SourcePackage in the API.

Description of the change

Hi,

This exposes the creation of code imports over the API.

It first adds an "owner" argument, so that we can specify
this through the API, with associated tests.

It then exposes the IBranchTarget.newCodeImport() method
over the API using a small interface and mixin, due to it being
on an adapter.

It then adds a bunch of tests for this, including error cases
where the user may specify bad information.

For the last bit I used webservice_error so that they would get
400 not 500 responses, and there won't be OOPSes generated for them.

To do this I had to add the exception classes that have the decorator
to the interfaces/webservice.py file so that lazr.restful can register
views for them.

Doing this I discovered that the existing webservice_error in lp.code
wasn't working, and in fact had a test asserting it wasn't working,
so I fixed that along the way.

Thanks,

James

To post a comment you must log in.
Revision history for this message
James Westby (james-w) wrote :

Passes ec2, so requesting a review.

Thanks,

James

Revision history for this message
Aaron Bentley (abentley) wrote :

It's nice of you to defer requesting a review until all tests are passing, but I think a lot of us don't observe that.

Was there a preimplementation call?

Was there any lint?

AIUI PEP8 requires that items within a class definition be separated by a single blank line, so a bunch of your webservice_error(400) lines need a blank line in front of them.

Why are you using export_write_operation rather than export_factory_operation on branchtarget?

On IHasCodeImports, you claim that newCodeImport returns BranchMergeProposal. I think this is inaccurate.

What would you think about expressing xx-code-import.txt's "Exceptions" heading as a unit test? Failing that, it needs two blank lines separating it from the previous section.

review: Needs Information
Revision history for this message
James Westby (james-w) wrote :

> It's nice of you to defer requesting a review until all tests are passing, but
> I think a lot of us don't observe that.
>
> Was there a preimplementation call?

No, but I chatted with wgrant about the strategy.

> Was there any lint?

I don't know, running "make lint" runs it on lots of unexpected files.

> AIUI PEP8 requires that items within a class definition be separated by a
> single blank line, so a bunch of your webservice_error(400) lines need a blank
> line in front of them.

Done.

> Why are you using export_write_operation rather than export_factory_operation
> on branchtarget?

That's not supposed to be there any more, deleted.

> On IHasCodeImports, you claim that newCodeImport returns BranchMergeProposal.
> I think this is inaccurate.

Fixed.

> What would you think about expressing xx-code-import.txt's "Exceptions"
> heading as a unit test? Failing that, it needs two blank lines separating it
> from the previous section.

I don't know how to do webservice tests as unit tests, if you can point me to
examples I will convert them.

Blank lines issue fixed anyway, and I updated the text to make the intent of
the tests clearer as well.

Thanks,

James

Revision history for this message
Aaron Bentley (abentley) wrote :
Download full text (14.5 KiB)

On 04/05/2010 05:39 PM, James Westby wrote:

>> Was there any lint?
>
> I don't know, running "make lint" runs it on lots of unexpected files.

When there are no uncommitted changes, "make lint" compares your tree to
the parent branch to determine which files it runs against. This means
it will also lint files changed by your prerequisite branch. You parent
branch may also be out of date.

You can also run bin/lint.sh with the list of files you modified. If
you use pipelines and have the lp-review-body plugin installed, you can
use lp-propose (formerly lp-submit) and it will use the previous pipe to
determine which files to lint.

Anyhow, there was lots of lint:
$ make lint
= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
   lib/canonical/launchpad/interfaces/_schema_circular_imports.py
   lib/lp/code/errors.py
   lib/lp/code/interfaces/branch.py
   lib/lp/code/interfaces/branchtarget.py
   lib/lp/code/interfaces/codeimport.py
   lib/lp/code/interfaces/hasbranches.py
   lib/lp/code/interfaces/webservice.py
   lib/lp/code/model/branchtarget.py
   lib/lp/code/model/codeimport.py
   lib/lp/code/model/hasbranches.py
   lib/lp/code/model/tests/test_codeimport.py
   lib/lp/code/stories/webservice/xx-branchmergeproposal.txt
   lib/lp/code/stories/webservice/xx-code-import.txt
   lib/lp/registry/interfaces/product.py
   lib/lp/registry/interfaces/sourcepackage.py
   lib/lp/registry/model/product.py
   lib/lp/registry/model/sourcepackage.py

== Pyflakes notices ==

lib/canonical/launchpad/interfaces/_schema_circular_imports.py
     43: 'IBranchTarget' imported but unused

lib/lp/code/interfaces/branch.py
     11: undefined name 'UnknownBranchTypeError' in __all__

lib/lp/code/interfaces/webservice.py
     8: 'BranchMergeProposalExists' imported but unused
     9: 'BranchCreatorNotOwner' imported but unused
     9: 'IBranchSet' imported but unused
     9: 'IBranch' imported but unused
     9: 'BranchExists' imported but unused
     9: 'BranchCreatorNotMemberOfOwnerTeam' imported but unused
     12: 'IBranchMergeProposal' imported but unused
     13: 'IBranchSubscription' imported but unused
     14: 'ICodeImport' imported but unused
     15: 'ICodeReviewComment' imported but unused
     16: 'ICodeReviewVoteReference' imported but unused
     17: 'IStaticDiff' imported but unused
     17: 'IDiff' imported but unused
     17: 'IPreviewDiff' imported but unused

lib/lp/registry/model/sourcepackage.py
     684: local variable 'displayname' is assigned to but never used

== Pylint notices ==

lib/canonical/launchpad/interfaces/_schema_circular_imports.py
     18: [F0401] Unable to import 'lazr.restful.declarations'
     43: [W0611] Unused import IBranchTarget

lib/lp/code/errors.py
     20: [F0401] Unable to import 'lazr.restful.declarations'

lib/lp/code/interfaces/branch.py
     442: [C0322, IBranch.setOwner] Operator not preceded by a space
     title=_("The new owner of the branch."),
     ^
     schema=IPerson))
     @export_write_operation()
     def setOwner(new_owner, user):
     451: [C0322, IBranch.setTarget] Opera...

Revision history for this message
James Westby (james-w) wrote :
Download full text (9.6 KiB)

On Tue, 06 Apr 2010 15:33:31 -0000, Aaron Bentley <email address hidden> wrote:
> On 04/05/2010 05:39 PM, James Westby wrote:
>
> >> Was there any lint?
> >
> > I don't know, running "make lint" runs it on lots of unexpected files.
>
> When there are no uncommitted changes, "make lint" compares your tree to
> the parent branch to determine which files it runs against. This means
> it will also lint files changed by your prerequisite branch. You parent
> branch may also be out of date.

I am using pipelines, so I would have thought it would run against the
previous pipe?

> You can also run bin/lint.sh with the list of files you modified. If
> you use pipelines and have the lp-review-body plugin installed, you can
> use lp-propose (formerly lp-submit) and it will use the previous pipe to
> determine which files to lint.

That's what I used (at least for one of these proposals,) but I didn't
have the extra plugin installed.

> lib/canonical/launchpad/interfaces/_schema_circular_imports.py
> 43: 'IBranchTarget' imported but unused

Fixed.

> lib/lp/code/interfaces/branch.py
> 11: undefined name 'UnknownBranchTypeError' in __all__

Fixed.

> lib/lp/code/interfaces/webservice.py
> 8: 'BranchMergeProposalExists' imported but unused
> 9: 'BranchCreatorNotOwner' imported but unused
> 9: 'IBranchSet' imported but unused
> 9: 'IBranch' imported but unused
> 9: 'BranchExists' imported but unused
> 9: 'BranchCreatorNotMemberOfOwnerTeam' imported but unused
> 12: 'IBranchMergeProposal' imported but unused
> 13: 'IBranchSubscription' imported but unused
> 14: 'ICodeImport' imported but unused
> 15: 'ICodeReviewComment' imported but unused
> 16: 'ICodeReviewVoteReference' imported but unused
> 17: 'IStaticDiff' imported but unused
> 17: 'IDiff' imported but unused
> 17: 'IPreviewDiff' imported but unused

Noise. This file is purely for importing.

> lib/lp/registry/model/sourcepackage.py
> 684: local variable 'displayname' is assigned to but never used

Fixed.

> lib/lp/code/interfaces/branch.py
> 442: [C0322, IBranch.setOwner] Operator not preceded by a space
> title=_("The new owner of the branch."),
> ^
> schema=IPerson))
> @export_write_operation()
> def setOwner(new_owner, user):
> 451: [C0322, IBranch.setTarget] Operator not preceded by a space
> title=_("The project the branch belongs to."),
> ^
> schema=Interface, required=False), # Really IProduct
> source_package=Reference(
> title=_("The source package the branch belongs to."),
> schema=Interface, required=False)) # Really ISourcePackage
> @export_write_operation()
> def setTarget(user, project=None, source_package=None):
> 511: [C0322, IBranch.isPersonTrustedReviewer] Operator not preceded
> by a space
> title=_("A person for which the reviewer status is in question."),
> ^
> schema=IPerson))
> @export_read_operation()
> def isPersonTrustedReviewer(reviewer):
> 760: [C0322, IBranch._createMergeProposal] Operator not preceded by
> a space
> needs_review=Bool(title=_('Needs review'),
> ...

Read more...

Revision history for this message
Aaron Bentley (abentley) wrote :

On 04/07/2010 10:06 AM, James Westby wrote:
> On Tue, 06 Apr 2010 15:33:31 -0000, Aaron Bentley<email address hidden> wrote:
>> When there are no uncommitted changes, "make lint" compares your tree to
>> the parent branch to determine which files it runs against. This means
>> it will also lint files changed by your prerequisite branch. You parent
>> branch may also be out of date.
>
> I am using pipelines, so I would have thought it would run against the
> previous pipe?

No, I did not design it and it predates pipelines.

>> lib/lp/code/interfaces/webservice.py
>> 8: 'BranchMergeProposalExists' imported but unused
>> 9: 'BranchCreatorNotOwner' imported but unused
>> 9: 'IBranchSet' imported but unused
>> 9: 'IBranch' imported but unused
>> 9: 'BranchExists' imported but unused
>> 9: 'BranchCreatorNotMemberOfOwnerTeam' imported but unused
>> 12: 'IBranchMergeProposal' imported but unused
>> 13: 'IBranchSubscription' imported but unused
>> 14: 'ICodeImport' imported but unused
>> 15: 'ICodeReviewComment' imported but unused
>> 16: 'ICodeReviewVoteReference' imported but unused
>> 17: 'IStaticDiff' imported but unused
>> 17: 'IDiff' imported but unused
>> 17: 'IPreviewDiff' imported but unused
>
> Noise. This file is purely for importing.

It seems really weird to me. If it's there for external users, why isn't there an __all__? I'll chat with Leonard, but it doesn't need to block you.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'lib/canonical/launchpad/apidoc'
=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-04-09 15:46:09 +0000
+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-04-13 15:21:36 +0000
@@ -45,7 +45,7 @@
45from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference45from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
46from lp.code.interfaces.diff import IPreviewDiff46from lp.code.interfaces.diff import IPreviewDiff
47from lp.code.interfaces.hasbranches import (47from lp.code.interfaces.hasbranches import (
48 IHasBranches, IHasMergeProposals, IHasRequestedReviews)48 IHasBranches, IHasCodeImports, IHasMergeProposals, IHasRequestedReviews)
49from lp.code.interfaces.sourcepackagerecipebuild import (49from lp.code.interfaces.sourcepackagerecipebuild import (
50 ISourcePackageRecipeBuild)50 ISourcePackageRecipeBuild)
51from lp.registry.interfaces.distribution import IDistribution51from lp.registry.interfaces.distribution import IDistribution
@@ -130,6 +130,10 @@
130 IHasMergeProposals, 'getMergeProposals', IBranchMergeProposal)130 IHasMergeProposals, 'getMergeProposals', IBranchMergeProposal)
131patch_collection_return_type(131patch_collection_return_type(
132 IHasRequestedReviews, 'getRequestedReviews', IBranchMergeProposal)132 IHasRequestedReviews, 'getRequestedReviews', IBranchMergeProposal)
133patch_entry_return_type(
134 IHasCodeImports, 'newCodeImport', ICodeImport)
135patch_plain_parameter_type(
136 IHasCodeImports, 'newCodeImport', 'owner', IPerson)
133137
134# IBugTask138# IBugTask
135139
136140
=== modified file 'lib/lp/code/errors.py'
--- lib/lp/code/errors.py 2010-03-10 18:53:50 +0000
+++ lib/lp/code/errors.py 2010-04-13 15:21:36 +0000
@@ -41,6 +41,7 @@
4141
42class BranchMergeProposalExists(InvalidBranchMergeProposal):42class BranchMergeProposalExists(InvalidBranchMergeProposal):
43 """Raised if there is already a matching BranchMergeProposal."""43 """Raised if there is already a matching BranchMergeProposal."""
44
44 webservice_error(400) #Bad request.45 webservice_error(400) #Bad request.
4546
4647
4748
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2010-04-09 02:08:39 +0000
+++ lib/lp/code/interfaces/branch.py 2010-04-13 15:21:36 +0000
@@ -49,7 +49,8 @@
49 export_as_webservice_collection, export_as_webservice_entry,49 export_as_webservice_collection, export_as_webservice_entry,
50 export_destructor_operation, export_factory_operation,50 export_destructor_operation, export_factory_operation,
51 export_operation_as, export_read_operation, export_write_operation,51 export_operation_as, export_read_operation, export_write_operation,
52 exported, mutator_for, operation_parameters, operation_returns_entry)52 exported, mutator_for, operation_parameters, operation_returns_entry,
53 webservice_error)
5354
54from canonical.config import config55from canonical.config import config
5556
@@ -93,6 +94,8 @@
93class BranchExists(BranchCreationException):94class BranchExists(BranchCreationException):
94 """Raised when creating a branch that already exists."""95 """Raised when creating a branch that already exists."""
9596
97 webservice_error(400)
98
96 def __init__(self, existing_branch):99 def __init__(self, existing_branch):
97 # XXX: TimPenhey 2009-07-12 bug=405214: This error100 # XXX: TimPenhey 2009-07-12 bug=405214: This error
98 # message logic is incorrect, but the exact text is being tested101 # message logic is incorrect, but the exact text is being tested
@@ -135,6 +138,8 @@
135 the branch to a team that they are not a member of.138 the branch to a team that they are not a member of.
136 """139 """
137140
141 webservice_error(400)
142
138143
139class BranchCreationNoTeamOwnedJunkBranches(BranchCreationException):144class BranchCreationNoTeamOwnedJunkBranches(BranchCreationException):
140 """We forbid the creation of team-owned +junk branches.145 """We forbid the creation of team-owned +junk branches.
@@ -158,6 +163,8 @@
158 the branch to another user.163 the branch to another user.
159 """164 """
160165
166 webservice_error(400)
167
161168
162class BranchTypeError(Exception):169class BranchTypeError(Exception):
163 """An operation cannot be performed for a particular branch type.170 """An operation cannot be performed for a particular branch type.
164171
=== modified file 'lib/lp/code/interfaces/branchtarget.py'
--- lib/lp/code/interfaces/branchtarget.py 2010-04-09 02:08:39 +0000
+++ lib/lp/code/interfaces/branchtarget.py 2010-04-13 15:21:36 +0000
@@ -121,7 +121,7 @@
121 """Get the BugTask for a given bug related to the branch target."""121 """Get the BugTask for a given bug related to the branch target."""
122122
123 def newCodeImport(registrant, branch_name, rcs_type, url=None,123 def newCodeImport(registrant, branch_name, rcs_type, url=None,
124 cvs_root=None, cvs_module=None):124 cvs_root=None, cvs_module=None, owner=None):
125 """Create a new code import for this target.125 """Create a new code import for this target.
126126
127 :param registrant: the `IPerson` who should be recorded as creating127 :param registrant: the `IPerson` who should be recorded as creating
@@ -131,6 +131,8 @@
131 :param url: the url to import from if the import isn't CVS.131 :param url: the url to import from if the import isn't CVS.
132 :param cvs_root: if the import is from CVS the CVSROOT to import from.132 :param cvs_root: if the import is from CVS the CVSROOT to import from.
133 :param cvs_module: if the import is from CVS the module to import.133 :param cvs_module: if the import is from CVS the module to import.
134 :param owner: the `IPerson` to own the resulting branch, or None to
135 use registrant.
134 :returns: an `ICodeImport`.136 :returns: an `ICodeImport`.
135 :raises AssertionError: if supports_code_imports is False.137 :raises AssertionError: if supports_code_imports is False.
136 """138 """
137139
=== modified file 'lib/lp/code/interfaces/codeimport.py'
--- lib/lp/code/interfaces/codeimport.py 2010-03-26 02:24:29 +0000
+++ lib/lp/code/interfaces/codeimport.py 2010-04-13 15:21:36 +0000
@@ -182,10 +182,14 @@
182 """Interface representing the set of code imports."""182 """Interface representing the set of code imports."""
183183
184 def new(registrant, target, branch_name, rcs_type, url=None,184 def new(registrant, target, branch_name, rcs_type, url=None,
185 cvs_root=None, cvs_module=None, review_status=None):185 cvs_root=None, cvs_module=None, review_status=None,
186 owner=None):
186 """Create a new CodeImport.187 """Create a new CodeImport.
187188
188 :param target: An `IBranchTarget` that the code is associated with.189 :param target: An `IBranchTarget` that the code is associated with.
190 :param owner: The `IPerson` to set as the owner of the branch, or
191 None to use registrant. registrant must be a member of owner to
192 do this.
189 """193 """
190194
191 def get(id):195 def get(id):
192196
=== modified file 'lib/lp/code/interfaces/hasbranches.py'
--- lib/lp/code/interfaces/hasbranches.py 2010-02-08 14:37:50 +0000
+++ lib/lp/code/interfaces/hasbranches.py 2010-04-13 15:21:36 +0000
@@ -1,25 +1,30 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=E0213
5
4"""Interface definitions for IHas<code related bits>."""6"""Interface definitions for IHas<code related bits>."""
57
6__metaclass__ = type8__metaclass__ = type
7__all__ = [9__all__ = [
8 'IHasBranches',10 'IHasBranches',
11 'IHasCodeImports',
9 'IHasMergeProposals',12 'IHasMergeProposals',
10 'IHasRequestedReviews',13 'IHasRequestedReviews',
11 ]14 ]
1215
1316
14from zope.interface import Interface17from zope.interface import Interface
15from zope.schema import Choice, Datetime, List18from zope.schema import Choice, Datetime, List, TextLine
1619
17from canonical.launchpad import _20from canonical.launchpad import _
18from lp.code.enums import BranchLifecycleStatus, BranchMergeProposalStatus21from lp.code.enums import (
22 BranchLifecycleStatus, BranchMergeProposalStatus, RevisionControlSystems)
1923
20from lazr.restful.declarations import (24from lazr.restful.declarations import (
21 REQUEST_USER, call_with, export_read_operation, operation_parameters,25 REQUEST_USER, call_with, export_factory_operation, export_read_operation,
22 operation_returns_collection_of)26 operation_parameters, operation_returns_collection_of)
27from lazr.restful.fields import Reference
2328
2429
25class IHasBranches(Interface):30class IHasBranches(Interface):
@@ -98,7 +103,7 @@
98 @operation_returns_collection_of(Interface) # Really IBranchMergeProposal.103 @operation_returns_collection_of(Interface) # Really IBranchMergeProposal.
99 @export_read_operation()104 @export_read_operation()
100 def getRequestedReviews(status=None, visible_by_user=None):105 def getRequestedReviews(status=None, visible_by_user=None):
101 """Returns merge proposals that a review was requested from the person.106 """Returns merge proposals where a person was asked to review.
102107
103 This does not include merge proposals that were requested from108 This does not include merge proposals that were requested from
104 teams that the person is part of. If status is not passed then109 teams that the person is part of. If status is not passed then
@@ -108,3 +113,42 @@
108 :param visible_by_user: Normally the user who is asking.113 :param visible_by_user: Normally the user who is asking.
109 :returns: A list of `IBranchMergeProposal`.114 :returns: A list of `IBranchMergeProposal`.
110 """115 """
116
117
118class IHasCodeImports(Interface):
119 """Some things can have code imports that target them.
120
121 This interface defines the common methods that for working with them.
122 """
123
124 # In order to minimise dependancies the returns_collection is defined as
125 # Interface here and defined fully in the circular imports file.
126
127 @operation_parameters(
128 branch_name=TextLine(
129 title=_('Name of branch to create'), required=True),
130 rcs_type=Choice(vocabulary=RevisionControlSystems, required=True),
131 url=TextLine(title=_('Foreign VCS URL')),
132 cvs_root=TextLine(title=_('CVS root URL')),
133 cvs_module=TextLine(title=_('CVS module to import')),
134 owner=Reference(title=_('Owner of the resulting branch'),
135 schema=Interface)
136 )
137 @call_with(registrant=REQUEST_USER)
138 @export_factory_operation(Interface, []) # Really ICodeImport.
139 def newCodeImport(registrant=None, branch_name=None, rcs_type=None,
140 url=None, cvs_root=None, cvs_module=None, owner=None):
141 """Create a new code import.
142
143 :param registrant: The IPerson to record as the registrant of the
144 import
145 :param branch_name: The name of the branch to create.
146 :param rcs_type: The type of the foreign VCS.
147 :param url: The URL to import from if the VCS type uses a single URL
148 (i.e. isn't CVS).
149 :param cvs_root: The CVSROOT for a CVS import.
150 :param cvs_module: The module to import for a CVS import.
151 :param owner: Who should own the created branch, or None for it to
152 be the same as the registrant, or the caller over the API.
153 :returns: An instance of `ICodeImport`.
154 """
111155
=== modified file 'lib/lp/code/interfaces/webservice.py'
--- lib/lp/code/interfaces/webservice.py 2010-03-16 19:04:48 +0000
+++ lib/lp/code/interfaces/webservice.py 2010-04-13 15:21:36 +0000
@@ -3,7 +3,12 @@
33
4"""All the interfaces that are exposed through the webservice."""4"""All the interfaces that are exposed through the webservice."""
55
6from lp.code.interfaces.branch import IBranch, IBranchSet6# The exceptions are imported so that they can produce the special
7# status code defined by webservice_error when they are raised.
8from lp.code.errors import BranchMergeProposalExists
9from lp.code.interfaces.branch import (
10 IBranch, IBranchSet, BranchCreatorNotMemberOfOwnerTeam,
11 BranchCreatorNotOwner, BranchExists)
7from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal12from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
8from lp.code.interfaces.branchsubscription import IBranchSubscription13from lp.code.interfaces.branchsubscription import IBranchSubscription
9from lp.code.interfaces.codeimport import ICodeImport14from lp.code.interfaces.codeimport import ICodeImport
1015
=== modified file 'lib/lp/code/mail/tests/test_codeimport.py'
--- lib/lp/code/mail/tests/test_codeimport.py 2010-04-13 15:21:34 +0000
+++ lib/lp/code/mail/tests/test_codeimport.py 2010-04-13 15:21:36 +0000
@@ -118,7 +118,7 @@
118 series = self.factory.makeDistroSeries(118 series = self.factory.makeDistroSeries(
119 name='manic', distribution=distro)119 name='manic', distribution=distro)
120 fooix = self.factory.makeSourcePackage(120 fooix = self.factory.makeSourcePackage(
121 sourcename='fooix', distroseries=series)121 sourcepackagename='fooix', distroseries=series)
122 # Eric needs to be logged in for the mail to be sent.122 # Eric needs to be logged in for the mail to be sent.
123 login_person(eric)123 login_person(eric)
124 code_import = self.factory.makePackageCodeImport(124 code_import = self.factory.makePackageCodeImport(
125125
=== modified file 'lib/lp/code/model/branchtarget.py'
--- lib/lp/code/model/branchtarget.py 2010-04-13 15:21:34 +0000
+++ lib/lp/code/model/branchtarget.py 2010-04-13 15:21:36 +0000
@@ -37,10 +37,11 @@
37 return self.context != other.context37 return self.context != other.context
3838
39 def newCodeImport(self, registrant, branch_name, rcs_type, url=None,39 def newCodeImport(self, registrant, branch_name, rcs_type, url=None,
40 cvs_root=None, cvs_module=None):40 cvs_root=None, cvs_module=None, owner=None):
41 """See `IBranchTarget`."""
41 return getUtility(ICodeImportSet).new(42 return getUtility(ICodeImportSet).new(
42 registrant, self, branch_name, rcs_type, url=url,43 registrant, self, branch_name, rcs_type, url=url,
43 cvs_root=cvs_root, cvs_module=cvs_module)44 cvs_root=cvs_root, cvs_module=cvs_module, owner=owner)
4445
4546
46class PackageBranchTarget(_BaseBranchTarget):47class PackageBranchTarget(_BaseBranchTarget):
@@ -336,6 +337,23 @@
336 branch.sourcepackagename = None337 branch.sourcepackagename = None
337338
338339
340class ProductSeriesBranchTarget(ProductBranchTarget):
341
342 def __init__(self, productseries):
343 ProductBranchTarget.__init__(self, productseries.product)
344 self.productseries = productseries
345
346 @property
347 def context(self):
348 """See `IBranchTarget`."""
349 return self.productseries
350
351 @property
352 def supports_code_imports(self):
353 """See `IBranchTarget`."""
354 return False
355
356
339def get_canonical_url_data_for_target(branch_target):357def get_canonical_url_data_for_target(branch_target):
340 """Return the `ICanonicalUrlData` for an `IBranchTarget`."""358 """Return the `ICanonicalUrlData` for an `IBranchTarget`."""
341 return ICanonicalUrlData(branch_target.context)359 return ICanonicalUrlData(branch_target.context)
342360
=== modified file 'lib/lp/code/model/codeimport.py'
--- lib/lp/code/model/codeimport.py 2010-03-26 01:41:16 +0000
+++ lib/lp/code/model/codeimport.py 2010-04-13 15:21:36 +0000
@@ -203,7 +203,8 @@
203 implements(ICodeImportSet)203 implements(ICodeImportSet)
204204
205 def new(self, registrant, target, branch_name, rcs_type,205 def new(self, registrant, target, branch_name, rcs_type,
206 url=None, cvs_root=None, cvs_module=None, review_status=None):206 url=None, cvs_root=None, cvs_module=None, review_status=None,
207 owner=None):
207 """See `ICodeImportSet`."""208 """See `ICodeImportSet`."""
208 if rcs_type == RevisionControlSystems.CVS:209 if rcs_type == RevisionControlSystems.CVS:
209 assert cvs_root is not None and cvs_module is not None210 assert cvs_root is not None and cvs_module is not None
@@ -227,14 +228,16 @@
227 review_status = CodeImportReviewStatus.NEW228 review_status = CodeImportReviewStatus.NEW
228 if not target.supports_code_imports:229 if not target.supports_code_imports:
229 raise AssertionError("%r doesn't support code imports" % target)230 raise AssertionError("%r doesn't support code imports" % target)
231 if owner is None:
232 owner = registrant
230 # Create the branch for the CodeImport.233 # Create the branch for the CodeImport.
231 namespace = target.getNamespace(registrant)234 namespace = target.getNamespace(owner)
232 import_branch = namespace.createBranch(235 import_branch = namespace.createBranch(
233 branch_type=BranchType.IMPORTED, name=branch_name,236 branch_type=BranchType.IMPORTED, name=branch_name,
234 registrant=registrant)237 registrant=registrant)
235238
236 code_import = CodeImport(239 code_import = CodeImport(
237 registrant=registrant, owner=registrant, branch=import_branch,240 registrant=registrant, owner=owner, branch=import_branch,
238 rcs_type=rcs_type, url=url,241 rcs_type=rcs_type, url=url,
239 cvs_root=cvs_root, cvs_module=cvs_module,242 cvs_root=cvs_root, cvs_module=cvs_module,
240 review_status=review_status)243 review_status=review_status)
241244
=== modified file 'lib/lp/code/model/hasbranches.py'
--- lib/lp/code/model/hasbranches.py 2010-03-09 17:30:18 +0000
+++ lib/lp/code/model/hasbranches.py 2010-04-13 15:21:36 +0000
@@ -6,6 +6,7 @@
6__metaclass__ = type6__metaclass__ = type
7__all__ = [7__all__ = [
8 'HasBranchesMixin',8 'HasBranchesMixin',
9 'HasCodeImportsMixin',
9 'HasMergeProposalsMixin',10 'HasMergeProposalsMixin',
10 'HasRequestedReviewsMixin',11 'HasRequestedReviewsMixin',
11 ]12 ]
@@ -16,6 +17,7 @@
16from lp.code.interfaces.branch import DEFAULT_BRANCH_STATUS_IN_LISTING17from lp.code.interfaces.branch import DEFAULT_BRANCH_STATUS_IN_LISTING
17from lp.code.interfaces.branchcollection import (18from lp.code.interfaces.branchcollection import (
18 IAllBranches, IBranchCollection)19 IAllBranches, IBranchCollection)
20from lp.code.interfaces.branchtarget import IBranchTarget
1921
2022
21class HasBranchesMixin:23class HasBranchesMixin:
@@ -59,5 +61,17 @@
5961
60 visible_branches = getUtility(IAllBranches).visibleByUser(62 visible_branches = getUtility(IAllBranches).visibleByUser(
61 visible_by_user)63 visible_by_user)
62 proposals = visible_branches.getMergeProposalsForReviewer(self, status)64 proposals = visible_branches.getMergeProposalsForReviewer(
65 self, status)
63 return proposals66 return proposals
67
68
69class HasCodeImportsMixin:
70
71 def newCodeImport(self, registrant=None, branch_name=None,
72 rcs_type=None, url=None, cvs_root=None, cvs_module=None,
73 owner=None):
74 """See `IHasCodeImports`."""
75 return IBranchTarget(self).newCodeImport(registrant, branch_name,
76 rcs_type, url=url, cvs_root=cvs_root, cvs_module=cvs_module,
77 owner=owner)
6478
=== modified file 'lib/lp/code/model/tests/test_codeimport.py'
--- lib/lp/code/model/tests/test_codeimport.py 2010-04-13 15:21:34 +0000
+++ lib/lp/code/model/tests/test_codeimport.py 2010-04-13 15:21:36 +0000
@@ -16,6 +16,7 @@
16from lp.code.model.codeimportevent import CodeImportEvent16from lp.code.model.codeimportevent import CodeImportEvent
17from lp.code.model.codeimportjob import CodeImportJob, CodeImportJobSet17from lp.code.model.codeimportjob import CodeImportJob, CodeImportJobSet
18from lp.code.model.codeimportresult import CodeImportResult18from lp.code.model.codeimportresult import CodeImportResult
19from lp.code.interfaces.branch import BranchCreatorNotMemberOfOwnerTeam
19from lp.code.interfaces.branchtarget import IBranchTarget20from lp.code.interfaces.branchtarget import IBranchTarget
20from lp.registry.interfaces.person import IPersonSet21from lp.registry.interfaces.person import IPersonSet
21from lp.code.enums import (22from lp.code.enums import (
@@ -155,6 +156,45 @@
155 # And a job is still created156 # And a job is still created
156 self.assertIsNot(None, code_import.import_job)157 self.assertIsNot(None, code_import.import_job)
157158
159 def test_set_owner(self):
160 """Test that we can create an import owned by someone else."""
161 registrant = self.factory.makePerson()
162 owner = self.factory.makeTeam()
163 removeSecurityProxy(registrant).join(owner)
164 source_package = self.factory.makeSourcePackage()
165 target = IBranchTarget(source_package)
166 code_import = CodeImportSet().new(
167 registrant=registrant,
168 target=target,
169 branch_name='imported',
170 rcs_type=RevisionControlSystems.HG,
171 url=self.factory.getUniqueURL(),
172 review_status=None, owner=owner)
173 code_import = removeSecurityProxy(code_import)
174 self.assertEqual(registrant, code_import.registrant)
175 self.assertEqual(owner, code_import.branch.owner)
176 self.assertEqual(registrant, code_import.branch.registrant)
177 self.assertEqual(target, code_import.branch.target)
178 self.assertEqual(source_package, code_import.branch.sourcepackage)
179 # And a job is still created
180 self.assertIsNot(None, code_import.import_job)
181
182 def test_registrant_must_be_in_owner(self):
183 """Test that we can't create an import for an arbitrary team."""
184 registrant = self.factory.makePerson()
185 owner = self.factory.makeTeam()
186 source_package = self.factory.makeSourcePackage()
187 target = IBranchTarget(source_package)
188 self.assertRaises(
189 BranchCreatorNotMemberOfOwnerTeam,
190 CodeImportSet().new,
191 registrant=registrant,
192 target=target,
193 branch_name='imported',
194 rcs_type=RevisionControlSystems.HG,
195 url=self.factory.getUniqueURL(),
196 review_status=None, owner=owner)
197
158198
159class TestCodeImportDeletion(TestCaseWithFactory):199class TestCodeImportDeletion(TestCaseWithFactory):
160 """Test the deletion of CodeImports."""200 """Test the deletion of CodeImports."""
161201
=== modified file 'lib/lp/code/stories/webservice/xx-branchmergeproposal.txt'
--- lib/lp/code/stories/webservice/xx-branchmergeproposal.txt 2010-04-12 15:53:56 +0000
+++ lib/lp/code/stories/webservice/xx-branchmergeproposal.txt 2010-04-13 15:21:36 +0000
@@ -84,7 +84,7 @@
84 ... initial_comment='Merge\nit!', needs_review=True,84 ... initial_comment='Merge\nit!', needs_review=True,
85 ... commit_message='It was merged!\n', reviewers=[reviewer_url],85 ... commit_message='It was merged!\n', reviewers=[reviewer_url],
86 ... review_types=['green'])86 ... review_types=['green'])
87 HTTP/1.1 500 Internal Server Error ...87 HTTP/1.1 400 Bad Request ...
88 BranchMergeProposalExists: There is already a branch merge proposal88 BranchMergeProposalExists: There is already a branch merge proposal
89 registered for branch ... to land on ... that is still active.89 registered for branch ... to land on ... that is still active.
9090
9191
=== modified file 'lib/lp/code/stories/webservice/xx-code-import.txt'
--- lib/lp/code/stories/webservice/xx-code-import.txt 2010-04-13 15:21:34 +0000
+++ lib/lp/code/stories/webservice/xx-code-import.txt 2010-04-13 15:21:36 +0000
@@ -5,24 +5,33 @@
5if it is an import branch.5if it is an import branch.
66
7 >>> from zope.security.proxy import removeSecurityProxy7 >>> from zope.security.proxy import removeSecurityProxy
8 >>> from canonical.launchpad.testing.pages import webservice_for_person
9 >>> from canonical.launchpad.webapp.interfaces import OAuthPermission
810
9First we create some objects for use in the tests.11First we create some objects for use in the tests.
1012
11 >>> login(ANONYMOUS)13 >>> login(ANONYMOUS)
12 >>> person = factory.makePerson(name='import-owner')14 >>> person = factory.makePerson(name='import-owner')
15 >>> team = factory.makeTeam(name='import-owner-team')
16 >>> other_team = factory.makeTeam(name='other-team')
17 >>> other_person = factory.makePerson(name='other-person')
18 >>> removeSecurityProxy(person).join(team)
13 >>> product = factory.makeProduct(name='scruff')19 >>> product = factory.makeProduct(name='scruff')
20 >>> svn_branch_url = "http://svn.domain.com/source"
14 >>> code_import = removeSecurityProxy(factory.makeProductCodeImport(21 >>> code_import = removeSecurityProxy(factory.makeProductCodeImport(
15 ... registrant=person, product=product, branch_name='import',22 ... registrant=person, product=product, branch_name='import',
16 ... svn_branch_url="http://svn.domain.com/source"))23 ... svn_branch_url=svn_branch_url))
17 >>> no_import_branch = removeSecurityProxy(factory.makeProductBranch(24 >>> no_import_branch = removeSecurityProxy(factory.makeProductBranch(
18 ... owner=person, product=product, name='no-import'))25 ... owner=person, product=product, name='no-import'))
19 >>> logout()26 >>> logout()
27 >>> import_webservice = webservice_for_person(
28 ... person, permission=OAuthPermission.WRITE_PUBLIC)
2029
21If we query a branch with no import then we find that it tells us30If we query a branch with no import then we find that it tells us
22it doesn't have one.31it doesn't have one.
2332
24 >>> branch_url = '/' + no_import_branch.unique_name33 >>> branch_url = '/' + no_import_branch.unique_name
25 >>> response = webservice.get(branch_url)34 >>> response = import_webservice.get(branch_url)
26 >>> representation = response.jsonBody()35 >>> representation = response.jsonBody()
27 >>> print representation['code_import_link']36 >>> print representation['code_import_link']
28 None37 None
@@ -31,7 +40,7 @@
31representation.40representation.
3241
33 >>> branch_url = '/' + code_import.branch.unique_name42 >>> branch_url = '/' + code_import.branch.unique_name
34 >>> response = webservice.get(branch_url)43 >>> response = import_webservice.get(branch_url)
35 >>> representation = response.jsonBody()44 >>> representation = response.jsonBody()
36 >>> print representation['code_import_link']45 >>> print representation['code_import_link']
37 http://.../~import-owner/scruff/import/+code-import46 http://.../~import-owner/scruff/import/+code-import
@@ -39,7 +48,7 @@
39We can get some information about the import using this URL.48We can get some information about the import using this URL.
4049
41 >>> import_url = representation['code_import_link']50 >>> import_url = representation['code_import_link']
42 >>> response = webservice.get(import_url)51 >>> response = import_webservice.get(import_url)
43 >>> representation = response.jsonBody()52 >>> representation = response.jsonBody()
44 >>> print representation['self_link'] == import_url53 >>> print representation['self_link'] == import_url
45 True54 True
@@ -69,17 +78,19 @@
69 >>> distroseries = factory.makeDistroSeries(78 >>> distroseries = factory.makeDistroSeries(
70 ... name='manic', distribution=distribution)79 ... name='manic', distribution=distribution)
71 >>> source_package = factory.makeSourcePackage(80 >>> source_package = factory.makeSourcePackage(
72 ... sourcename='scruff', distroseries=distroseries)81 ... sourcepackagename='scruff', distroseries=distroseries)
73 >>> code_import = removeSecurityProxy(factory.makePackageCodeImport(82 >>> code_import = removeSecurityProxy(factory.makePackageCodeImport(
74 ... registrant=person, sourcepackage=source_package,83 ... registrant=person, sourcepackage=source_package,
75 ... branch_name='import',84 ... branch_name='import',
76 ... svn_branch_url="http://svn.domain.com/package_source"))85 ... svn_branch_url="http://svn.domain.com/package_source"))
77 >>> logout()86 >>> logout()
87 >>> import_webservice = webservice_for_person(
88 ... person, permission=OAuthPermission.WRITE_PUBLIC)
7889
79There is a link on the branch object90There is a link on the branch object
8091
81 >>> branch_url = '/' + code_import.branch.unique_name92 >>> branch_url = '/' + code_import.branch.unique_name
82 >>> response = webservice.get(branch_url)93 >>> response = import_webservice.get(branch_url)
83 >>> representation = response.jsonBody()94 >>> representation = response.jsonBody()
84 >>> print representation['code_import_link']95 >>> print representation['code_import_link']
85 http://.../~import-owner/scruffbuntu/manic/scruff/import/+code-import96 http://.../~import-owner/scruffbuntu/manic/scruff/import/+code-import
@@ -87,7 +98,7 @@
87and there is information available about the import itsef.98and there is information available about the import itsef.
8899
89 >>> import_url = representation['code_import_link']100 >>> import_url = representation['code_import_link']
90 >>> response = webservice.get(import_url)101 >>> response = import_webservice.get(import_url)
91 >>> representation = response.jsonBody()102 >>> representation = response.jsonBody()
92 >>> print representation['self_link'] == import_url103 >>> print representation['self_link'] == import_url
93 True104 True
@@ -106,3 +117,114 @@
106 >>> print representation['date_last_successful']117 >>> print representation['date_last_successful']
107 None118 None
108119
120== Creating Imports ==
121
122We can create an import using the API by calling a method on the project.
123
124 >>> product_url = '/' + product.name
125 >>> new_remote_url = factory.getUniqueURL()
126 >>> response = import_webservice.named_post(product_url, 'newCodeImport',
127 ... branch_name='new-import', rcs_type='Git',
128 ... url=new_remote_url)
129 >>> print response.status
130 201
131 >>> location = response.getHeader('Location')
132 >>> response = import_webservice.get(location)
133 >>> representation = response.jsonBody()
134 >>> print representation['self_link']
135 http://.../~import-owner/scruff/new-import/+code-import
136 >>> print representation['branch_link']
137 http://.../~import-owner/scruff/new-import
138 >>> print representation['rcs_type']
139 Git
140 >>> print representation['url'] == new_remote_url
141 True
142 >>> print representation['cvs_root']
143 None
144 >>> print representation['cvs_module']
145 None
146 >>> print representation['date_last_successful']
147 None
148
149If we must we can create a CVS import.
150
151 >>> product_url = '/' + product.name
152 >>> new_remote_url = factory.getUniqueURL()
153 >>> response = import_webservice.named_post(product_url, 'newCodeImport',
154 ... branch_name='cvs-import', rcs_type='Concurrent Versions System',
155 ... cvs_root=new_remote_url, cvs_module="foo")
156 >>> print response.status
157 201
158 >>> location = response.getHeader('Location')
159 >>> response = import_webservice.get(location)
160 >>> representation = response.jsonBody()
161 >>> print representation['self_link']
162 http://.../~import-owner/scruff/cvs-import/+code-import
163 >>> print representation['branch_link']
164 http://.../~import-owner/scruff/cvs-import
165 >>> print representation['rcs_type']
166 Concurrent Versions System
167 >>> print representation['url']
168 None
169 >>> print representation['cvs_root'] == new_remote_url
170 True
171 >>> print representation['cvs_module'] == "foo"
172 True
173 >>> print representation['date_last_successful']
174 None
175
176We can also create an import targetting a source package.
177
178 >>> source_package_url = (
179 ... '/' + distribution.name + '/' + distroseries.name + '/+source/'
180 ... + source_package.name)
181 >>> new_remote_url = factory.getUniqueURL()
182 >>> response = import_webservice.named_post(source_package_url,
183 ... 'newCodeImport', branch_name='new-import', rcs_type='Mercurial',
184 ... url=new_remote_url)
185 >>> print response.status
186 201
187 >>> location = response.getHeader('Location')
188 >>> response = import_webservice.get(location)
189 >>> representation = response.jsonBody()
190 >>> print representation['self_link']
191 http://.../~import-owner/scruffbuntu/manic/scruff/new-import/+code-import
192 >>> print representation['branch_link']
193 http://.../~import-owner/scruffbuntu/manic/scruff/new-import
194 >>> print representation['rcs_type']
195 Mercurial
196 >>> print representation['url'] == new_remote_url
197 True
198 >>> print representation['cvs_root']
199 None
200 >>> print representation['cvs_module']
201 None
202 >>> print representation['date_last_successful']
203 None
204
205If we wish to create a branch owned by a team we are part of then we can.
206
207 >>> team_url = import_webservice.getAbsoluteUrl('/~import-owner-team')
208 >>> new_remote_url = factory.getUniqueURL()
209 >>> response = import_webservice.named_post(product_url, 'newCodeImport',
210 ... branch_name='team-import', rcs_type='Git',
211 ... url=new_remote_url, owner=team_url)
212 >>> print response.status
213 201
214 >>> location = response.getHeader('Location')
215 >>> response = import_webservice.get(location)
216 >>> representation = response.jsonBody()
217 >>> print representation['self_link']
218 http://.../~import-owner-team/scruff/team-import/+code-import
219 >>> print representation['branch_link']
220 http://.../~import-owner-team/scruff/team-import
221 >>> print representation['rcs_type']
222 Git
223 >>> print representation['url'] == new_remote_url
224 True
225 >>> print representation['cvs_root']
226 None
227 >>> print representation['cvs_module']
228 None
229 >>> print representation['date_last_successful']
230 None
109231
=== modified file 'lib/lp/registry/interfaces/product.py'
--- lib/lp/registry/interfaces/product.py 2010-04-07 19:23:28 +0000
+++ lib/lp/registry/interfaces/product.py 2010-04-13 15:21:36 +0000
@@ -43,7 +43,8 @@
43from lp.app.interfaces.headings import IRootContext43from lp.app.interfaces.headings import IRootContext
44from lp.code.interfaces.branchvisibilitypolicy import (44from lp.code.interfaces.branchvisibilitypolicy import (
45 IHasBranchVisibilityPolicy)45 IHasBranchVisibilityPolicy)
46from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals46from lp.code.interfaces.hasbranches import (
47 IHasBranches, IHasCodeImports, IHasMergeProposals)
47from lp.code.interfaces.hasrecipes import IHasRecipes48from lp.code.interfaces.hasrecipes import IHasRecipes
48from lp.bugs.interfaces.bugtarget import (49from lp.bugs.interfaces.bugtarget import (
49 IBugTarget, IOfficialBugTagTargetPublic, IOfficialBugTagTargetRestricted)50 IBugTarget, IOfficialBugTagTargetPublic, IOfficialBugTagTargetRestricted)
@@ -339,7 +340,8 @@
339 IHasLogo, IHasMentoringOffers, IHasMergeProposals, IHasMilestones,340 IHasLogo, IHasMentoringOffers, IHasMergeProposals, IHasMilestones,
340 IHasMugshot, IHasOwner, IHasSecurityContact, IHasSprints,341 IHasMugshot, IHasOwner, IHasSecurityContact, IHasSprints,
341 ITranslationPolicy, IKarmaContext, ILaunchpadUsage, IMakesAnnouncements,342 ITranslationPolicy, IKarmaContext, ILaunchpadUsage, IMakesAnnouncements,
342 IOfficialBugTagTargetPublic, IPillar, ISpecificationTarget, IHasRecipes):343 IOfficialBugTagTargetPublic, IPillar, ISpecificationTarget, IHasRecipes,
344 IHasCodeImports):
343 """Public IProduct properties."""345 """Public IProduct properties."""
344346
345 # XXX Mark Shuttleworth 2004-10-12: Let's get rid of ID's in interfaces347 # XXX Mark Shuttleworth 2004-10-12: Let's get rid of ID's in interfaces
346348
=== modified file 'lib/lp/registry/interfaces/sourcepackage.py'
--- lib/lp/registry/interfaces/sourcepackage.py 2010-03-02 15:30:18 +0000
+++ lib/lp/registry/interfaces/sourcepackage.py 2010-04-13 15:21:36 +0000
@@ -22,7 +22,8 @@
2222
23from canonical.launchpad import _23from canonical.launchpad import _
24from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags24from lp.bugs.interfaces.bugtarget import IBugTarget, IHasOfficialBugTags
25from lp.code.interfaces.hasbranches import IHasBranches, IHasMergeProposals25from lp.code.interfaces.hasbranches import (
26 IHasBranches, IHasCodeImports, IHasMergeProposals)
26from lp.soyuz.interfaces.component import IComponent27from lp.soyuz.interfaces.component import IComponent
27from lazr.restful.fields import Reference, ReferenceChoice28from lazr.restful.fields import Reference, ReferenceChoice
28from lazr.restful.declarations import (29from lazr.restful.declarations import (
@@ -32,7 +33,7 @@
3233
3334
34class ISourcePackage(IBugTarget, IHasBranches, IHasMergeProposals,35class ISourcePackage(IBugTarget, IHasBranches, IHasMergeProposals,
35 IHasOfficialBugTags):36 IHasOfficialBugTags, IHasCodeImports):
36 """A SourcePackage. See the MagicSourcePackage specification. This37 """A SourcePackage. See the MagicSourcePackage specification. This
37 interface preserves as much as possible of the old SourcePackage38 interface preserves as much as possible of the old SourcePackage
38 interface from the SourcePackage table, with the new table-less39 interface from the SourcePackage table, with the new table-less
3940
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2010-04-09 19:11:36 +0000
+++ lib/lp/registry/model/product.py 2010-04-13 15:21:36 +0000
@@ -33,7 +33,8 @@
33from canonical.launchpad.interfaces.lpstorm import IStore33from canonical.launchpad.interfaces.lpstorm import IStore
34from lp.code.model.branchvisibilitypolicy import (34from lp.code.model.branchvisibilitypolicy import (
35 BranchVisibilityPolicyMixin)35 BranchVisibilityPolicyMixin)
36from lp.code.model.hasbranches import HasBranchesMixin, HasMergeProposalsMixin36from lp.code.model.hasbranches import (
37 HasBranchesMixin, HasCodeImportsMixin, HasMergeProposalsMixin)
37from lp.code.model.sourcepackagerecipe import SourcePackageRecipe38from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
38from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData39from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData
39from lp.bugs.interfaces.bugtarget import IHasBugHeat40from lp.bugs.interfaces.bugtarget import IHasBugHeat
@@ -173,8 +174,7 @@
173 HasAliasMixin, StructuralSubscriptionTargetMixin,174 HasAliasMixin, StructuralSubscriptionTargetMixin,
174 HasMilestonesMixin, OfficialBugTagTargetMixin, HasBranchesMixin,175 HasMilestonesMixin, OfficialBugTagTargetMixin, HasBranchesMixin,
175 HasCustomLanguageCodesMixin, HasMergeProposalsMixin,176 HasCustomLanguageCodesMixin, HasMergeProposalsMixin,
176 HasBugHeatMixin):177 HasBugHeatMixin, HasCodeImportsMixin):
177
178 """A Product."""178 """A Product."""
179179
180 implements(180 implements(
181181
=== modified file 'lib/lp/registry/model/sourcepackage.py'
--- lib/lp/registry/model/sourcepackage.py 2010-04-12 08:29:02 +0000
+++ lib/lp/registry/model/sourcepackage.py 2010-04-13 15:21:36 +0000
@@ -23,7 +23,8 @@
23from canonical.lazr.utils import smartquote23from canonical.lazr.utils import smartquote
24from lp.buildmaster.interfaces.buildbase import BuildStatus24from lp.buildmaster.interfaces.buildbase import BuildStatus
25from lp.code.model.branch import Branch25from lp.code.model.branch import Branch
26from lp.code.model.hasbranches import HasBranchesMixin, HasMergeProposalsMixin26from lp.code.model.hasbranches import (
27 HasBranchesMixin, HasCodeImportsMixin, HasMergeProposalsMixin)
27from lp.bugs.interfaces.bugtarget import IHasBugHeat28from lp.bugs.interfaces.bugtarget import IHasBugHeat
28from lp.bugs.model.bug import get_bug_tags_open_count29from lp.bugs.model.bug import get_bug_tags_open_count
29from lp.bugs.model.bugtarget import BugTargetBase, HasBugHeatMixin30from lp.bugs.model.bugtarget import BugTargetBase, HasBugHeatMixin
@@ -160,7 +161,7 @@
160class SourcePackage(BugTargetBase, SourcePackageQuestionTargetMixin,161class SourcePackage(BugTargetBase, SourcePackageQuestionTargetMixin,
161 HasTranslationImportsMixin, HasTranslationTemplatesMixin,162 HasTranslationImportsMixin, HasTranslationTemplatesMixin,
162 HasBranchesMixin, HasMergeProposalsMixin,163 HasBranchesMixin, HasMergeProposalsMixin,
163 HasBugHeatMixin):164 HasBugHeatMixin, HasCodeImportsMixin):
164 """A source package, e.g. apache2, in a distroseries.165 """A source package, e.g. apache2, in a distroseries.
165166
166 This object is not a true database object, but rather attempts to167 This object is not a true database object, but rather attempts to
@@ -681,7 +682,6 @@
681 our_format = PackageUploadCustomFormat.ROSETTA_TRANSLATIONS682 our_format = PackageUploadCustomFormat.ROSETTA_TRANSLATIONS
682683
683 packagename = self.sourcepackagename.name684 packagename = self.sourcepackagename.name
684 displayname = self.displayname
685 distro = self.distroseries.distribution685 distro = self.distroseries.distribution
686686
687 histories = distro.main_archive.getPublishedSources(687 histories = distro.main_archive.getPublishedSources(