← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~adeuring/launchpad/security-guarded-test-object-factory-1 into lp:launchpad/devel

 

Abel Deuring has proposed merging lp:~adeuring/launchpad/security-guarded-test-object-factory-1 into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)


While working together with Brian on another branch during the LP epic last week, I was bitten by the fact that we have still a number of methods in LaunchpadObjectFactory which return unproxied objects. While we fixed this problem for the one or two methods we needed in that branch, a number of other methods still remained that return unproxied objects.

Some time ago, Gavin noticed the same problem, IIRC for makePerson(); he also filed a bug that other methods still returned unproxied objects, and Salgado finally fixed that bug.

However, new methods obviously returning unproxied objects creeped into LaunchpadObjectFactory in the meantime, thus reintroducing the old problem. So I thought that it might be reasonable to ensure that all objects returned by LPObjectFactory methods are either of a simple Python type or are security proxied.

The implementation is quite simple: I renamed LaunchpadObjectFactory to _LaunchpadObjectFactory and wrote a new class LaunchpadObjectFactory with basically one method, __getattr__(). This method returns a the "real" factory method, wrapped in a function guarded_method() which ensures that the object returned by the _LaunchpadObjectFactory.makeWhatever() matches the conditions described above.

We have at present ca 20 methods which return, at least sometimes, unproxied objects. Fixing all these methods in one branch would result in a huge diff, so I fixed only two methods in this branch, makeDistroRelease() and makeProductSeries().

My plan is to let guarded_method() raise an exception if it detects an unproxied object, once all existing factory methods return properly proxied objects. For now, it just print a warning to stderr.

The largest part of the diff is test fixes. I took the simple route: Just to remove the security proxy so that the affected tests pass again. I did not bother to check if this is reasonable in a given test -- doing so would simply have needed far too much time. Instead, I added a function remove_security_proxy_and_shout_at_engineer() which just calls removeSecurityProxy() and print a warning to stderr. This will make the tests quite noisy (as do the warnings issued guarded_method() about unproxied objects)...

Pre-/mid-imp discussions with Jeroen and Gary.

affected tests: Many, see the diff.

I also removed some lint in the files I touched, but some messages remain:

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/code/browser/tests/test_branchlisting.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py
  lib/lp/code/model/tests/test_branch.py
  lib/lp/code/model/tests/test_linkedbranch.py
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
  lib/lp/registry/browser/tests/distroseries-views.txt
  lib/lp/registry/browser/tests/milestone-views.txt
  lib/lp/registry/browser/tests/productseries-views.txt
  lib/lp/registry/stories/webservice/xx-project-registry.txt
  lib/lp/registry/tests/test_distroseries.py
  lib/lp/registry/tests/test_sourcepackage.py
  lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py
  lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py
  lib/lp/soyuz/tests/test_publishing.py
  lib/lp/testing/factory.py
  lib/lp/translations/browser/tests/test_breadcrumbs.py
  lib/lp/translations/doc/translations-export-to-branch.txt
  lib/lp/translations/stories/buildfarm/xx-build-summary.txt

./lib/lp/registry/browser/tests/productseries-views.txt
      83: want exceeds 78 characters.
./lib/lp/registry/stories/webservice/xx-project-registry.txt
     570: source exceeds 78 characters.
    1254: 'IRepresentationCache' imported but unused
./lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py
     119: E302 expected 2 blank lines, found 1
./lib/lp/translations/browser/tests/test_breadcrumbs.py
      27: E501 line too long (81 characters)
      37: E501 line too long (86 characters)
      45: E501 line too long (81 characters)
      56: E501 line too long (86 characters)
      64: E501 line too long (81 characters)
      72: E501 line too long (82 characters)
      81: E501 line too long (82 characters)
      88: E501 line too long (81 characters)
     138: E301 expected 1 blank line, found 0
     152: E501 line too long (85 characters)
      27: Line exceeds 78 characters.
      37: Line exceeds 78 characters.
      45: Line exceeds 78 characters.
      56: Line exceeds 78 characters.
      64: Line exceeds 78 characters.
      72: Line exceeds 78 characters.
      81: Line exceeds 78 characters.
      88: Line exceeds 78 characters.
     116: Line exceeds 78 characters.
     133: Line exceeds 78 characters.
     152: Line exceeds 78 characters.
./lib/lp/translations/doc/translations-export-to-branch.txt
       0: narrative uses a moin header.
      15: want exceeds 78 characters.
     273: narrative uses a moin header.
     301: narrative uses a moin header.
     242: Could not compile:
             finally:
./lib/lp/translations/stories/buildfarm/xx-build-summary.txt
       0: narrative uses a moin header.
       5: narrative uses a moin header.
      64: narrative uses a moin header.

-- 
https://code.launchpad.net/~adeuring/launchpad/security-guarded-test-object-factory-1/+merge/30497
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~adeuring/launchpad/security-guarded-test-object-factory-1 into lp:launchpad/devel.
=== modified file 'lib/lp/code/browser/tests/test_branchlisting.py'
--- lib/lp/code/browser/tests/test_branchlisting.py	2010-05-28 09:54:45 +0000
+++ lib/lp/code/browser/tests/test_branchlisting.py	2010-07-21 09:04:53 +0000
@@ -32,12 +32,14 @@
 from lp.testing import (
     BrowserTestCase, TestCase, TestCaseWithFactory, login_person,
     person_logged_in, time_counter)
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from lp.testing.views import create_initialized_view
 from canonical.launchpad.testing.pages import extract_text, find_tag_by_id
 from canonical.launchpad.webapp import canonical_url
 from canonical.launchpad.webapp.servers import LaunchpadTestRequest
 from canonical.testing.layers import DatabaseFunctionalLayer
 
+
 class TestListingToSortOrder(TestCase):
     """Tests for the BranchSet._listingSortToOrderBy static method.
 
@@ -60,6 +62,7 @@
 
     def assertSortsEqual(self, sort_one, sort_two):
         """Assert that one list of sort specs is equal to another."""
+
         def sort_data(sort):
             return sort.suffix, sort.expr
         self.assertEqual(map(sort_data, sort_one), map(sort_data, sort_two))
@@ -352,7 +355,7 @@
         # series on the main site, not the code site.
         branch = self.factory.makeProductBranch()
         series = self.factory.makeProductSeries(product=branch.product)
-        series.branch = branch
+        remove_security_proxy_and_shout_at_engineer(series).branch = branch
         browser = self.getUserBrowser(
             canonical_url(branch.product, rootsite='code'))
         link = browser.getLink(re.compile('^' + series.name + '$'))
@@ -402,4 +405,3 @@
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromName(__name__)
-

=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2010-07-19 10:51:39 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2010-07-21 09:04:53 +0000
@@ -28,6 +28,7 @@
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.soyuz.model.processor import ProcessorFamily
 from lp.testing import ANONYMOUS, BrowserTestCase, login, logout
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 
 
 class TestCaseForRecipe(BrowserTestCase):
@@ -44,7 +45,9 @@
         self.squirrel = self.factory.makeDistroSeries(
             displayname='Secret Squirrel', name='secret', version='100.04',
             distribution=self.ppa.distribution)
-        self.squirrel.nominatedarchindep = self.squirrel.newArch(
+        naked_squirrel = remove_security_proxy_and_shout_at_engineer(
+            self.squirrel)
+        naked_squirrel.nominatedarchindep = self.squirrel.newArch(
             'i386', ProcessorFamily.get(1), False, self.chef,
             supports_virtualized=True)
 
@@ -492,7 +495,7 @@
     def makeBuildJob(self, recipe):
         """Return a build associated with a buildjob."""
         build = self.factory.makeSourcePackageRecipeBuild(
-            recipe=recipe, distroseries=self.squirrel, archive=self.ppa )
+            recipe=recipe, distroseries=self.squirrel, archive=self.ppa)
         self.factory.makeSourcePackageRecipeBuildJob(recipe_build=build)
         return build
 
@@ -525,6 +528,7 @@
         self.assertEqual(
             set([build1, build2, build3, build4, build5, build6]),
             set(view.builds))
+
         def set_day(build, day):
             removeSecurityProxy(build).datebuilt = datetime(
                 2010, 03, day, tzinfo=utc)
@@ -567,7 +571,8 @@
         woody = self.factory.makeDistroSeries(
             name='woody', displayname='Woody',
             distribution=self.ppa.distribution)
-        woody.nominatedarchindep = woody.newArch(
+        naked_woody = remove_security_proxy_and_shout_at_engineer(woody)
+        naked_woody.nominatedarchindep = woody.newArch(
             'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
             supports_virtualized=True)
 
@@ -599,7 +604,8 @@
         woody = self.factory.makeDistroSeries(
             name='woody', displayname='Woody',
             distribution=self.ppa.distribution)
-        woody.nominatedarchindep = woody.newArch(
+        naked_woody = remove_security_proxy_and_shout_at_engineer(woody)
+        naked_woody.nominatedarchindep = woody.newArch(
             'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
             supports_virtualized=True)
 
@@ -620,7 +626,8 @@
         woody = self.factory.makeDistroSeries(
             name='woody', displayname='Woody',
             distribution=self.ppa.distribution)
-        woody.nominatedarchindep = woody.newArch(
+        naked_woody = remove_security_proxy_and_shout_at_engineer(woody)
+        naked_woody.nominatedarchindep = woody.newArch(
             'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
             supports_virtualized=True)
 

=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py	2010-07-19 09:22:06 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipebuild.py	2010-07-21 09:04:53 +0000
@@ -18,6 +18,7 @@
 from lp.buildmaster.interfaces.buildbase import BuildStatus
 from lp.soyuz.model.processor import ProcessorFamily
 from lp.testing import ANONYMOUS, BrowserTestCase, login, logout
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 
 
 class TestSourcePackageRecipeBuild(BrowserTestCase):
@@ -36,7 +37,9 @@
         self.squirrel = self.factory.makeDistroSeries(
             displayname='Secret Squirrel', name='secret', version='100.04',
             distribution=self.ppa.distribution)
-        self.squirrel.nominatedarchindep = self.squirrel.newArch(
+        naked_squirrel = remove_security_proxy_and_shout_at_engineer(
+            self.squirrel)
+        naked_squirrel.nominatedarchindep = self.squirrel.newArch(
             'i386', ProcessorFamily.get(1), False, self.chef,
             supports_virtualized=True)
 

=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py	2010-07-17 23:15:30 +0000
+++ lib/lp/code/model/tests/test_branch.py	2010-07-21 09:04:53 +0000
@@ -537,7 +537,7 @@
         jobs = list(getUtility(IBranchUpgradeJobSource).iterReady())
         self.assertEqual(
             jobs,
-            [job,])
+            [job, ])
 
     def test_requestUpgrade_no_upgrade_needed(self):
         # If a branch doesn't need to be upgraded, requestUpgrade raises an
@@ -635,6 +635,7 @@
         branch = self.factory.makeProductBranch(
             product=fooix, owner=eric, name='trunk')
         linked_branch = ICanHasLinkedBranch(future)
+        login_person(fooix.owner)
         linked_branch.setBranch(branch)
         self.assertEqual(
             [linked_branch],
@@ -827,6 +828,7 @@
         product = branch.product
         series = self.factory.makeProductSeries(product=product)
         linked_branch = ICanHasLinkedBranch(series)
+        login_person(series.owner)
         linked_branch.setBranch(branch)
         self.assertBzrIdentity(branch, linked_branch.bzr_path)
 
@@ -852,6 +854,7 @@
             removeSecurityProxy(branch.product))
         series_link = ICanHasLinkedBranch(series)
         product_link.setBranch(branch)
+        login_person(series.owner)
         series_link.setBranch(branch)
         self.assertBzrIdentity(branch, product_link.bzr_path)
 

=== modified file 'lib/lp/code/model/tests/test_linkedbranch.py'
--- lib/lp/code/model/tests/test_linkedbranch.py	2010-04-16 15:06:55 +0000
+++ lib/lp/code/model/tests/test_linkedbranch.py	2010-07-21 09:04:53 +0000
@@ -18,6 +18,7 @@
 from lp.registry.interfaces.distroseries import NoSuchDistroSeries
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.testing import run_with_login, TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 
 
 class TestProductSeriesLinkedBranch(TestCaseWithFactory):
@@ -27,7 +28,9 @@
     def test_branch(self):
         # The linked branch of a product series is its branch attribute.
         product_series = self.factory.makeProductSeries()
-        product_series.branch = self.factory.makeProductBranch(
+        naked_product_series = remove_security_proxy_and_shout_at_engineer(
+            product_series)
+        naked_product_series.branch = self.factory.makeProductBranch(
             product=product_series.product)
         self.assertEqual(
             product_series.branch, ICanHasLinkedBranch(product_series).branch)
@@ -35,9 +38,11 @@
     def test_setBranch(self):
         # setBranch sets the linked branch of the product series.
         product_series = self.factory.makeProductSeries()
+        naked_product_series = remove_security_proxy_and_shout_at_engineer(
+            product_series)
         branch = self.factory.makeProductBranch(
             product=product_series.product)
-        ICanHasLinkedBranch(product_series).setBranch(branch)
+        ICanHasLinkedBranch(naked_product_series).setBranch(branch)
         self.assertEqual(branch, product_series.branch)
 
     def test_bzr_path(self):

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py	2010-06-18 19:36:55 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py	2010-07-21 09:04:53 +0000
@@ -49,6 +49,7 @@
 from lp.testing import (
     ANONYMOUS, launchpadlib_for, login, login_person, person_logged_in,
     TestCaseWithFactory, ws_object)
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 
 
 class TestSourcePackageRecipe(TestCaseWithFactory):
@@ -167,7 +168,6 @@
         branch2 = self.factory.makeAnyBranch()
         builder_recipe2 = self.factory.makeRecipe(branch2)
         login_person(sp_recipe.owner.teamowner)
-        #import pdb; pdb.set_trace()
         sp_recipe.builder_recipe = builder_recipe2
         self.assertEquals([branch2], list(sp_recipe.getReferencedBranches()))
 
@@ -210,7 +210,7 @@
 
     def test_requestBuild(self):
         recipe = self.factory.makeSourcePackageRecipe()
-        (distroseries,) = list(recipe.distroseries)
+        (distroseries, ) = list(recipe.distroseries)
         ppa = self.factory.makeArchive()
         build = recipe.requestBuild(ppa, ppa.owner, distroseries,
                 PackagePublishingPocket.RELEASE)
@@ -234,7 +234,7 @@
     def test_requestBuildRejectsNotPPA(self):
         recipe = self.factory.makeSourcePackageRecipe()
         not_ppa = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
-        (distroseries,) = list(recipe.distroseries)
+        (distroseries, ) = list(recipe.distroseries)
         self.assertRaises(NonPPABuildRequest, recipe.requestBuild, not_ppa,
                 not_ppa.owner, distroseries, PackagePublishingPocket.RELEASE)
 
@@ -242,14 +242,14 @@
         recipe = self.factory.makeSourcePackageRecipe()
         ppa = self.factory.makeArchive()
         requester = self.factory.makePerson()
-        (distroseries,) = list(recipe.distroseries)
+        (distroseries, ) = list(recipe.distroseries)
         self.assertRaises(CannotUploadToArchive, recipe.requestBuild, ppa,
                 requester, distroseries, PackagePublishingPocket.RELEASE)
 
     def test_requestBuildRejectsInvalidPocket(self):
         recipe = self.factory.makeSourcePackageRecipe()
         ppa = self.factory.makeArchive()
-        (distroseries,) = list(recipe.distroseries)
+        (distroseries, ) = list(recipe.distroseries)
         self.assertRaises(InvalidPocketForPPA, recipe.requestBuild, ppa,
                 ppa.owner, distroseries, PackagePublishingPocket.BACKPORTS)
 
@@ -257,7 +257,7 @@
         recipe = self.factory.makeSourcePackageRecipe()
         ppa = self.factory.makeArchive()
         removeSecurityProxy(ppa).disable()
-        (distroseries,) = list(recipe.distroseries)
+        (distroseries, ) = list(recipe.distroseries)
         self.assertRaises(ArchiveDisabled, recipe.requestBuild, ppa,
                 ppa.owner, distroseries, PackagePublishingPocket.RELEASE)
 
@@ -283,7 +283,7 @@
 
     def test_requestBuildHonoursConfig(self):
         recipe = self.factory.makeSourcePackageRecipe()
-        (distroseries,) = list(recipe.distroseries)
+        (distroseries, ) = list(recipe.distroseries)
         ppa = self.factory.makeArchive()
         self.pushConfig('build_from_branch', enabled=False)
         self.assertRaises(
@@ -297,6 +297,7 @@
             name=u'myrecipe', owner=requester)
         series = list(recipe.distroseries)[0]
         archive = self.factory.makeArchive(owner=requester)
+
         def request_build():
             build = recipe.requestBuild(archive, requester, series,
                     PackagePublishingPocket.RELEASE)
@@ -323,7 +324,9 @@
             series, PackagePublishingPocket.RELEASE)
         # Varying distroseries allows build.
         new_distroseries = self.factory.makeDistroSeries()
-        new_distroseries.nominatedarchindep = new_distroseries.newArch(
+        naked_new_distroseries = remove_security_proxy_and_shout_at_engineer(
+            new_distroseries)
+        naked_new_distroseries.nominatedarchindep = new_distroseries.newArch(
             'i386', ProcessorFamily.get(1), False, recipe.owner,
             supports_virtualized=True)
         recipe.requestBuild(archive, recipe.owner,
@@ -344,7 +347,7 @@
         """Test that the distroseries behaves as a set."""
         recipe = self.factory.makeSourcePackageRecipe()
         distroseries = self.factory.makeDistroSeries()
-        (old_distroseries,) = recipe.distroseries
+        (old_distroseries, ) = recipe.distroseries
         recipe.distroseries.add(distroseries)
         self.assertEqual(
             set([distroseries, old_distroseries]), set(recipe.distroseries))
@@ -430,6 +433,7 @@
         build.buildduration = timedelta(minutes=10)
         self.assertEqual(
             timedelta(minutes=10), recipe.getMedianBuildDuration())
+
         def addBuild(minutes):
             build = removeSecurityProxy(
                 self.factory.makeSourcePackageRecipeBuild(recipe=recipe))
@@ -695,7 +699,9 @@
         distroseries_i386 = distroseries.newArch(
             'i386', ProcessorFamily.get(1), False, person,
             supports_virtualized=True)
-        distroseries.nominatedarchindep = distroseries_i386
+        naked_distroseries = remove_security_proxy_and_shout_at_engineer(
+            distroseries)
+        naked_distroseries.nominatedarchindep = distroseries_i386
 
         recipe, user, launchpad = self.makeRecipe(person)
         distroseries = ws_object(launchpad, distroseries)
@@ -712,7 +718,9 @@
         distroseries_i386 = distroseries.newArch(
             'i386', ProcessorFamily.get(1), False, person,
             supports_virtualized=True)
-        distroseries.nominatedarchindep = distroseries_i386
+        naked_distroseries = remove_security_proxy_and_shout_at_engineer(
+            distroseries)
+        naked_distroseries.nominatedarchindep = distroseries_i386
 
         recipe, user, launchpad = self.makeRecipe(person)
         distroseries = ws_object(launchpad, distroseries)
@@ -733,7 +741,9 @@
         distroseries_i386 = distroseries.newArch(
             'i386', ProcessorFamily.get(1), False, person,
             supports_virtualized=True)
-        distroseries.nominatedarchindep = distroseries_i386
+        naked_distroseries = remove_security_proxy_and_shout_at_engineer(
+            distroseries)
+        naked_distroseries.nominatedarchindep = distroseries_i386
 
         recipe, user, launchpad = self.makeRecipe(person)
         distroseries = ws_object(launchpad, distroseries)

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2010-07-14 08:42:01 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2010-07-21 09:04:53 +0000
@@ -37,6 +37,7 @@
 from lp.soyuz.model.processor import ProcessorFamily
 from lp.soyuz.tests.soyuzbuilddhelpers import WaitingSlave
 from lp.testing import ANONYMOUS, login, person_logged_in, TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.mail_helpers import pop_notifications
 
@@ -53,7 +54,9 @@
         distroseries_i386 = distroseries.newArch(
             'i386', ProcessorFamily.get(1), False, person,
             supports_virtualized=True)
-        distroseries.nominatedarchindep = distroseries_i386
+        naked_distroseries = remove_security_proxy_and_shout_at_engineer(
+            distroseries)
+        naked_distroseries.nominatedarchindep = distroseries_i386
 
         return getUtility(ISourcePackageRecipeBuildSource).new(
             distroseries=distroseries,
@@ -250,9 +253,12 @@
             recipe.daily_build_archive, recipe.owner, first_distroseries,
             PackagePublishingPocket.RELEASE)
         second_distroseries = self.factory.makeDistroSeries()
-        second_distroseries.nominatedarchindep = second_distroseries.newArch(
-            'i386', ProcessorFamily.get(1), False, self.factory.makePerson(),
-            supports_virtualized=True)
+        naked_second_distroseries = (
+            remove_security_proxy_and_shout_at_engineer(second_distroseries))
+        naked_second_distroseries.nominatedarchindep = (
+            second_distroseries.newArch(
+                'i386', ProcessorFamily.get(1), False,
+                self.factory.makePerson(), supports_virtualized=True))
         recipe.distroseries.add(second_distroseries)
         builds = SourcePackageRecipeBuild.makeDailyBuilds()
         self.assertEqual(
@@ -274,6 +280,7 @@
             recipe=recipe, distroseries=series)
         self.factory.makeSourcePackageRecipeBuild(
             requester=requester, distroseries=series)
+
         def get_recent():
             Store.of(build).flush()
             return SourcePackageRecipeBuild.getRecentBuilds(
@@ -321,7 +328,7 @@
         removeSecurityProxy(build).buildstate = BuildStatus.FULLYBUILT
         IStore(build).flush()
         build.notify()
-        (message,) = pop_notifications()
+        (message, ) = pop_notifications()
         requester = build.requester
         requester_address = format_address(
             requester.displayname, requester.preferredemail.email)
@@ -332,11 +339,11 @@
         body, footer = message.get_payload(decode=True).split('\n-- \n')
         self.assertEqual(
             'Build person/recipe into ppa for distroseries: Successfully'
-            ' built.\n', body
-            )
+            ' built.\n', body)
 
     def test_handleStatusNotifies(self):
-        """"handleStatus causes notification, even if OK."""
+        """handleStatus causes notification, even if OK."""
+
         def prepare_build():
             queue_record = self.factory.makeSourcePackageRecipeBuildJob()
             build = queue_record.specific_job.build
@@ -345,6 +352,7 @@
             slave = WaitingSlave('BuildStatus.OK')
             queue_record.builder.setSlaveForTesting(slave)
             return build
+
         def assertNotifyOnce(status, build):
             build.handleStatus(status, None, {'filemap': {}})
             self.assertEqual(1, len(pop_notifications()))

=== modified file 'lib/lp/registry/browser/tests/distroseries-views.txt'
--- lib/lp/registry/browser/tests/distroseries-views.txt	2010-05-24 22:04:19 +0000
+++ lib/lp/registry/browser/tests/distroseries-views.txt	2010-07-21 09:04:53 +0000
@@ -196,7 +196,7 @@
     >>> [field.__name__ for field in view.form_fields]
     ['displayname', 'title', 'summary', 'description', 'status']
 
-    >>> print view.widgets.get('status')._getFormValue()
+    >>> print view.widgets.get('status')._getFormValue().title
     Active Development
 
 

=== modified file 'lib/lp/registry/browser/tests/milestone-views.txt'
--- lib/lp/registry/browser/tests/milestone-views.txt	2010-06-30 19:37:09 +0000
+++ lib/lp/registry/browser/tests/milestone-views.txt	2010-07-21 09:04:53 +0000
@@ -579,9 +579,13 @@
 The driver of a series that belongs to an `IDerivativeDistribution` is a
 release manager and can create milestones.
 
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> distroseries = factory.makeDistroRelease(name='pumpkin')
     >>> driver = factory.makePerson(name='a-driver')
-    >>> distroseries.driver = driver
+    >>> naked_distroseries = remove_security_proxy_and_shout_at_engineer(
+    ...     distroseries)
+    >>> naked_distroseries.driver = driver
     >>> login_person(driver)
 
     >>> form = {

=== modified file 'lib/lp/registry/browser/tests/productseries-views.txt'
--- lib/lp/registry/browser/tests/productseries-views.txt	2010-06-13 02:01:25 +0000
+++ lib/lp/registry/browser/tests/productseries-views.txt	2010-07-21 09:04:53 +0000
@@ -255,6 +255,8 @@
 
     >>> from datetime import datetime
     >>> from pytz import UTC
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
 
     >>> product = factory.makeProduct(name="field", displayname='Field')
     >>> productseries = factory.makeProductSeries(
@@ -263,7 +265,9 @@
 
     # Hack the creation date for testing purposes.
     >>> test_date = datetime(2009, 05, 01, 19, 34, 24, tzinfo=UTC)
-    >>> productseries.datecreated = test_date
+    >>> naked_productseries = remove_security_proxy_and_shout_at_engineer(
+    ...     productseries)
+    >>> naked_productseries.datecreated = test_date
 
 Users without edit permission cannot access the view.
 
@@ -470,7 +474,7 @@
 
 The series status is set to obsolete and the releasefileglob was set to None.
 
-    >>> print productseries.status
+    >>> print productseries.status.title
     Obsolete
     >>> print productseries.releasefileglob
     None

=== modified file 'lib/lp/registry/stories/webservice/xx-project-registry.txt'
--- lib/lp/registry/stories/webservice/xx-project-registry.txt	2010-06-14 18:32:58 +0000
+++ lib/lp/registry/stories/webservice/xx-project-registry.txt	2010-07-21 09:04:53 +0000
@@ -847,15 +847,17 @@
 The entry for a project series is available at its canonical URL on the
 virtual host.
 
+    >>> from zope.security.proxy import removeSecurityProxy
     >>> login('test@xxxxxxxxxxxxx')
     >>> babadoo_owner = factory.makePerson(name='babadoo-owner')
     >>> babadoo = factory.makeProduct(name='babadoo', owner=babadoo_owner)
     >>> foobadoo = factory.makeProductSeries(
     ...     product=babadoo, name='foobadoo', owner=babadoo_owner)
-    >>> foobadoo.summary = u'Foobadoo support for Babadoo'
+    >>> removeSecurityProxy(foobadoo).summary = (
+    ...     u'Foobadoo support for Babadoo')
     >>> fooey = factory.makeAnyBranch(
     ...     product=babadoo, name='fooey', owner=babadoo_owner)
-    >>> foobadoo.branch = fooey
+    >>> removeSecurityProxy(foobadoo).branch = fooey
     >>> logout()
 
     >>> babadoo_foobadoo = webservice.get('/babadoo/foobadoo').jsonBody()

=== modified file 'lib/lp/registry/tests/test_distroseries.py'
--- lib/lp/registry/tests/test_distroseries.py	2010-06-23 23:23:28 +0000
+++ lib/lp/registry/tests/test_distroseries.py	2010-07-21 09:04:53 +0000
@@ -24,6 +24,7 @@
     active_publishing_status, PackagePublishingStatus)
 from lp.soyuz.model.processor import ProcessorFamilySet
 from lp.testing import TestCase, TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.translations.interfaces.translations import (
     TranslationsBranchImportMode)
@@ -285,7 +286,9 @@
         self.linkPackage('hot')
         self.makeSeriesPackage('cold')
         product_series = self.linkPackage('cold')
-        product_series.product.bugtraker = self.factory.makeBugTracker()
+        naked_product_series = remove_security_proxy_and_shout_at_engineer(
+            product_series)
+        naked_product_series.product.bugtraker = self.factory.makeBugTracker()
         packagings = self.series.getPrioritizedlPackagings()
         names = [packaging.sourcepackagename.name for packaging in packagings]
         expected = [u'hot', u'cold', u'linked']
@@ -296,7 +299,9 @@
         self.linkPackage('translatable')
         self.makeSeriesPackage('withbranch')
         product_series = self.linkPackage('withbranch')
-        product_series.branch = self.factory.makeBranch()
+        naked_product_series = remove_security_proxy_and_shout_at_engineer(
+            product_series)
+        naked_product_series.branch = self.factory.makeBranch()
         packagings = self.series.getPrioritizedlPackagings()
         names = [packaging.sourcepackagename.name for packaging in packagings]
         expected = [u'translatable', u'linked', u'withbranch']
@@ -308,8 +313,10 @@
         self.linkPackage('translatable')
         self.makeSeriesPackage('importabletranslatable')
         product_series = self.linkPackage('importabletranslatable')
-        product_series.branch = self.factory.makeBranch()
-        product_series.translations_autoimport_mode = (
+        naked_product_series = remove_security_proxy_and_shout_at_engineer(
+            product_series)
+        naked_product_series.branch = self.factory.makeBranch()
+        naked_product_series.translations_autoimport_mode = (
             TranslationsBranchImportMode.IMPORT_TEMPLATES)
         packagings = self.series.getPrioritizedlPackagings()
         names = [packaging.sourcepackagename.name for packaging in packagings]
@@ -343,7 +350,9 @@
 
         new_distroseries = (
             self.factory.makeDistroRelease(name=u"sampleseries"))
-        new_distroseries.hide_all_translations = False
+        naked_new_distroseries = remove_security_proxy_and_shout_at_engineer(
+            new_distroseries)
+        naked_new_distroseries.hide_all_translations = False
         transaction.commit()
         translatables = self._get_translatables()
         self.failUnlessEqual(
@@ -365,7 +374,7 @@
                 translatables,
                 self._ref_translatables(u"sampleseries")))
 
-        new_distroseries.hide_all_translations = True
+        naked_new_distroseries.hide_all_translations = True
         transaction.commit()
         translatables = self._get_translatables()
         self.failUnlessEqual(

=== modified file 'lib/lp/registry/tests/test_sourcepackage.py'
--- lib/lp/registry/tests/test_sourcepackage.py	2010-02-24 03:06:54 +0000
+++ lib/lp/registry/tests/test_sourcepackage.py	2010-07-21 09:04:53 +0000
@@ -22,6 +22,7 @@
 from lp.code.interfaces.seriessourcepackagebranch import (
     IMakeOfficialBranchLinks)
 from lp.testing import TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from lp.testing.views import create_initialized_view
 from canonical.testing.layers import DatabaseFunctionalLayer
 
@@ -252,11 +253,17 @@
 
         self.obsolete_productseries = self.factory.makeProductSeries(
             name='obsolete', product=self.product)
-        self.obsolete_productseries.status = SeriesStatus.OBSOLETE
+        naked_obsolete_productseries = (
+            remove_security_proxy_and_shout_at_engineer(
+                self.obsolete_productseries))
+        naked_obsolete_productseries.status = SeriesStatus.OBSOLETE
 
         self.dev_productseries = self.factory.makeProductSeries(
             name='current', product=self.product)
-        self.dev_productseries.status = SeriesStatus.DEVELOPMENT
+        naked_dev_productseries = (
+            remove_security_proxy_and_shout_at_engineer(
+                self.dev_productseries))
+        naked_dev_productseries.status = SeriesStatus.DEVELOPMENT
 
         self.distribution = self.factory.makeDistribution(
             name='youbuntu', displayname='Youbuntu', owner=self.owner)

=== modified file 'lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py	2010-07-06 11:52:17 +0000
+++ lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py	2010-07-21 09:04:53 +0000
@@ -16,6 +16,7 @@
     DistributionSourcePackageRelease)
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from lp.testing.views import create_initialized_view
 
 
@@ -29,7 +30,11 @@
         # The package must be published for the page to render.
         stp = SoyuzTestPublisher()
         distroseries = stp.setUpDefaultDistroSeries()
-        distro = distroseries.distribution
+        naked_distroseries = remove_security_proxy_and_shout_at_engineer(
+            distroseries)
+        # XXX this is scary. But if we use distroseries.distribution
+        # instead, test_spr_files_deleted() and test_spr_files_one() fail.
+        distro = naked_distroseries.distribution
         source_package_release = stp.getPubSource().sourcepackagerelease
         self.dspr = DistributionSourcePackageRelease(
             distro, source_package_release)

=== modified file 'lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py	2010-07-02 17:51:33 +0000
+++ lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py	2010-07-21 09:04:53 +0000
@@ -18,6 +18,7 @@
 from canonical.testing import (
     DatabaseFunctionalLayer, LaunchpadFunctionalLayer)
 from lp.testing import TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from lp.testing.views import create_initialized_view
 
 
@@ -66,20 +67,23 @@
 
     def test_highlighted_copyright_is_None(self):
         expected = ''
-        self.source_package_release.copyright = None
+        remove_security_proxy_and_shout_at_engineer(
+            self.source_package_release).copyright = None
         view = create_initialized_view(
             self.source_package_release, '+copyright')
         self.assertEqual(expected, view.highlighted_copyright)
 
     def test_highlighted_copyright_no_matches(self):
         expected = 'nothing to see and/or do.'
-        self.source_package_release.copyright = expected
+        remove_security_proxy_and_shout_at_engineer(
+            self.source_package_release).copyright = expected
         view = create_initialized_view(
             self.source_package_release, '+copyright')
         self.assertEqual(expected, view.highlighted_copyright)
 
     def test_highlighted_copyright_match_url(self):
-        self.source_package_release.copyright = (
+        remove_security_proxy_and_shout_at_engineer(
+            self.source_package_release).copyright = (
             'Downloaded from https://upstream.dom/fnord/no/ and')
         expected = (
             'Downloaded from '
@@ -90,7 +94,8 @@
         self.assertEqual(expected, view.highlighted_copyright)
 
     def test_highlighted_copyright_match_path(self):
-        self.source_package_release.copyright = (
+        remove_security_proxy_and_shout_at_engineer(
+            self.source_package_release).copyright = (
             'See /usr/share/common-licenses/GPL')
         expected = (
             'See '
@@ -100,7 +105,8 @@
         self.assertEqual(expected, view.highlighted_copyright)
 
     def test_highlighted_copyright_match_multiple(self):
-        self.source_package_release.copyright = (
+        remove_security_proxy_and_shout_at_engineer(
+            self.source_package_release).copyright = (
             'See /usr/share/common-licenses/GPL or https://osi.org/mit')
         expected = (
             'See '

=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
--- lib/lp/soyuz/tests/test_publishing.py	2010-07-20 17:32:03 +0000
+++ lib/lp/soyuz/tests/test_publishing.py	2010-07-21 09:04:53 +0000
@@ -43,7 +43,8 @@
 from lp.soyuz.interfaces.queue import PackageUploadStatus
 from canonical.launchpad.scripts import FakeLogger
 from lp.testing import TestCaseWithFactory
-from lp.testing.factory import LaunchpadObjectFactory
+from lp.testing.factory import (
+    LaunchpadObjectFactory, remove_security_proxy_and_shout_at_engineer)
 
 
 class SoyuzTestPublisher:
@@ -135,7 +136,7 @@
                          changes_file_name="foo_666_source.changes",
                          changes_file_content="fake changes file content",
                          upload_status=PackageUploadStatus.DONE):
-        signing_key =  self.person.gpg_keys[0]
+        signing_key = self.person.gpg_keys[0]
         package_upload = distroseries.createQueueEntry(
             pocket, changes_file_name, changes_file_content, archive,
             signing_key)
@@ -144,7 +145,10 @@
             PackageUploadStatus.DONE: 'setDone',
             PackageUploadStatus.ACCEPTED: 'setAccepted',
             }
-        method = getattr(package_upload, status_to_method[upload_status])
+        naked_package_upload = remove_security_proxy_and_shout_at_engineer(
+            package_upload)
+        method = getattr(
+            naked_package_upload, status_to_method[upload_status])
         method()
 
         return package_upload
@@ -220,7 +224,9 @@
             changes_file_name=changes_file_name,
             changes_file_content=changes_file_content,
             upload_status=upload_status)
-        package_upload.addSource(spr)
+        naked_package_upload = remove_security_proxy_and_shout_at_engineer(
+            package_upload)
+        naked_package_upload.addSource(spr)
 
         if filename is None:
             filename = "%s_%s.dsc" % (sourcename, version)
@@ -472,6 +478,7 @@
         self.pool_dir = self.config.poolroot
         self.temp_dir = self.config.temproot
         self.logger = FakeLogger()
+
         def message(self, prefix, *stuff, **kw):
             pass
         self.logger.message = message
@@ -600,7 +607,7 @@
         pub_source.publish(self.disk_pool, self.logger)
         self.layer.commit()
         self.assertEqual(
-            pub_source.status,PackagePublishingStatus.PENDING)
+            pub_source.status, PackagePublishingStatus.PENDING)
         self.assertEqual(open(foo_dsc_path).read().strip(), 'Hello world')
 
     def testPublishingDifferentContents(self):
@@ -779,7 +786,7 @@
         try:
             copies = tuple(copied)
         except TypeError:
-            copies = (copied,)
+            copies = (copied, )
 
         for copy in copies:
             self.assertEquals(copy.component, pub_record.component)
@@ -889,7 +896,8 @@
         """Return a mock source package publishing record for the archive
         and architecture used in this testcase.
 
-        :param architecturehintlist: Architecture hint list (e.g. "i386 amd64")
+        :param architecturehintlist: Architecture hint list (e.g.
+        "i386 amd64")
         """
         return super(BuildRecordCreationTests, self).getPubSource(
             archive=self.archive, distroseries=self.distroseries,
@@ -930,7 +938,8 @@
 
     def test_createMissingBuilds_restricts_explicitlist(self):
         """createMissingBuilds() should limit builds targeted at a
-        variety of architectures architecture to those allowed for the archive.
+        variety of architectures architecture to those allowed for the
+        archive.
         """
         pubrec = self.getPubSource(architecturehintlist='sparc i386 avr')
         builds = pubrec.createMissingBuilds()

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2010-07-17 21:02:33 +0000
+++ lib/lp/testing/factory.py	2010-07-21 09:04:53 +0000
@@ -15,6 +15,7 @@
     'GPGSigningContext',
     'LaunchpadObjectFactory',
     'ObjectFactory',
+    'remove_security_proxy_and_shout_at_engineer',
     ]
 
 from contextlib import nested
@@ -28,15 +29,18 @@
 import os.path
 from random import randint
 from StringIO import StringIO
+import sys
 from textwrap import dedent
 from threading import local
+from types import InstanceType
 
 import pytz
 
 from twisted.python.util import mergeFunctionMetadata
 
 from zope.component import ComponentLookupError, getUtility
-from zope.security.proxy import removeSecurityProxy
+from zope.security.proxy import (
+    builtin_isinstance, Proxy, ProxyFactory, removeSecurityProxy)
 
 from canonical.autodecorate import AutoDecorate
 from canonical.config import config
@@ -325,7 +329,7 @@
             branch_id, rcstype, url, cvs_root, cvs_module)
 
 
-class LaunchpadObjectFactory(ObjectFactory):
+class _LaunchpadObjectFactory(ObjectFactory):
     """Factory methods for creating Launchpad objects.
 
     All the factory methods should be callable with no parameters.
@@ -744,8 +748,8 @@
         # We don't want to login() as the person used to create the product,
         # so we remove the security proxy before creating the series.
         naked_product = removeSecurityProxy(product)
-        return naked_product.newSeries(owner=owner, name=name,
-                                       summary=summary)
+        return ProxyFactory(
+            naked_product.newSeries(owner=owner, name=name, summary=summary))
 
     def makeProject(self, name=None, displayname=None, title=None,
                     homepageurl=None, summary=None, owner=None,
@@ -828,7 +832,7 @@
                 url = self.getUniqueURL()
         else:
             raise UnknownBranchTypeError(
-                'Unrecognized branch type: %r' % (branch_type,))
+                'Unrecognized branch type: %r' % (branch_type, ))
 
         namespace = get_branch_namespace(
             owner, product=product, distroseries=distroseries,
@@ -1671,7 +1675,7 @@
             description=self.getUniqueString(),
             parent_series=parent_series, owner=distribution.owner)
         series.status = status
-        return series
+        return ProxyFactory(series)
 
     # Most people think of distro releases as distro series.
     makeDistroSeries = makeDistroRelease
@@ -1780,7 +1784,7 @@
 
     def makeRecipeText(self, *branches):
         if len(branches) == 0:
-            branches = (self.makeAnyBranch(),)
+            branches = (self.makeAnyBranch(), )
         base_branch = branches[0]
         other_branches = branches[1:]
         text = MINIMAL_RECIPE_TEXT % base_branch.bzr_identity
@@ -1810,9 +1814,11 @@
             owner = self.makePerson()
         if distroseries is None:
             distroseries = self.makeDistroSeries()
-            distroseries.nominatedarchindep = distroseries.newArch(
-                'i386', ProcessorFamily.get(1), False, owner,
-                supports_virtualized=True)
+            naked_distroseries = removeSecurityProxy(distroseries)
+            naked_distroseries.nominatedarchindep = (
+                naked_distroseries.newArch(
+                    'i386', ProcessorFamily.get(1), False, owner,
+                    supports_virtualized=True))
 
         if name is None:
             name = self.getUniqueString().decode('utf8')
@@ -1903,6 +1909,7 @@
                  ddd57463774cae9b50e70cd51221281b 185913 ed_0.2.orig.tar.gz
                  f9e1e5f13725f581919e9bfd62272a05 8506 ed_0.2-20.diff.gz
                 """))
+
             class Changes:
                 architectures = ['source']
             logger = QuietFakeLogger()
@@ -1936,7 +1943,7 @@
             productseries = self.makeProductSeries(owner=owner)
             # Make it use Translations, otherwise there's little point
             # to us creating a template for it.
-            productseries.product.official_rosetta = True
+            removeSecurityProxy(productseries).product.official_rosetta = True
         templateset = getUtility(IPOTemplateSet)
         subset = templateset.getSubset(
             distroseries, sourcepackagename, productseries)
@@ -2711,3 +2718,56 @@
         new_uuid = getUtility(ITemporaryStorageManager).new(blob, expires)
 
         return getUtility(ITemporaryStorageManager).fetch(new_uuid)
+
+
+class LaunchpadObjectFactory:
+    """A wrapper around _LaunchpadObjectFactory.
+
+    Ensure that each object created by a _LaunchpadObjectFactory method
+    is either of a simple Python type or is security proxied.
+
+    A warning message s printed to stderr if a factory method creates
+    an object without a security proxy.
+    """
+
+    def __init__(self):
+        self._factory = _LaunchpadObjectFactory()
+
+    def __getattr__(self, name):
+        attr = getattr(self._factory, name)
+        if callable(attr):
+
+            def guarded_method(*args, **kw):
+                result = attr(*args, **kw)
+                if builtin_isinstance(result, Proxy):
+                    return result
+                if result is None:
+                    return result
+                if type(result) not in (
+                    int, str, unicode, Message, DSCFile, InstanceType, tuple,
+                    datetime):
+                    message = (
+                        "Unproxied object returned by "
+                        "LaunchpadObjectFactory.%s" % name)
+                    print >>sys.stderr, message
+                return result
+            return guarded_method
+        else:
+            return attr
+
+
+def remove_security_proxy_and_shout_at_engineer(obj):
+    """Remove an object's security proxy and print a warning.
+
+    A number of LaunchpadObjectFactory methods returned objects without
+    a security proxy. This is now no longer possible, but a number of
+    tests rely on unrestricted access to object attributes.
+
+    This function should only beused in existing tests if a test fails
+    because a newer version of LaunchpadObjectFactory returns a security
+    proxied object.
+    """
+    print >>sys.stderr, (
+        "\ncalled removeSecurityProxy() for %r without a check if this "
+        "reasonable" % obj)
+    return removeSecurityProxy(obj)

=== modified file 'lib/lp/translations/browser/tests/test_breadcrumbs.py'
--- lib/lp/translations/browser/tests/test_breadcrumbs.py	2010-04-28 10:13:00 +0000
+++ lib/lp/translations/browser/tests/test_breadcrumbs.py	2010-07-21 09:04:53 +0000
@@ -9,6 +9,7 @@
 
 from lp.services.worlddata.interfaces.language import ILanguageSet
 from lp.testing.breadcrumbs import BaseBreadcrumbTestCase
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from lp.translations.interfaces.distroserieslanguage import (
     IDistroSeriesLanguageSet)
 from lp.translations.interfaces.productserieslanguage import (
@@ -101,7 +102,8 @@
             name='crumb-tester', displayname="Crumb Tester")
         series = self.factory.makeDistroRelease(
             name="test", version="1.0", distribution=distribution)
-        series.hide_all_translations = False
+        naked_series = remove_security_proxy_and_shout_at_engineer(series)
+        naked_series.hide_all_translations = False
         serieslanguage = getUtility(IDistroSeriesLanguageSet).getDummy(
             series, self.language)
 

=== modified file 'lib/lp/translations/doc/translations-export-to-branch.txt'
--- lib/lp/translations/doc/translations-export-to-branch.txt	2010-06-16 07:22:57 +0000
+++ lib/lp/translations/doc/translations-export-to-branch.txt	2010-07-21 09:04:53 +0000
@@ -220,8 +220,12 @@
     >>> from email import message_from_string
     >>> from lp.services.mail import stub
     >>> from lp.codehosting.vfs import get_rw_server
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> productseries = factory.makeProductSeries()
-    >>> productseries.translations_branch = factory.makeBranch()
+    >>> naked_productseries = remove_security_proxy_and_shout_at_engineer(
+    ...     productseries)
+    >>> naked_productseries.translations_branch = factory.makeBranch()
     >>> template = factory.makePOTemplate(productseries=productseries)
     >>> potmsgset = factory.makePOTMsgSet(template)
     >>> pofile = removeSecurityProxy(

=== modified file 'lib/lp/translations/stories/buildfarm/xx-build-summary.txt'
--- lib/lp/translations/stories/buildfarm/xx-build-summary.txt	2010-03-16 11:09:46 +0000
+++ lib/lp/translations/stories/buildfarm/xx-build-summary.txt	2010-07-21 09:04:53 +0000
@@ -14,6 +14,8 @@
     ...     ILibraryFileAliasSet)
     >>> from canonical.launchpad.scripts.logger import QuietFakeLogger
     >>> from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> from lp.testing.fakemethod import FakeMethod
     >>> from lp.translations.interfaces.translations import (
     ...     TranslationsBranchImportMode)
@@ -29,12 +31,15 @@
 
     >>> productseries = factory.makeProductSeries(owner=owner)
     >>> product = productseries.product
-    >>> product.official_rosetta = True
+    >>> naked_product = remove_security_proxy_and_shout_at_engineer(product)
+    >>> naked_product.official_rosetta = True
     >>> branch = factory.makeProductBranch(product=product, owner=owner)
     >>> branch_url = branch.unique_name
 
-    >>> productseries.branch = factory.makeBranch()
-    >>> productseries.translations_autoimport_mode = (
+    >>> naked_productseries = remove_security_proxy_and_shout_at_engineer(
+    ...     productseries)
+    >>> naked_productseries.branch = factory.makeBranch()
+    >>> naked_productseries.translations_autoimport_mode = (
     ...     TranslationsBranchImportMode.IMPORT_TEMPLATES)
     >>> specific_job = factory.makeTranslationTemplatesBuildJob(branch=branch)
     >>> buildqueue = getUtility(IBuildQueueSet).getByJob(specific_job.job)