← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

Building on https://code.launchpad.net/~stevenk/launchpad/branch-use-information_type/+merge/106109 which got reverted due to test failures.

I have fixed the test failures, and have changed transitionToInformationType so it will change the information_type of branches stacked on the branch which information_type is being changed.
-- 
https://code.launchpad.net/~stevenk/launchpad/branch-use-information_type-redux/+merge/107150
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/branch-use-information_type-redux into lp:launchpad.
=== modified file 'lib/lp/app/browser/tests/test_launchpad.py'
--- lib/lp/app/browser/tests/test_launchpad.py	2012-02-23 23:44:00 +0000
+++ lib/lp/app/browser/tests/test_launchpad.py	2012-05-24 02:20:24 +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).
 
 """Tests for traversal from the root branch object."""
@@ -20,6 +20,7 @@
 from lp.app.errors import GoneError
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
+from lp.registry.enums import InformationType
 from lp.registry.interfaces.person import (
     IPersonSet,
     PersonVisibility,
@@ -192,7 +193,8 @@
         branch = self.factory.makeProductBranch()
         naked_product = removeSecurityProxy(branch.product)
         ICanHasLinkedBranch(naked_product).setBranch(branch)
-        removeSecurityProxy(branch).explicitly_private = True
+        removeSecurityProxy(branch).information_type = (
+            InformationType.USERDATA)
         login(ANONYMOUS)
         requiredMessage = (
             u"The target %s does not have a linked branch." %
@@ -218,7 +220,8 @@
         branch = self.factory.makeProductBranch()
         naked_product = removeSecurityProxy(branch.product)
         ICanHasLinkedBranch(naked_product).setBranch(branch)
-        removeSecurityProxy(branch).explicitly_private = True
+        removeSecurityProxy(branch).information_type = (
+            InformationType.USERDATA)
         login(ANONYMOUS)
         self.assertNotFound(naked_product.name, use_default_referer=False)
 

=== modified file 'lib/lp/code/browser/tests/test_branch.py'
--- lib/lp/code/browser/tests/test_branch.py	2012-05-23 22:27:56 +0000
+++ lib/lp/code/browser/tests/test_branch.py	2012-05-24 02:20:24 +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-23 22:27:56 +0000
+++ lib/lp/code/browser/tests/test_branchmergeproposal.py	2012-05-24 02:20:24 +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-05-23 22:27:56 +0000
+++ lib/lp/code/errors.py	2012-05-24 02:20:24 +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 information type of this branch cannot be changed."""
+
+
 class InvalidBranchException(Exception):
     """Base exception for an error resolving a branch for a component.
 

=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py	2012-05-23 22:27:56 +0000
+++ lib/lp/code/model/branch.py	2012-05-24 02:20:24 +0000
@@ -77,6 +77,7 @@
     AlreadyLatestFormat,
     BranchCannotBePrivate,
     BranchCannotBePublic,
+    BranchCannotChangeInformationType,
     BranchMergeProposalExists,
     BranchTargetError,
     BranchTypeError,
@@ -128,7 +129,11 @@
     )
 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,
+    PUBLIC_INFORMATION_TYPES,
+    )
 from lp.registry.interfaces.person import (
     validate_person,
     validate_public_person,
@@ -187,33 +192,47 @@
 
     @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:
+        if private:
+            information_type = InformationType.USERDATA
+        else:
+            information_type = InformationType.PUBLIC
+        return self.transitionToInformationType(information_type, 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 in PRIVATE_INFORMATION_TYPES
+            and information_type in PUBLIC_INFORMATION_TYPES):
+            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
+        # Change the information_type of any stacked branches.
+        children = list(self.getStackedBranches())
+        for child in children:
+            children.extend(list(child.getStackedBranches()))
+            child.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 +1071,13 @@
                 self.mirror_status_message = (
                     'Invalid stacked on location: ' + stacked_on_url)
         self.stacked_on = stacked_on_branch
+        # If the branch we are stacking on is not public, and we are,
+        # set our information_type to the stacked on's, since having a
+        # public branch stacked on a private branch does not make sense.
+        if (self.stacked_on
+            and self.stacked_on.information_type in PRIVATE_INFORMATION_TYPES
+            and self.information_type in PUBLIC_INFORMATION_TYPES):
+            self.information_type = self.stacked_on.information_type
         if self.branch_type == BranchType.HOSTED:
             self.last_mirrored = UTC_NOW
         else:
@@ -1207,7 +1233,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-23 22:27:56 +0000
+++ lib/lp/code/model/tests/test_branch.py	2012-05-24 02:20:24 +0000
@@ -56,6 +56,7 @@
     AlreadyLatestFormat,
     BranchCannotBePrivate,
     BranchCannotBePublic,
+    BranchCannotChangeInformationType,
     BranchCreatorNotMemberOfOwnerTeam,
     BranchCreatorNotOwner,
     BranchTargetError,
@@ -2334,16 +2335,21 @@
     def test_public_stacked_on_private_is_private(self):
         # A public branch stacked on a private branch is private.
         stacked_on = self.factory.makeBranch(private=True)
-        branch = self.factory.makeBranch(stacked_on=stacked_on, private=False)
+        branch = self.factory.makeBranch(
+            stacked_on=stacked_on, private=False)
         self.assertTrue(branch.private)
+        self.assertEqual(
+            stacked_on.information_type, branch.information_type)
         self.assertTrue(removeSecurityProxy(branch).transitively_private)
         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)
+        self.assertNotEqual(
+            stacked_on.information_type, branch.information_type)
         self.assertTrue(removeSecurityProxy(branch).transitively_private)
         self.assertTrue(branch.explicitly_private)
 
@@ -2436,6 +2442,26 @@
             branch.setPrivate,
             False, branch.owner)
 
+    def test_cannot_transition_with_private_stacked_on(self):
+        # If a public branch is stacked on a private branch, it can not
+        # change its information_type to public.
+        stacked_on = self.factory.makeBranch(private=True)
+        branch = self.factory.makeBranch(stacked_on=stacked_on)
+        self.assertRaises(
+            BranchCannotChangeInformationType,
+            branch.transitionToInformationType, InformationType.PUBLIC,
+            branch.owner)
+
+    def test_can_transition_with_public_stacked_on(self):
+        # If a private branch is stacked on a public branch, it can change
+        # its information_type.
+        stacked_on = self.factory.makeBranch()
+        branch = self.factory.makeBranch(stacked_on=stacked_on, private=True)
+        branch.transitionToInformationType(
+            InformationType.UNEMBARGOEDSECURITY, branch.owner)
+        self.assertEqual(
+            InformationType.UNEMBARGOEDSECURITY, branch.information_type)
+
 
 class TestBranchCommitsForDays(TestCaseWithFactory):
     """Tests for `Branch.commitsForDays`."""

=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
--- lib/lp/code/model/tests/test_branchmergeproposal.py	2012-05-23 22:27:56 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposal.py	2012-05-24 02:20:24 +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)
@@ -1505,7 +1506,7 @@
         base_branch = self.factory.makeBranch(
             owner=owner, private=True, product=product)
         source_branch = self.factory.makeBranch(
-            stacked_on=base_branch, product=product)
+            stacked_on=base_branch, product=product, owner=owner)
         target_branch = self.factory.makeBranch(owner=owner, product=product)
         target_branch.product.setBranchVisibilityTeamPolicy(
             owner, BranchVisibilityRule.PRIVATE)

=== modified file 'lib/lp/code/model/tests/test_branchvisibility.py'
--- lib/lp/code/model/tests/test_branchvisibility.py	2012-05-23 22:27:56 +0000
+++ lib/lp/code/model/tests/test_branchvisibility.py	2012-05-24 02:20:24 +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,14 +134,11 @@
         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(private=x > 2)
             test_branches.append(branch)
         test_branches.append(
-            self.factory.makeBranch(
-                name='branch_5', private=True, owner=private_owner))
+            self.factory.makeBranch(private=True, owner=private_owner))
 
         # Anonymous users see just the public branches.
         branch_info = [(branch, branch.private)

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py	2012-02-28 11:14:44 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py	2012-05-24 02:20:24 +0000
@@ -50,6 +50,7 @@
     )
 from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData
 from lp.code.tests.helpers import recipe_parser_newest_version
+from lp.registry.enums import InformationType
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.services.database.bulk import load_referencing
 from lp.services.database.constants import UTC_NOW
@@ -529,7 +530,8 @@
         with person_logged_in(owner):
             recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
             self.assertTrue(check_permission('launchpad.View', recipe))
-        removeSecurityProxy(branch).explicitly_private = True
+        removeSecurityProxy(branch).information_type = (
+            InformationType.USERDATA)
         with person_logged_in(self.factory.makePerson()):
             self.assertFalse(check_permission('launchpad.View', recipe))
         self.assertFalse(check_permission('launchpad.View', recipe))

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2012-04-24 06:44:30 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2012-05-24 02:20:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for source package builds."""
@@ -38,6 +38,7 @@
     SourcePackageRecipeBuildMailer,
     )
 from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild
+from lp.registry.enums import InformationType
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.database.lpstorm import IStore
@@ -176,7 +177,8 @@
             job = build.makeJob()
             self.assertTrue(check_permission('launchpad.View', build))
             self.assertTrue(check_permission('launchpad.View', job))
-        removeSecurityProxy(branch).explicitly_private = True
+        removeSecurityProxy(branch).information_type = (
+            InformationType.USERDATA)
         with person_logged_in(self.factory.makePerson()):
             self.assertFalse(check_permission('launchpad.View', build))
             self.assertFalse(check_permission('launchpad.View', job))

=== modified file 'lib/lp/code/xmlrpc/tests/test_branch.py'
--- lib/lp/code/xmlrpc/tests/test_branch.py	2012-05-23 22:27:56 +0000
+++ lib/lp/code/xmlrpc/tests/test_branch.py	2012-05-24 02:20:24 +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/registry/stories/product/xx-product-development-focus.txt'
--- lib/lp/registry/stories/product/xx-product-development-focus.txt	2012-01-15 13:32:27 +0000
+++ lib/lp/registry/stories/product/xx-product-development-focus.txt	2012-05-24 02:20:24 +0000
@@ -14,7 +14,9 @@
     >>> eric = factory.makePerson(name='eric', email='eric@xxxxxxxxxxx')
     >>> fooix = factory.makeProduct(name='fooix', owner=eric)
     >>> branch = factory.makeBranch(owner=eric, product=fooix, name='trunk')
-    >>> # Make revisions for the branch so it has a codebrowse link.
+
+Make revisions for the branch so it has a codebrowse link.
+
     >>> factory.makeRevisionsForBranch(branch)
     >>> logout()
 
@@ -145,7 +147,9 @@
 
     >>> login('admin@xxxxxxxxxxxxx')
     >>> from zope.security.proxy import removeSecurityProxy
-    >>> removeSecurityProxy(branch).explicitly_private = True
+    >>> from lp.registry.enums import InformationType
+    >>> removeSecurityProxy(branch).information_type = (
+    ...     InformationType.USERDATA)
     >>> logout()
 
     >>> anon_browser.open('http://launchpad.dev/fooix')

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2012-05-23 22:27:56 +0000
+++ lib/lp/testing/factory.py	2012-05-24 02:20:24 +0000
@@ -140,6 +140,7 @@
     DistroSeriesDifferenceStatus,
     DistroSeriesDifferenceType,
     InformationType,
+    PRIVATE_INFORMATION_TYPES,
     )
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifactGrantSource,
@@ -1072,8 +1073,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=None, 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 +1120,23 @@
         branch = namespace.createBranch(
             branch_type=branch_type, name=name, registrant=registrant,
             url=url, **optional_branch_args)
-        if private:
-            removeSecurityProxy(branch).explicitly_private = True
-            removeSecurityProxy(branch).transitively_private = True
+        assert information_type is None or private is None, (
+            "Can not specify both information_type and private")
+        if information_type is not None or private is not None:
+            if information_type:
+                private = information_type in PRIVATE_INFORMATION_TYPES
+            else:
+                information_type = (
+                    InformationType.USERDATA if private else
+                    InformationType.PUBLIC)
+            if private:
+                removeSecurityProxy(branch).explicitly_private = True
+                removeSecurityProxy(branch).transitively_private = True
+            removeSecurityProxy(branch).information_type = information_type
         if stacked_on is not None:
-            removeSecurityProxy(branch).stacked_on = stacked_on
+            removeSecurityProxy(branch).branchChanged(
+                removeSecurityProxy(stacked_on).unique_name, 'rev1', None,
+                None, None)
         if reviewer is not None:
             removeSecurityProxy(branch).reviewer = reviewer
         return branch

=== modified file 'lib/lp/translations/tests/test_translationtemplatesbuildjob.py'
--- lib/lp/translations/tests/test_translationtemplatesbuildjob.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/tests/test_translationtemplatesbuildjob.py	2012-05-24 02:20:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -17,6 +17,7 @@
 from lp.code.model.branchjob import BranchJob
 from lp.code.model.directbranchcommit import DirectBranchCommit
 from lp.codehosting.scanner import events
+from lp.registry.enums import InformationType
 from lp.services.job.model.job import Job
 from lp.services.webapp.interfaces import (
     DEFAULT_FLAVOR,
@@ -249,7 +250,8 @@
     def test_private_branch(self):
         # We don't generate templates for private branches.
         branch = self._makeTranslationBranch(fake_pottery_compatible=True)
-        removeSecurityProxy(branch).explicitly_private = True
+        removeSecurityProxy(branch).information_type = (
+            InformationType.USERDATA)
         self.assertFalse(self.jobsource.generatesTemplates(branch))
 
     def test_scheduleTranslationTemplatesBuild_subscribed(self):


Follow ups