← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/branch-use-information_type into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #933768 in Launchpad itself: "Update branch to use information_visibility_policy"
  https://bugs.launchpad.net/launchpad/+bug/933768

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/branch-use-information_type/+merge/106109

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.
-- 
https://code.launchpad.net/~stevenk/launchpad/branch-use-information_type/+merge/106109
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/branch-use-information_type into lp:launchpad.
=== modified file 'lib/lp/code/adapters/branch.py'
--- lib/lp/code/adapters/branch.py	2011-09-26 20:00:50 +0000
+++ lib/lp/code/adapters/branch.py	2012-05-17 04:48:18 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Components related to branches."""
@@ -8,6 +8,7 @@
     "BranchDelta",
     "BranchMergeProposalDelta",
     "BranchMergeProposalNoPreviewDiffDelta",
+    "convert_to_information_type",
     ]
 
 from contextlib import contextmanager
@@ -23,6 +24,7 @@
     IBranchDelta,
     )
 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
+from lp.registry.enums import InformationType
 
 # XXX: thumper 2006-12-20: This needs to be extended
 # to cover bugs and specs linked and unlinked, as
@@ -150,3 +152,10 @@
     new_values = tuple(
         name for name in BranchMergeProposalDelta.new_values
         if name != "preview_diff")
+
+
+def convert_to_information_type(private):
+    if private:
+        return InformationType.USERDATA
+    else:
+        return InformationType.PUBLIC

=== modified file 'lib/lp/code/browser/tests/test_branch.py'
--- lib/lp/code/browser/tests/test_branch.py	2012-05-02 05:25:11 +0000
+++ lib/lp/code/browser/tests/test_branch.py	2012-05-17 04:48:18 +0000
@@ -758,7 +758,8 @@
         # If the target is private, the landing targets should not include it.
         bmp = self.factory.makeBranchMergeProposal()
         branch = bmp.source_branch
-        removeSecurityProxy(bmp.target_branch).explicitly_private = True
+        removeSecurityProxy(bmp.target_branch).information_type = (
+            InformationType.USERDATA)
         view = BranchView(branch, LaunchpadTestRequest())
         self.assertTrue(view.no_merges)
         self.assertEqual([], view.landing_targets)
@@ -779,7 +780,8 @@
         # it.
         bmp = self.factory.makeBranchMergeProposal()
         branch = bmp.target_branch
-        removeSecurityProxy(bmp.source_branch).explicitly_private = True
+        removeSecurityProxy(bmp.source_branch).information_type = (
+            InformationType.USERDATA)
         view = BranchView(branch, LaunchpadTestRequest())
         self.assertTrue(view.no_merges)
         self.assertEqual([], view.landing_candidates)
@@ -799,7 +801,8 @@
         # the target is private, then the dependent_branches are not shown.
         branch = self.factory.makeProductBranch()
         bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)
-        removeSecurityProxy(bmp.source_branch).explicitly_private = True
+        removeSecurityProxy(bmp.source_branch).information_type = (
+            InformationType.USERDATA)
         view = BranchView(branch, LaunchpadTestRequest())
         self.assertTrue(view.no_merges)
         self.assertEqual([], view.dependent_branches)

=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
--- lib/lp/code/browser/tests/test_branchmergeproposal.py	2012-05-02 05:25:11 +0000
+++ lib/lp/code/browser/tests/test_branchmergeproposal.py	2012-05-17 04:48:18 +0000
@@ -1290,7 +1290,8 @@
         bmp2 = self.factory.makeBranchMergeProposal(
             date_created=(
                 datetime(year=2008, month=10, day=10, tzinfo=pytz.UTC)))
-        removeSecurityProxy(bmp2.source_branch).explicitly_private = True
+        removeSecurityProxy(bmp2.source_branch).information_type = (
+            InformationType.USERDATA)
         self.assertEqual(
             [bmp1], latest_proposals_for_each_branch([bmp1, bmp2]))
 

=== modified file 'lib/lp/code/errors.py'
--- lib/lp/code/errors.py	2012-02-21 22:46:28 +0000
+++ lib/lp/code/errors.py	2012-05-17 04:48:18 +0000
@@ -10,6 +10,7 @@
     'BadStateTransition',
     'BranchCannotBePrivate',
     'BranchCannotBePublic',
+    'BranchCannotChangeInformationType',
     'BranchCreationException',
     'BranchCreationForbidden',
     'BranchCreatorNotMemberOfOwnerTeam',
@@ -155,6 +156,10 @@
     """The branch cannot be made private."""
 
 
+class BranchCannotChangeInformationType(Exception):
+    """The branch cannot change its information type."""
+
+
 class InvalidBranchException(Exception):
     """Base exception for an error resolving a branch for a component.
 

=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py	2012-05-04 04:52:24 +0000
+++ lib/lp/code/interfaces/branch.py	2012-05-17 04:48:18 +0000
@@ -53,6 +53,7 @@
     Reference,
     ReferenceChoice,
     )
+from lazr.restful.interface import copy_field
 from zope.component import getUtility
 from zope.interface import (
     Attribute,

=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py	2012-05-04 04:52:24 +0000
+++ lib/lp/code/model/branch.py	2012-05-17 04:48:18 +0000
@@ -61,6 +61,7 @@
     )
 from lp.bugs.interfaces.bugtaskfilter import filter_bugtasks_by_context
 from lp.buildmaster.model.buildqueue import BuildQueue
+from lp.code.adapters.branch import convert_to_information_type
 from lp.code.bzr import (
     BranchFormat,
     ControlFormat,
@@ -77,6 +78,7 @@
     AlreadyLatestFormat,
     BranchCannotBePrivate,
     BranchCannotBePublic,
+    BranchCannotChangeInformationType,
     BranchMergeProposalExists,
     BranchTargetError,
     BranchTypeError,
@@ -128,7 +130,10 @@
     )
 from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch
 from lp.codehosting.safe_open import safe_open
-from lp.registry.enums import InformationType
+from lp.registry.enums import (
+    InformationType,
+    PRIVATE_INFORMATION_TYPES,
+    )
 from lp.registry.interfaces.person import (
     validate_person,
     validate_public_person,
@@ -186,34 +191,45 @@
         enum=InformationType, default=InformationType.PUBLIC)
 
     @property
+    def _private(self):
+        return self.transitively_private
+
+    @property
     def private(self):
-        return self.transitively_private
+        return self.information_type in PRIVATE_INFORMATION_TYPES
 
     def setPrivate(self, private, user):
         """See `IBranch`."""
-        if private == self.explicitly_private:
+        return self.transitionToInformationType(
+            convert_to_information_type(private), user)
+
+    def transitionToInformationType(self, information_type, who):
+        """See `IBranch`."""
+        if self.information_type == information_type:
             return
+        if (
+            self.stacked_on and self.stacked_on.information_type !=
+            InformationType.PUBLIC and information_type !=
+            self.stacked_on.information_type):
+            raise BranchCannotChangeInformationType()
+        private = information_type in PRIVATE_INFORMATION_TYPES
         # Only check the privacy policy if the user is not special.
-        if (not user_has_special_branch_access(user)):
+        if (not user_has_special_branch_access(who)):
             policy = IBranchNamespacePolicy(self.namespace)
 
             if private and not policy.canBranchesBePrivate():
                 raise BranchCannotBePrivate()
             if not private and not policy.canBranchesBePublic():
                 raise BranchCannotBePublic()
+        self.information_type = information_type
+        # Set the legacy values for now.
         self.explicitly_private = private
         # If this branch is private, then it is also transitively_private
         # otherwise we need to reload the value.
         if private:
             self.transitively_private = True
-            self.information_type = InformationType.USERDATA
         else:
             self.transitively_private = AutoReload
-            self.information_type = InformationType.PUBLIC
-
-    def transitionToInformationType(self, information_type, who):
-        """See `IBranch`."""
-        self.information_type = information_type
 
     registrant = ForeignKey(
         dbName='registrant', foreignKey='Person',
@@ -1052,6 +1068,10 @@
                 self.mirror_status_message = (
                     'Invalid stacked on location: ' + stacked_on_url)
         self.stacked_on = stacked_on_branch
+        if (
+            self.stacked_on and self.stacked_on.information_type != 
+            InformationType.PUBLIC):
+            self.information_type = self.stacked_on.information_type
         if self.branch_type == BranchType.HOSTED:
             self.last_mirrored = UTC_NOW
         else:
@@ -1207,7 +1227,7 @@
         This method doesn't check the stacked upon branch.  That is handled by
         the `visibleByUser` method.
         """
-        if not self.explicitly_private:
+        if self.information_type not in PRIVATE_INFORMATION_TYPES:
             return True
         if user is None:
             return False

=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py	2012-05-04 04:52:24 +0000
+++ lib/lp/code/model/tests/test_branch.py	2012-05-17 04:48:18 +0000
@@ -2340,7 +2340,7 @@
         self.assertFalse(branch.explicitly_private)
 
     def test_private_stacked_on_public_is_private(self):
-        # A public branch stacked on a private branch is private.
+        # A private branch stacked on a public branch is private.
         stacked_on = self.factory.makeBranch(private=False)
         branch = self.factory.makeBranch(stacked_on=stacked_on, private=True)
         self.assertTrue(branch.private)

=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
--- lib/lp/code/model/tests/test_branchmergeproposal.py	2012-05-02 05:25:11 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposal.py	2012-05-17 04:48:18 +0000
@@ -929,8 +929,9 @@
             charlie, BranchSubscriptionNotificationLevel.NOEMAIL, None,
             CodeReviewNotificationLevel.FULL, charlie)
         # Make both branches private.
-        removeSecurityProxy(bmp.source_branch).explicitly_private = True
-        removeSecurityProxy(bmp.target_branch).explicitly_private = True
+        for branch in (bmp.source_branch, bmp.target_branch):
+            removeSecurityProxy(branch).information_type = (
+                InformationType.USERDATA)
         recipients = bmp.getNotificationRecipients(
             CodeReviewNotificationLevel.FULL)
         self.assertFalse(bob in recipients)

=== modified file 'lib/lp/code/model/tests/test_branchvisibility.py'
--- lib/lp/code/model/tests/test_branchvisibility.py	2012-01-15 17:43:05 +0000
+++ lib/lp/code/model/tests/test_branchvisibility.py	2012-05-17 04:48:18 +0000
@@ -1,4 +1,4 @@
-# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2011-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for visibility of branches.
@@ -134,10 +134,9 @@
         private_owner = self.factory.makePerson()
         test_branches = []
         for x in range(5):
-            # We want the first 3 public and the last 3 private
-            branch = self.factory.makeBranch(name='branch_%s' % x)
-            # The 3rd, 4th and 5th will be explicitly private.
-            branch.explicitly_private = x > 2
+            # We want the first 3 public and the last 3 private.
+            branch = self.factory.makeBranch(
+                name='branch_%s' % x, private=x > 2)
             test_branches.append(branch)
         test_branches.append(
             self.factory.makeBranch(

=== modified file 'lib/lp/code/xmlrpc/tests/test_branch.py'
--- lib/lp/code/xmlrpc/tests/test_branch.py	2012-01-01 02:58:52 +0000
+++ lib/lp/code/xmlrpc/tests/test_branch.py	2012-05-17 04:48:18 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for the public codehosting API."""
@@ -16,6 +16,7 @@
 from lp.code.interfaces.codehosting import BRANCH_ALIAS_PREFIX
 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
 from lp.code.xmlrpc.branch import PublicCodehostingAPI
+from lp.registry.enums import InformationType
 from lp.services.xmlrpc import LaunchpadFault
 from lp.testing import (
     person_logged_in,
@@ -323,7 +324,8 @@
     def test_private_branch_as_development_focus(self):
         # We resolve private linked branches using the writable alias.
         product, trunk = self.makeProdutWithTrunk()
-        removeSecurityProxy(trunk).explicitly_private = True
+        removeSecurityProxy(trunk).information_type = (
+            InformationType.USERDATA)
         self.assertOnlyWritableResolves(product.name)
 
     def test_private_branch_as_user(self):

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2012-05-04 14:52:44 +0000
+++ lib/lp/testing/factory.py	2012-05-17 04:48:18 +0000
@@ -1072,8 +1072,8 @@
 
     def makeBranch(self, branch_type=None, owner=None,
                    name=None, product=_DEFAULT, url=_DEFAULT, registrant=None,
-                   private=False, stacked_on=None, sourcepackage=None,
-                   reviewer=None, **optional_branch_args):
+                   private=False, information_type=None, stacked_on=None,
+                   sourcepackage=None, reviewer=None, **optional_branch_args):
         """Create and return a new, arbitrary Branch of the given type.
 
         Any parameters for `IBranchNamespace.createBranch` can be specified to
@@ -1119,11 +1119,17 @@
         branch = namespace.createBranch(
             branch_type=branch_type, name=name, registrant=registrant,
             url=url, **optional_branch_args)
+        if information_type:
+            removeSecurityProxy(branch).information_type = information_type
         if private:
             removeSecurityProxy(branch).explicitly_private = True
             removeSecurityProxy(branch).transitively_private = True
+            if not information_type:
+                removeSecurityProxy(branch).information_type = (
+                    InformationType.USERDATA)
         if stacked_on is not None:
-            removeSecurityProxy(branch).stacked_on = stacked_on
+            removeSecurityProxy(branch).branchChanged(
+                stacked_on.unique_name, 'rev1', None, None, None)
         if reviewer is not None:
             removeSecurityProxy(branch).reviewer = reviewer
         return branch


Follow ups