← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #608624 add a security proxy to the objects returned by LaunchpadObjectFactory.makeDiff() and LaunchpadObjectFactory.makeCopyArchiveLocation()
  https://bugs.launchpad.net/bugs/608624


This branch is the second of series with the final goal that all
methods of LaunchpadObjectFactory will return security proxied
objects. Basically, it ensures that the methods
LaunchpadObjectFactory.makeDiff() and
LaunchpadObjectFactory.makeCopyArchiveLocation() return security
proxied objects.

The remaining changes are only calls of the function
remove_security_proxy_and_shout_at_engineer() where necessary and
some lint removal.

See also the merge proposal for the base branch:
https://code.edge.launchpad.net/~adeuring/launchpad/security-guarded-test-object-factory-1/+merge/30497

tests:
./bin/test -vvt lp.soyuz.adapters.tests.test_packagelocation
./bin/test -vvt lp.soyuz.browser.tests.archive-views.txt
./bin/test -vvt lp.soyuz.tests....doc.archive.txt
./bin/test -vvt xx-distribution-archives.txt

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/adapters/tests/test_packagelocation.py
  lib/lp/soyuz/browser/tests/archive-views.txt
  lib/lp/soyuz/doc/archive.txt
  lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt
  lib/lp/testing/factory.py

./lib/lp/soyuz/browser/tests/archive-views.txt
       0: narrative uses a moin header.
       8: narrative uses a moin header.
     472: narrative uses a moin header.
     515: narrative uses a moin header.
     642: narrative uses a moin header.
     743: want exceeds 78 characters.
    1025: narrative uses a moin header.
    1302: narrative uses a moin header.
    1368: narrative uses a moin header.
./lib/lp/soyuz/doc/archive.txt
       0: narrative uses a moin header.
     120: narrative exceeds 78 characters.
     282: narrative uses a moin header.
     444: narrative uses a moin header.
     674: narrative uses a moin header.
     714: narrative uses a moin header.
     760: narrative uses a moin header.
     826: narrative uses a moin header.
     933: narrative uses a moin header.
     952: narrative uses a moin header.
    1031: narrative uses a moin header.
    1087: narrative uses a moin header.
    1282: narrative uses a moin header.
    1318: narrative uses a moin header.
    1352: source has bad indentation.
    1854: narrative uses a moin header.
    1998: narrative has trailing whitespace.
    2024: narrative has trailing whitespace.
    2025: narrative has trailing whitespace.
    2037: narrative uses a moin header.
    2054: narrative uses a moin header.
    2057: narrative exceeds 78 characters.
    2071: source exceeds 78 characters.
    2077: narrative exceeds 78 characters.
    2092: narrative exceeds 78 characters.
    2243: narrative uses a moin header.
    2314: narrative uses a moin header.
    2521: narrative uses a moin header.
    2560: narrative uses a moin header.
    2640: narrative uses a moin header.
    2672: narrative uses a moin header.
    2704: narrative uses a moin header.
    2749: narrative uses a moin header.
./lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt
       0: narrative uses a moin header.
./lib/lp/testing/factory.py
     157: 'logout' imported but unused
     837: E231 missing whitespace after ','
    2734: E302 expected 2 blank lines, found 1
    2761: E301 expected 1 blank line, found 0
    1043: Line exceeds 78 characters.
    2735: Line exceeds 78 characters.

I  simply did not bother to fix these already existing lint issues.

This branch is based on
lp:~adeuring/launchpad/security-guarded-test-object-factory-1 (reviewed
and approved by jtv) which did not land due to the recent buildbot
failure. The diff against this branch:

=== modified file 'lib/lp/soyuz/adapters/tests/test_packagelocation.py'
--- lib/lp/soyuz/adapters/tests/test_packagelocation.py	2010-06-22 12:08:51 +0000
+++ lib/lp/soyuz/adapters/tests/test_packagelocation.py	2010-07-21 09:52:05 +0000
@@ -12,8 +12,10 @@
 from lp.soyuz.interfaces.archive import ArchivePurpose
 from lp.soyuz.interfaces.component import IComponentSet
 from lp.testing import TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from canonical.testing import LaunchpadZopelessLayer
 
+
 class TestPackageLocation(TestCaseWithFactory):
     """Test the `PackageLocation` class."""
     layer = LaunchpadZopelessLayer
@@ -34,7 +36,8 @@
         returned_location = self.factory.makeCopyArchiveLocation(
             distribution=ubuntu, name='now-comes-the-mystery',
             owner=self.factory.makePerson(name='mysteryman'))
-        copy_archive = returned_location.archive
+        copy_archive = remove_security_proxy_and_shout_at_engineer(
+            returned_location).archive
 
         # Now use the created copy archive to test the build_package_location
         # helper (called via getPackageLocation):

=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
--- lib/lp/soyuz/browser/tests/archive-views.txt	2010-05-21 10:59:09 +0000
+++ lib/lp/soyuz/browser/tests/archive-views.txt	2010-07-21 09:55:43 +0000
@@ -15,11 +15,14 @@
 
     >>> from lp.registry.interfaces.distribution import (
     ...     IDistributionSet)
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
     >>> copy_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu,
     ...     name="intrepid-security-rebuild")
-    >>> copy_archive = copy_location.archive
+    >>> copy_archive = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location).archive
 
 And let's create two views to compare:
 
@@ -65,8 +68,10 @@
     0
 
     # Create a copy-request to Celso's PPA.
+    >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location)
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_location, copy_archive.owner)
+    ...     naked_copy_location, copy_archive.owner)
 
     >>> len(copy_archive_view.package_copy_requests)
     1

=== modified file 'lib/lp/soyuz/doc/archive.txt'
--- lib/lp/soyuz/doc/archive.txt	2010-07-21 09:41:48 +0000
+++ lib/lp/soyuz/doc/archive.txt	2010-07-21 15:47:37 +0000
@@ -1285,10 +1285,15 @@
 The IArchive interface includes a convenience method for creating a
 package copy request:
 
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> requestor = factory.makePerson(name='me-copy')
     >>> copy_target = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu, name='my-copy-archive', owner=requestor)
-    >>> pcr = ubuntu.main_archive.requestPackageCopy(copy_target, requestor)
+    >>> naked_copy_target = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_target)
+    >>> pcr = ubuntu.main_archive.requestPackageCopy(
+    ...     naked_copy_target, requestor)
     >>> print pcr
     Package copy request
     source = primary/hoary/-/RELEASE
@@ -1301,7 +1306,7 @@
 The requestPackageCopy method can also take an optional suite name:
 
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_target, requestor, suite="hoary-updates");
+    ...     naked_copy_target, requestor, suite="hoary-updates");
     >>> print package_copy_request
     Package copy request
     source = primary/hoary/-/UPDATES
@@ -1412,9 +1417,9 @@
 
 COPY archives use a URL format of <distro-name>-<archive-name>:
 
-    >>> print copy_target.archive.is_copy
+    >>> print naked_copy_target.archive.is_copy
     True
-    >>> print copy_target.archive.archive_url
+    >>> print naked_copy_target.archive.archive_url
     http://rebuild-test.internal/ubuntu-my-copy-archive/ubuntu
 
 If the archive is private, the url may be different as private PPAs

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt	2010-01-12 08:24:36 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt	2010-07-21 10:15:28 +0000
@@ -6,6 +6,8 @@
     >>> from canonical.launchpad.interfaces import (
     ...     IDistributionSet)
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
 
@@ -13,18 +15,21 @@
     >>> copy_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu, owner=joe,
     ...     name="intrepid-security-rebuild")
-    >>> copy_archive = copy_location.archive
+    >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location)
+    >>> copy_archive = naked_copy_location.archive
     >>> copy_archive.enabled
     True
 
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_location, copy_archive.owner)
+    ...     naked_copy_location, copy_archive.owner)
 
     >>> nopriv = getUtility(IPersonSet).getByName('no-priv')
     >>> disabled_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu, owner=nopriv,
     ...     name="disabled-security-rebuild", enabled=False)
-    >>> disabled_archive = disabled_location.archive
+    >>> disabled_archive = remove_security_proxy_and_shout_at_engineer(
+    ...     disabled_location).archive
     >>> disabled_archive.enabled
     False
 
@@ -119,12 +124,14 @@
     >>> copy_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu,
     ...     name="intrepid-private-security-rebuild")
-    >>> copy_archive = copy_location.archive
+    >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location)
+    >>> copy_archive = naked_copy_location.archive
     >>> copy_archive.buildd_secret = 'really secret'
     >>> copy_archive.private = True
     >>> copy_archive.owner.displayname = "Harry Potter"
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_location, copy_archive.owner)
+    ...     naked_copy_location, copy_archive.owner)
     >>> pub_src = stp.getPubSource(
     ...     archive=copy_archive, architecturehintlist='any')
     >>> pub_bins = stp.getPubBinaries(pub_source=pub_src)

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2010-07-21 15:43:07 +0000
+++ lib/lp/testing/factory.py	2010-07-21 15:47:37 +0000
@@ -362,7 +362,7 @@
 
         location = PackageLocation(copy_archive, distribution, distroseries,
             pocket)
-        return location
+        return ProxyFactory(location)
 
     def makeAccount(self, displayname, email=None, password=None,
                     status=AccountStatus.ACTIVE,
@@ -1040,7 +1040,7 @@
             CodeReviewNotificationLevel.NOEMAIL, subscribed_by)
 
     def makeDiff(self, diff_text=DIFF):
-        return Diff.fromFile(StringIO(diff_text), len(diff_text))
+        return ProxyFactory(Diff.fromFile(StringIO(diff_text), len(diff_text)))
 
     def makePreviewDiff(self, conflicts=u''):
         diff = self.makeDiff()


-- 
https://code.launchpad.net/~adeuring/launchpad/security-guarded-test-object-factory-2/+merge/30642
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~adeuring/launchpad/security-guarded-test-object-factory-2 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-22 12:31:49 +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-21 21:16:54 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2010-07-22 12:31:49 +0000
@@ -29,6 +29,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):
@@ -45,7 +46,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)
 
@@ -494,7 +497,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
 
@@ -527,6 +530,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)
@@ -569,7 +573,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)
 
@@ -603,7 +608,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)
 
@@ -624,7 +630,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-22 12:31:49 +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-22 12:31:49 +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-22 12:31:49 +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_sourcepackagerecipebuild.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2010-07-20 12:12:46 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2010-07-22 12:31:49 +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,
@@ -320,7 +323,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)

=== 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-22 12:31:49 +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-22 12:31:49 +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-22 12:31:49 +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-22 12:31:49 +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-22 12:31:49 +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-22 12:31:49 +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/adapters/tests/test_packagelocation.py'
--- lib/lp/soyuz/adapters/tests/test_packagelocation.py	2010-06-22 12:08:51 +0000
+++ lib/lp/soyuz/adapters/tests/test_packagelocation.py	2010-07-22 12:31:49 +0000
@@ -12,8 +12,10 @@
 from lp.soyuz.interfaces.archive import ArchivePurpose
 from lp.soyuz.interfaces.component import IComponentSet
 from lp.testing import TestCaseWithFactory
+from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
 from canonical.testing import LaunchpadZopelessLayer
 
+
 class TestPackageLocation(TestCaseWithFactory):
     """Test the `PackageLocation` class."""
     layer = LaunchpadZopelessLayer
@@ -34,7 +36,8 @@
         returned_location = self.factory.makeCopyArchiveLocation(
             distribution=ubuntu, name='now-comes-the-mystery',
             owner=self.factory.makePerson(name='mysteryman'))
-        copy_archive = returned_location.archive
+        copy_archive = remove_security_proxy_and_shout_at_engineer(
+            returned_location).archive
 
         # Now use the created copy archive to test the build_package_location
         # helper (called via getPackageLocation):

=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
--- lib/lp/soyuz/browser/tests/archive-views.txt	2010-05-21 10:59:09 +0000
+++ lib/lp/soyuz/browser/tests/archive-views.txt	2010-07-22 12:31:49 +0000
@@ -15,11 +15,14 @@
 
     >>> from lp.registry.interfaces.distribution import (
     ...     IDistributionSet)
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
     >>> copy_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu,
     ...     name="intrepid-security-rebuild")
-    >>> copy_archive = copy_location.archive
+    >>> copy_archive = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location).archive
 
 And let's create two views to compare:
 
@@ -65,8 +68,10 @@
     0
 
     # Create a copy-request to Celso's PPA.
+    >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location)
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_location, copy_archive.owner)
+    ...     naked_copy_location, copy_archive.owner)
 
     >>> len(copy_archive_view.package_copy_requests)
     1

=== modified file 'lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py'
--- lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py	2010-07-20 12:06:36 +0000
+++ lib/lp/soyuz/browser/tests/test_distrosourcepackagerelease.py	2010-07-22 12:31:49 +0000
@@ -14,6 +14,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
 
 
@@ -27,7 +28,12 @@
         # 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 Abel Deuring, 2010-07-21, bug 608240. 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-20 12:06:36 +0000
+++ lib/lp/soyuz/browser/tests/test_sourcepackagerelease.py	2010-07-22 12:31:49 +0000
@@ -16,6 +16,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
 
 
@@ -64,20 +65,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 '
@@ -88,7 +92,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 '
@@ -98,7 +103,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/doc/archive.txt'
--- lib/lp/soyuz/doc/archive.txt	2010-07-21 09:41:48 +0000
+++ lib/lp/soyuz/doc/archive.txt	2010-07-22 12:31:49 +0000
@@ -1285,10 +1285,15 @@
 The IArchive interface includes a convenience method for creating a
 package copy request:
 
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> requestor = factory.makePerson(name='me-copy')
     >>> copy_target = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu, name='my-copy-archive', owner=requestor)
-    >>> pcr = ubuntu.main_archive.requestPackageCopy(copy_target, requestor)
+    >>> naked_copy_target = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_target)
+    >>> pcr = ubuntu.main_archive.requestPackageCopy(
+    ...     naked_copy_target, requestor)
     >>> print pcr
     Package copy request
     source = primary/hoary/-/RELEASE
@@ -1301,7 +1306,7 @@
 The requestPackageCopy method can also take an optional suite name:
 
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_target, requestor, suite="hoary-updates");
+    ...     naked_copy_target, requestor, suite="hoary-updates");
     >>> print package_copy_request
     Package copy request
     source = primary/hoary/-/UPDATES
@@ -1412,9 +1417,9 @@
 
 COPY archives use a URL format of <distro-name>-<archive-name>:
 
-    >>> print copy_target.archive.is_copy
+    >>> print naked_copy_target.archive.is_copy
     True
-    >>> print copy_target.archive.archive_url
+    >>> print naked_copy_target.archive.archive_url
     http://rebuild-test.internal/ubuntu-my-copy-archive/ubuntu
 
 If the archive is private, the url may be different as private PPAs

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt	2010-01-12 08:24:36 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt	2010-07-22 12:31:49 +0000
@@ -6,6 +6,8 @@
     >>> from canonical.launchpad.interfaces import (
     ...     IDistributionSet)
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.testing.factory import (
+    ...     remove_security_proxy_and_shout_at_engineer)
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
 
@@ -13,18 +15,21 @@
     >>> copy_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu, owner=joe,
     ...     name="intrepid-security-rebuild")
-    >>> copy_archive = copy_location.archive
+    >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location)
+    >>> copy_archive = naked_copy_location.archive
     >>> copy_archive.enabled
     True
 
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_location, copy_archive.owner)
+    ...     naked_copy_location, copy_archive.owner)
 
     >>> nopriv = getUtility(IPersonSet).getByName('no-priv')
     >>> disabled_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu, owner=nopriv,
     ...     name="disabled-security-rebuild", enabled=False)
-    >>> disabled_archive = disabled_location.archive
+    >>> disabled_archive = remove_security_proxy_and_shout_at_engineer(
+    ...     disabled_location).archive
     >>> disabled_archive.enabled
     False
 
@@ -119,12 +124,14 @@
     >>> copy_location = factory.makeCopyArchiveLocation(
     ...     distribution=ubuntu,
     ...     name="intrepid-private-security-rebuild")
-    >>> copy_archive = copy_location.archive
+    >>> naked_copy_location = remove_security_proxy_and_shout_at_engineer(
+    ...     copy_location)
+    >>> copy_archive = naked_copy_location.archive
     >>> copy_archive.buildd_secret = 'really secret'
     >>> copy_archive.private = True
     >>> copy_archive.owner.displayname = "Harry Potter"
     >>> package_copy_request = ubuntu.main_archive.requestPackageCopy(
-    ...     copy_location, copy_archive.owner)
+    ...     naked_copy_location, copy_archive.owner)
     >>> pub_src = stp.getPubSource(
     ...     archive=copy_archive, architecturehintlist='any')
     >>> pub_bins = stp.getPubBinaries(pub_source=pub_src)

=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
--- lib/lp/soyuz/tests/test_publishing.py	2010-07-21 14:14:54 +0000
+++ lib/lp/soyuz/tests/test_publishing.py	2010-07-22 12:31:49 +0000
@@ -44,7 +44,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)
 from lp.testing.fakemethod import FakeMethod
 
 
@@ -146,7 +147,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
@@ -222,7 +226,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)

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2010-07-21 14:41:26 +0000
+++ lib/lp/testing/factory.py	2010-07-22 12:31:49 +0000
@@ -13,8 +13,10 @@
 __metaclass__ = type
 __all__ = [
     'GPGSigningContext',
+    'is_security_proxied_or_harmless',
     'LaunchpadObjectFactory',
     'ObjectFactory',
+    'remove_security_proxy_and_shout_at_engineer',
     ]
 
 from contextlib import nested
@@ -25,18 +27,22 @@
 from email.mime.text import MIMEText
 from email.mime.multipart import MIMEMultipart
 from itertools import count
+from operator import isSequenceType
 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 +331,7 @@
             branch_id, rcstype, url, cvs_root, cvs_module)
 
 
-class LaunchpadObjectFactory(ObjectFactory):
+class BareLaunchpadObjectFactory(ObjectFactory):
     """Factory methods for creating Launchpad objects.
 
     All the factory methods should be callable with no parameters.
@@ -356,7 +362,7 @@
 
         location = PackageLocation(copy_archive, distribution, distroseries,
             pocket)
-        return location
+        return ProxyFactory(location)
 
     def makeAccount(self, displayname, email=None, password=None,
                     status=AccountStatus.ACTIVE,
@@ -744,8 +750,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,
@@ -1034,7 +1040,7 @@
             CodeReviewNotificationLevel.NOEMAIL, subscribed_by)
 
     def makeDiff(self, diff_text=DIFF):
-        return Diff.fromFile(StringIO(diff_text), len(diff_text))
+        return ProxyFactory(Diff.fromFile(StringIO(diff_text), len(diff_text)))
 
     def makePreviewDiff(self, conflicts=u''):
         diff = self.makeDiff()
@@ -1671,7 +1677,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 +1786,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
@@ -1943,7 +1949,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)
@@ -2718,3 +2724,72 @@
         new_uuid = getUtility(ITemporaryStorageManager).new(blob, expires)
 
         return getUtility(ITemporaryStorageManager).fetch(new_uuid)
+
+
+# Some factory methods return simple Python types. We don't add
+# security wrappers for them.
+unwrapped_types = (
+    DSCFile, InstanceType, Message, datetime, int, str, unicode)
+
+def is_security_proxied_or_harmless(obj):
+    """Check that the given object is security wrapped or a harmless object."""
+    if obj is None:
+        return True
+    if builtin_isinstance(obj, Proxy):
+        return True
+    if type(obj) in unwrapped_types:
+        return True
+    if isSequenceType(obj):
+        for element in obj:
+            if not is_security_proxied_or_harmless(element):
+                return False
+        return True
+    return False
+
+
+class LaunchpadObjectFactory:
+    """A wrapper around `BareLaunchpadObjectFactory`.
+
+    Ensure that each object created by a `BareLaunchpadObjectFactory` method
+    is either of a simple Python type or is security proxied.
+
+    A warning message is printed to stderr if a factory method creates
+    an object without a security proxy.
+
+    Whereever you see such a warning: fix it!
+    """
+    def __init__(self):
+        self._factory = BareLaunchpadObjectFactory()
+
+    def __getattr__(self, name):
+        attr = getattr(self._factory, name)
+        if callable(attr):
+
+            def guarded_method(*args, **kw):
+                result = attr(*args, **kw)
+                if not is_security_proxied_or_harmless(result):
+                    message = (
+                        "PLEASE FIX: LaunchpadObjectFactory.%s returns an "
+                        "unproxied object." % 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 be used in legacy tests which fail because
+    they expect unproxied objects.
+    """
+    print >>sys.stderr, (
+        "\nWarning: called removeSecurityProxy() for %r without a check if "
+        "this reasonable. Look for a call of "
+        "remove_security_proxy_and_shout_at_engineer(some_object)." % obj)
+    return removeSecurityProxy(obj)

=== modified file 'lib/lp/testing/tests/test_factory.py'
--- lib/lp/testing/tests/test_factory.py	2010-07-17 18:40:02 +0000
+++ lib/lp/testing/tests/test_factory.py	2010-07-22 12:31:49 +0000
@@ -8,11 +8,13 @@
 import unittest
 
 from zope.component import getUtility
+from zope.security.proxy import removeSecurityProxy
 
 from canonical.launchpad.webapp.interfaces import ILaunchBag
 from canonical.testing.layers import DatabaseFunctionalLayer
 from lp.code.enums import CodeImportReviewStatus
 from lp.testing import TestCaseWithFactory
+from lp.testing.factory import is_security_proxied_or_harmless
 
 
 class TestFactory(TestCaseWithFactory):
@@ -39,6 +41,54 @@
         self.assertIsNot(None, person)
         self.assertEqual(person, current_person)
 
+    def test_is_security_proxied_or_harmless__none(self):
+        # is_security_proxied_or_harmless() considers the None object
+        # to be a harmless object.
+        self.assertTrue(is_security_proxied_or_harmless(None))
+
+    def test_is_security_proxied_or_harmless__int(self):
+        # is_security_proxied_or_harmless() considers integers
+        # to be harmless.
+        self.assertTrue(is_security_proxied_or_harmless(1))
+
+    def test_is_security_proxied_or_harmless__string(self):
+        # is_security_proxied_or_harmless() considers strings
+        # to be harmless.
+        self.assertTrue(is_security_proxied_or_harmless('abc'))
+
+    def test_is_security_proxied_or_harmless__unicode(self):
+        # is_security_proxied_or_harmless() considers unicode objects
+        # to be harmless.
+        self.assertTrue(is_security_proxied_or_harmless(u'abc'))
+
+    def test_is_security_proxied_or_harmless__proxied_object(self):
+        # is_security_proxied_or_harmless() treats security proxied
+        # objects as harmless.
+        proxied_person = self.factory.makePerson()
+        self.assertTrue(is_security_proxied_or_harmless(proxied_person))
+
+    def test_is_security_proxied_or_harmless__unproxied_object(self):
+        # is_security_proxied_or_harmless() treats security proxied
+        # objects as harmless.
+        unproxied_person = removeSecurityProxy(self.factory.makePerson())
+        self.assertFalse(is_security_proxied_or_harmless(unproxied_person))
+
+    def test_is_security_proxied_or_harmless__sequence_harmless_content(self):
+        # is_security_proxied_or_harmless() checks all elements
+        # of a sequence. If all elements are harmless, so is the
+        # sequence.
+        proxied_person = self.factory.makePerson()
+        self.assertTrue(
+            is_security_proxied_or_harmless([1, '2', proxied_person]))
+
+    def test_is_security_proxied_or_harmless__sequence_harmful_content(self):
+        # is_security_proxied_or_harmless() checks all elements
+        # of a sequence. If at least elements are harmful, so is the
+        # sequence.
+        unproxied_person = removeSecurityProxy(self.factory.makePerson())
+        self.assertFalse(
+            is_security_proxied_or_harmless([1, '2', unproxied_person]))
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromName(__name__)

=== modified file 'lib/lp/translations/browser/tests/test_breadcrumbs.py'
--- lib/lp/translations/browser/tests/test_breadcrumbs.py	2010-07-19 15:31:57 +0000
+++ lib/lp/translations/browser/tests/test_breadcrumbs.py	2010-07-22 12:31:49 +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 (
@@ -109,7 +110,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-22 12:31:49 +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-22 12:31:49 +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)