Merge lp:~stevenk/launchpad/branch-use-information_type into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 15289
Proposed branch: lp:~stevenk/launchpad/branch-use-information_type
Merge into: lp:launchpad
Diff against target: 355 lines (+102/-34)
9 files modified
lib/lp/code/browser/tests/test_branch.py (+6/-3)
lib/lp/code/browser/tests/test_branchmergeproposal.py (+2/-1)
lib/lp/code/errors.py (+5/-0)
lib/lp/code/model/branch.py (+32/-11)
lib/lp/code/model/tests/test_branch.py (+28/-2)
lib/lp/code/model/tests/test_branchmergeproposal.py (+3/-2)
lib/lp/code/model/tests/test_branchvisibility.py (+4/-7)
lib/lp/code/xmlrpc/tests/test_branch.py (+4/-2)
lib/lp/testing/factory.py (+18/-6)
To merge this branch: bzr merge lp:~stevenk/launchpad/branch-use-information_type
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+106109@code.launchpad.net

Commit message

Switch Branch to using information_type by default.

Description of the change

Switch Branch to using information_type by default. I have also moved the code from setPrivate to transitionToInformationType and made setPrivate a wrapper around it. Changing the information_type of a branch that is stacked on a non-public branch is now forbidden. IBranch.branchChanged() now forces the branch that is being stacked on top of an existing branch will have its information_type set to the stacked on information_type if it's not public.

I have changed the factory method makeBranch to take an information_type argument, and have also made it call IBranch.branchChanged() if stacked_on is set, which more closely mirrors what happens in production.

This necessitated changing a fair number of tests, which naively just set explicitly_private by hand. I've converted them to setting information_type, but am willing to make use of IBranch.setPrivate() or IBranch.transistionToInformationType() instead.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

32 +def convert_to_information_type(private):
33 + if private:
34 + return InformationType.USERDATA
35 + else:
36 + return InformationType.PUBLIC

I'd put this in model rather than adapters. It's not useful outside model.

101 +class BranchCannotChangeInformationType(Exception):
102 + """The branch cannot change its information type."""

Of course it can't -- a branch isn't an actor. Do you mean that its information type can't be changed?

116 +from lazr.restful.interface import copy_field

Unused?

159 + @property
160 def private(self):
161 - return self.transitively_private
162 + return self.information_type in PRIVATE_INFORMATION_TYPES

Doesn't this need to defer to transitively_private if information_type? Depending on the way information_type is populated later, we may even need to ignore information_type completely until the migration is done.

174 + if (
175 + self.stacked_on and self.stacked_on.information_type !=
176 + InformationType.PUBLIC and information_type !=
177 + self.stacked_on.information_type):

That is the worst line wrapping in the history of the universe.

211 + if (
212 + self.stacked_on and self.stacked_on.information_type !=
213 + InformationType.PUBLIC):
214 + self.information_type = self.stacked_on.information_type

This could do with one fewer line breaks, and possibly a comment.

327 + if information_type:
328 + removeSecurityProxy(branch).information_type = information_type
329 if private:
330 removeSecurityProxy(branch).explicitly_private = True
331 removeSecurityProxy(branch).transitively_private = True
332 + if not information_type:
333 + removeSecurityProxy(branch).information_type = (
334 + InformationType.USERDATA)

Should you perhaps merge the two information_type bits? And how close are we to eliminating makeBranch(private)?

review: Approve (code)
Revision history for this message
William Grant (wgrant) wrote :

121 + if (self.stacked_on and self.stacked_on.information_type in
122 + PRIVATE_INFORMATION_TYPES and information_type in
123 + PUBLIC_INFORMATION_TYPES):

This linebreaking is still a capital offence in several jurisdictions. One legal spelling is this:

if (self.stacked on
    and self.stacked_on.information_type in PRIVATE_INFORMATION TYPES
    and information_type in PUBLIC_INFORMATION TYPES):

157 + # If the branch we are stacking on is not public, and we are,
158 + # set our information_type to the stacked on's.

This comment should explain why.

159 + if (self.stacked_on and self.stacked_on.information_type not in
160 + PUBLIC_INFORMATION_TYPES and self.information_type in
161 + PUBLIC_INFORMATION_TYPES):

Another capital offence.

273 + branch = self.factory.makeBranch(
274 + name='branch_%s' % x, private=x > 2)

That linebreak is probably no longer required.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/tests/test_branch.py'
2--- lib/lp/code/browser/tests/test_branch.py 2012-05-02 05:25:11 +0000
3+++ lib/lp/code/browser/tests/test_branch.py 2012-05-23 05:26:23 +0000
4@@ -758,7 +758,8 @@
5 # If the target is private, the landing targets should not include it.
6 bmp = self.factory.makeBranchMergeProposal()
7 branch = bmp.source_branch
8- removeSecurityProxy(bmp.target_branch).explicitly_private = True
9+ removeSecurityProxy(bmp.target_branch).information_type = (
10+ InformationType.USERDATA)
11 view = BranchView(branch, LaunchpadTestRequest())
12 self.assertTrue(view.no_merges)
13 self.assertEqual([], view.landing_targets)
14@@ -779,7 +780,8 @@
15 # it.
16 bmp = self.factory.makeBranchMergeProposal()
17 branch = bmp.target_branch
18- removeSecurityProxy(bmp.source_branch).explicitly_private = True
19+ removeSecurityProxy(bmp.source_branch).information_type = (
20+ InformationType.USERDATA)
21 view = BranchView(branch, LaunchpadTestRequest())
22 self.assertTrue(view.no_merges)
23 self.assertEqual([], view.landing_candidates)
24@@ -799,7 +801,8 @@
25 # the target is private, then the dependent_branches are not shown.
26 branch = self.factory.makeProductBranch()
27 bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)
28- removeSecurityProxy(bmp.source_branch).explicitly_private = True
29+ removeSecurityProxy(bmp.source_branch).information_type = (
30+ InformationType.USERDATA)
31 view = BranchView(branch, LaunchpadTestRequest())
32 self.assertTrue(view.no_merges)
33 self.assertEqual([], view.dependent_branches)
34
35=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
36--- lib/lp/code/browser/tests/test_branchmergeproposal.py 2012-05-02 05:25:11 +0000
37+++ lib/lp/code/browser/tests/test_branchmergeproposal.py 2012-05-23 05:26:23 +0000
38@@ -1290,7 +1290,8 @@
39 bmp2 = self.factory.makeBranchMergeProposal(
40 date_created=(
41 datetime(year=2008, month=10, day=10, tzinfo=pytz.UTC)))
42- removeSecurityProxy(bmp2.source_branch).explicitly_private = True
43+ removeSecurityProxy(bmp2.source_branch).information_type = (
44+ InformationType.USERDATA)
45 self.assertEqual(
46 [bmp1], latest_proposals_for_each_branch([bmp1, bmp2]))
47
48
49=== modified file 'lib/lp/code/errors.py'
50--- lib/lp/code/errors.py 2012-02-21 22:46:28 +0000
51+++ lib/lp/code/errors.py 2012-05-23 05:26:23 +0000
52@@ -10,6 +10,7 @@
53 'BadStateTransition',
54 'BranchCannotBePrivate',
55 'BranchCannotBePublic',
56+ 'BranchCannotChangeInformationType',
57 'BranchCreationException',
58 'BranchCreationForbidden',
59 'BranchCreatorNotMemberOfOwnerTeam',
60@@ -155,6 +156,10 @@
61 """The branch cannot be made private."""
62
63
64+class BranchCannotChangeInformationType(Exception):
65+ """The information type of this branch cannot be changed."""
66+
67+
68 class InvalidBranchException(Exception):
69 """Base exception for an error resolving a branch for a component.
70
71
72=== modified file 'lib/lp/code/model/branch.py'
73--- lib/lp/code/model/branch.py 2012-05-04 04:52:24 +0000
74+++ lib/lp/code/model/branch.py 2012-05-23 05:26:23 +0000
75@@ -77,6 +77,7 @@
76 AlreadyLatestFormat,
77 BranchCannotBePrivate,
78 BranchCannotBePublic,
79+ BranchCannotChangeInformationType,
80 BranchMergeProposalExists,
81 BranchTargetError,
82 BranchTypeError,
83@@ -128,7 +129,11 @@
84 )
85 from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch
86 from lp.codehosting.safe_open import safe_open
87-from lp.registry.enums import InformationType
88+from lp.registry.enums import (
89+ InformationType,
90+ PRIVATE_INFORMATION_TYPES,
91+ PUBLIC_INFORMATION_TYPES,
92+ )
93 from lp.registry.interfaces.person import (
94 validate_person,
95 validate_public_person,
96@@ -187,33 +192,42 @@
97
98 @property
99 def private(self):
100- return self.transitively_private
101+ return self.information_type in PRIVATE_INFORMATION_TYPES
102
103 def setPrivate(self, private, user):
104 """See `IBranch`."""
105- if private == self.explicitly_private:
106+ if private:
107+ information_type = InformationType.USERDATA
108+ else:
109+ information_type = InformationType.PUBLIC
110+ return self.transitionToInformationType(information_type, user)
111+
112+ def transitionToInformationType(self, information_type, who):
113+ """See `IBranch`."""
114+ if self.information_type == information_type:
115 return
116+ if (self.stacked_on
117+ and self.stacked_on.information_type in PRIVATE_INFORMATION_TYPES
118+ and information_type in PUBLIC_INFORMATION_TYPES):
119+ raise BranchCannotChangeInformationType()
120+ private = information_type in PRIVATE_INFORMATION_TYPES
121 # Only check the privacy policy if the user is not special.
122- if (not user_has_special_branch_access(user)):
123+ if (not user_has_special_branch_access(who)):
124 policy = IBranchNamespacePolicy(self.namespace)
125
126 if private and not policy.canBranchesBePrivate():
127 raise BranchCannotBePrivate()
128 if not private and not policy.canBranchesBePublic():
129 raise BranchCannotBePublic()
130+ self.information_type = information_type
131+ # Set the legacy values for now.
132 self.explicitly_private = private
133 # If this branch is private, then it is also transitively_private
134 # otherwise we need to reload the value.
135 if private:
136 self.transitively_private = True
137- self.information_type = InformationType.USERDATA
138 else:
139 self.transitively_private = AutoReload
140- self.information_type = InformationType.PUBLIC
141-
142- def transitionToInformationType(self, information_type, who):
143- """See `IBranch`."""
144- self.information_type = information_type
145
146 registrant = ForeignKey(
147 dbName='registrant', foreignKey='Person',
148@@ -1052,6 +1066,13 @@
149 self.mirror_status_message = (
150 'Invalid stacked on location: ' + stacked_on_url)
151 self.stacked_on = stacked_on_branch
152+ # If the branch we are stacking on is not public, and we are,
153+ # set our information_type to the stacked on's, since having a
154+ # public branch stacked on a private branch does not make sense.
155+ if (self.stacked_on
156+ and self.stacked_on.information_type in PRIVATE_INFORMATION_TYPES
157+ and self.information_type in PUBLIC_INFORMATION_TYPES):
158+ self.information_type = self.stacked_on.information_type
159 if self.branch_type == BranchType.HOSTED:
160 self.last_mirrored = UTC_NOW
161 else:
162@@ -1207,7 +1228,7 @@
163 This method doesn't check the stacked upon branch. That is handled by
164 the `visibleByUser` method.
165 """
166- if not self.explicitly_private:
167+ if self.information_type not in PRIVATE_INFORMATION_TYPES:
168 return True
169 if user is None:
170 return False
171
172=== modified file 'lib/lp/code/model/tests/test_branch.py'
173--- lib/lp/code/model/tests/test_branch.py 2012-05-04 04:52:24 +0000
174+++ lib/lp/code/model/tests/test_branch.py 2012-05-23 05:26:23 +0000
175@@ -56,6 +56,7 @@
176 AlreadyLatestFormat,
177 BranchCannotBePrivate,
178 BranchCannotBePublic,
179+ BranchCannotChangeInformationType,
180 BranchCreatorNotMemberOfOwnerTeam,
181 BranchCreatorNotOwner,
182 BranchTargetError,
183@@ -2334,16 +2335,21 @@
184 def test_public_stacked_on_private_is_private(self):
185 # A public branch stacked on a private branch is private.
186 stacked_on = self.factory.makeBranch(private=True)
187- branch = self.factory.makeBranch(stacked_on=stacked_on, private=False)
188+ branch = self.factory.makeBranch(
189+ stacked_on=stacked_on, private=False)
190 self.assertTrue(branch.private)
191+ self.assertEqual(
192+ stacked_on.information_type, branch.information_type)
193 self.assertTrue(removeSecurityProxy(branch).transitively_private)
194 self.assertFalse(branch.explicitly_private)
195
196 def test_private_stacked_on_public_is_private(self):
197- # A public branch stacked on a private branch is private.
198+ # A private branch stacked on a public branch is private.
199 stacked_on = self.factory.makeBranch(private=False)
200 branch = self.factory.makeBranch(stacked_on=stacked_on, private=True)
201 self.assertTrue(branch.private)
202+ self.assertNotEqual(
203+ stacked_on.information_type, branch.information_type)
204 self.assertTrue(removeSecurityProxy(branch).transitively_private)
205 self.assertTrue(branch.explicitly_private)
206
207@@ -2436,6 +2442,26 @@
208 branch.setPrivate,
209 False, branch.owner)
210
211+ def test_cannot_transition_with_private_stacked_on(self):
212+ # If a public branch is stacked on a private branch, it can not
213+ # change its information_type to public.
214+ stacked_on = self.factory.makeBranch(private=True)
215+ branch = self.factory.makeBranch(stacked_on=stacked_on)
216+ self.assertRaises(
217+ BranchCannotChangeInformationType,
218+ branch.transitionToInformationType, InformationType.PUBLIC,
219+ branch.owner)
220+
221+ def test_can_transition_with_public_stacked_on(self):
222+ # If a private branch is stacked on a public branch, it can change
223+ # its information_type.
224+ stacked_on = self.factory.makeBranch()
225+ branch = self.factory.makeBranch(stacked_on=stacked_on, private=True)
226+ branch.transitionToInformationType(
227+ InformationType.UNEMBARGOEDSECURITY, branch.owner)
228+ self.assertEqual(
229+ InformationType.UNEMBARGOEDSECURITY, branch.information_type)
230+
231
232 class TestBranchCommitsForDays(TestCaseWithFactory):
233 """Tests for `Branch.commitsForDays`."""
234
235=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
236--- lib/lp/code/model/tests/test_branchmergeproposal.py 2012-05-02 05:25:11 +0000
237+++ lib/lp/code/model/tests/test_branchmergeproposal.py 2012-05-23 05:26:23 +0000
238@@ -929,8 +929,9 @@
239 charlie, BranchSubscriptionNotificationLevel.NOEMAIL, None,
240 CodeReviewNotificationLevel.FULL, charlie)
241 # Make both branches private.
242- removeSecurityProxy(bmp.source_branch).explicitly_private = True
243- removeSecurityProxy(bmp.target_branch).explicitly_private = True
244+ for branch in (bmp.source_branch, bmp.target_branch):
245+ removeSecurityProxy(branch).information_type = (
246+ InformationType.USERDATA)
247 recipients = bmp.getNotificationRecipients(
248 CodeReviewNotificationLevel.FULL)
249 self.assertFalse(bob in recipients)
250
251=== modified file 'lib/lp/code/model/tests/test_branchvisibility.py'
252--- lib/lp/code/model/tests/test_branchvisibility.py 2012-01-15 17:43:05 +0000
253+++ lib/lp/code/model/tests/test_branchvisibility.py 2012-05-23 05:26:23 +0000
254@@ -1,4 +1,4 @@
255-# Copyright 2011 Canonical Ltd. This software is licensed under the
256+# Copyright 2011-2012 Canonical Ltd. This software is licensed under the
257 # GNU Affero General Public License version 3 (see the file LICENSE).
258
259 """Tests for visibility of branches.
260@@ -134,14 +134,11 @@
261 private_owner = self.factory.makePerson()
262 test_branches = []
263 for x in range(5):
264- # We want the first 3 public and the last 3 private
265- branch = self.factory.makeBranch(name='branch_%s' % x)
266- # The 3rd, 4th and 5th will be explicitly private.
267- branch.explicitly_private = x > 2
268+ # We want the first 3 public and the last 3 private.
269+ branch = self.factory.makeBranch(private=x > 2)
270 test_branches.append(branch)
271 test_branches.append(
272- self.factory.makeBranch(
273- name='branch_5', private=True, owner=private_owner))
274+ self.factory.makeBranch(private=True, owner=private_owner))
275
276 # Anonymous users see just the public branches.
277 branch_info = [(branch, branch.private)
278
279=== modified file 'lib/lp/code/xmlrpc/tests/test_branch.py'
280--- lib/lp/code/xmlrpc/tests/test_branch.py 2012-01-01 02:58:52 +0000
281+++ lib/lp/code/xmlrpc/tests/test_branch.py 2012-05-23 05:26:23 +0000
282@@ -1,4 +1,4 @@
283-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
284+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
285 # GNU Affero General Public License version 3 (see the file LICENSE).
286
287 """Unit tests for the public codehosting API."""
288@@ -16,6 +16,7 @@
289 from lp.code.interfaces.codehosting import BRANCH_ALIAS_PREFIX
290 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
291 from lp.code.xmlrpc.branch import PublicCodehostingAPI
292+from lp.registry.enums import InformationType
293 from lp.services.xmlrpc import LaunchpadFault
294 from lp.testing import (
295 person_logged_in,
296@@ -323,7 +324,8 @@
297 def test_private_branch_as_development_focus(self):
298 # We resolve private linked branches using the writable alias.
299 product, trunk = self.makeProdutWithTrunk()
300- removeSecurityProxy(trunk).explicitly_private = True
301+ removeSecurityProxy(trunk).information_type = (
302+ InformationType.USERDATA)
303 self.assertOnlyWritableResolves(product.name)
304
305 def test_private_branch_as_user(self):
306
307=== modified file 'lib/lp/testing/factory.py'
308--- lib/lp/testing/factory.py 2012-05-04 14:52:44 +0000
309+++ lib/lp/testing/factory.py 2012-05-23 05:26:23 +0000
310@@ -140,6 +140,7 @@
311 DistroSeriesDifferenceStatus,
312 DistroSeriesDifferenceType,
313 InformationType,
314+ PRIVATE_INFORMATION_TYPES,
315 )
316 from lp.registry.interfaces.accesspolicy import (
317 IAccessArtifactGrantSource,
318@@ -1072,8 +1073,8 @@
319
320 def makeBranch(self, branch_type=None, owner=None,
321 name=None, product=_DEFAULT, url=_DEFAULT, registrant=None,
322- private=False, stacked_on=None, sourcepackage=None,
323- reviewer=None, **optional_branch_args):
324+ private=None, information_type=None, stacked_on=None,
325+ sourcepackage=None, reviewer=None, **optional_branch_args):
326 """Create and return a new, arbitrary Branch of the given type.
327
328 Any parameters for `IBranchNamespace.createBranch` can be specified to
329@@ -1119,11 +1120,22 @@
330 branch = namespace.createBranch(
331 branch_type=branch_type, name=name, registrant=registrant,
332 url=url, **optional_branch_args)
333- if private:
334- removeSecurityProxy(branch).explicitly_private = True
335- removeSecurityProxy(branch).transitively_private = True
336+ assert information_type is None or private is None, (
337+ "Can not specify both information_type and private")
338+ if information_type is not None or private is not None:
339+ if information_type:
340+ private = information_type in PRIVATE_INFORMATION_TYPES
341+ else:
342+ information_type = (
343+ InformationType.USERDATA if private else
344+ InformationType.PUBLIC)
345+ if private:
346+ removeSecurityProxy(branch).explicitly_private = True
347+ removeSecurityProxy(branch).transitively_private = True
348+ removeSecurityProxy(branch).information_type = information_type
349 if stacked_on is not None:
350- removeSecurityProxy(branch).stacked_on = stacked_on
351+ removeSecurityProxy(branch).branchChanged(
352+ stacked_on.unique_name, 'rev1', None, None, None)
353 if reviewer is not None:
354 removeSecurityProxy(branch).reviewer = reviewer
355 return branch