← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/kill-old-switchdbuser into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/kill-old-switchdbuser into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/kill-old-switchdbuser/+merge/89459

LaunchpadZopelessLayer.switchDbUser has been sitting around marked DEPRECATED since September.  Kill it off, replacing it with switch_dbuser, dbuser, or lp_dbuser, whichever seemed most appropriate.  This way I stop having to think about the discrepancies while writing tests.

We can rely on switch_dbuser committing the transaction, saith wgrant, so I removed duplicate transaction.commit calls and similar immediately before switch_dbuser.

There's a fair amount of pre-existing lint, especially in doctests.  I tried to make sure I wasn't introducing anything new, but this branch is already sufficiently large (let me know if you think it needs to be broken up) that I didn't want to bloat it even more.
-- 
https://code.launchpad.net/~cjwatson/launchpad/kill-old-switchdbuser/+merge/89459
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/kill-old-switchdbuser into lp:launchpad.
=== modified file 'lib/lp/answers/tests/emailinterface.txt'
--- lib/lp/answers/tests/emailinterface.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/answers/tests/emailinterface.txt	2012-01-20 16:17:27 +0000
@@ -109,18 +109,15 @@
     >>> foo_bar = personset.getByEmail('foo.bar@xxxxxxxxxxxxx')
 
     >>> import transaction
-    >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import lp_dbuser
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
-    >>> question = ubuntu.newQuestion(
-    ...     no_priv, 'Unable to boot installer',
-    ...     "I've tried installing Ubuntu on a Mac. But the installer never "
-    ...     "boots.", datecreated=now.next())
-    >>> question_id = question.id
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
+    >>> with lp_dbuser():
+    ...     ubuntu = getUtility(IDistributionSet)['ubuntu']
+    ...     question = ubuntu.newQuestion(
+    ...         no_priv, 'Unable to boot installer',
+    ...         "I've tried installing Ubuntu on a Mac. But the installer "
+    ...         "never boots.", datecreated=now.next())
+    ...     question_id = question.id
 
     # We need to refetch the question, since a new transaction was started.
     >>> from lp.answers.interfaces.questioncollection import IQuestionSet
@@ -371,17 +368,13 @@
 
 Answers may also be linked to FAQ questions.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-    >>> from zope.security.proxy import removeSecurityProxy
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> faq = question.target.newFAQ(
-    ...     no_priv, 'Why everyone think this is weird.',
-    ...     "That's an easy one. It's because it is!")
-    >>> removeSecurityProxy(question).faq = faq
-    >>> transaction.commit()
-
-    >>> LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
+    >>> with lp_dbuser():
+    ...     login('foo.bar@xxxxxxxxxxxxx')
+    ...     faq = question.target.newFAQ(
+    ...         no_priv, 'Why everyone think this is weird.',
+    ...         "That's an easy one. It's because it is!")
+    ...     removeSecurityProxy(question).faq = faq
+
     >>> login('no-priv@xxxxxxxxxxxxx')
 
     # Make sure that the database security and permissions are set up

=== modified file 'lib/lp/archivepublisher/tests/test_ftparchive.py'
--- lib/lp/archivepublisher/tests/test_ftparchive.py	2012-01-01 02:58:52 +0000
+++ lib/lp/archivepublisher/tests/test_ftparchive.py	2012-01-20 16:17:27 +0000
@@ -30,6 +30,7 @@
     DevNullLogger,
     )
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadZopelessLayer,
     ZopelessDatabaseLayer,
@@ -71,7 +72,7 @@
 
     def setUp(self):
         super(TestFTPArchive, self).setUp()
-        self.layer.switchDbUser(config.archivepublisher.dbuser)
+        switch_dbuser(config.archivepublisher.dbuser)
 
         self._distribution = getUtility(IDistributionSet)['ubuntutest']
         self._archive = self._distribution.main_archive

=== modified file 'lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py'
--- lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py	2012-01-01 02:58:52 +0000
+++ lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py	2012-01-20 16:17:27 +0000
@@ -34,6 +34,10 @@
     ArchiveSubscriberStatus,
     )
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import (
+    lp_dbuser,
+    switch_dbuser,
+    )
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.testing.mail_helpers import pop_notifications
 
@@ -63,8 +67,7 @@
         script = HtaccessTokenGenerator("test tokens", test_args=test_args)
         script.logger = BufferLogger()
         script.txn = self.layer.txn
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         return script
 
     def runScript(self):
@@ -272,10 +275,8 @@
 
         # Now remove someone from team1, he will lose his token but
         # everyone else keeps theirs.
-        self.layer.switchDbUser("launchpad")
-        team1_person.leave(team1)
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            team1_person.leave(team1)
         # Clear out emails generated when leaving a team.
         pop_notifications()
 
@@ -293,11 +294,8 @@
         # Promiscuous_person now leaves team1, but does not lose his
         # token because he's also in team2. No other tokens are
         # affected.
-        self.layer.txn.commit()
-        self.layer.switchDbUser("launchpad")
-        promiscuous_person.leave(team1)
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            promiscuous_person.leave(team1)
         # Clear out emails generated when leaving a team.
         pop_notifications()
         script.deactivateTokens(send_email=True)
@@ -312,15 +310,13 @@
 
         # Team 2 now leaves parent_team, and all its members lose their
         # tokens.
-        self.layer.switchDbUser("launchpad")
-        name12 = getUtility(IPersonSet).getByName("name12")
-        parent_team.setMembershipData(
-            team2, TeamMembershipStatus.APPROVED, name12)
-        parent_team.setMembershipData(
-            team2, TeamMembershipStatus.DEACTIVATED, name12)
-        self.assertFalse(team2.inTeam(parent_team))
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            name12 = getUtility(IPersonSet).getByName("name12")
+            parent_team.setMembershipData(
+                team2, TeamMembershipStatus.APPROVED, name12)
+            parent_team.setMembershipData(
+                team2, TeamMembershipStatus.DEACTIVATED, name12)
+            self.assertFalse(team2.inTeam(parent_team))
         script.deactivateTokens()
         for person in persons2:
             self.assertDeactivated(tokens[person])
@@ -547,7 +543,7 @@
         # This happens even if they have no tokens.
 
         # Create a public PPA that should not be in the list.
-        public_ppa = self.factory.makeArchive(private=False)
+        self.factory.makeArchive(private=False)
 
         script = self.getScript()
         self.assertContentEqual([self.ppa], script.getNewPrivatePPAs())
@@ -570,7 +566,6 @@
 
     def test_getNewTokensSinceLastRun_no_previous_run(self):
         """All valid tokens returned if there is no record of previous run."""
-        now = datetime.now(pytz.UTC)
         tokens = self.setupDummyTokens()[1]
 
         # If there is no record of the script running previously, all
@@ -587,7 +582,7 @@
 
         getUtility(IScriptActivitySet).recordSuccess(
             'generate-ppa-htaccess', date_started=script_start_time,
-            date_completed = script_end_time)
+            date_completed=script_end_time)
         tokens = self.setupDummyTokens()[1]
         # This token will not be included.
         removeSecurityProxy(tokens[0]).date_created = before_previous_start
@@ -632,7 +627,6 @@
 
     def test_getNewTokensSinceLastRun_only_active_tokens(self):
         """Only active tokens are returned."""
-        now = datetime.now(pytz.UTC)
         tokens = self.setupDummyTokens()[1]
         tokens[0].deactivate()
 

=== modified file 'lib/lp/archivepublisher/tests/test_publisher_documentation.py'
--- lib/lp/archivepublisher/tests/test_publisher_documentation.py	2011-12-29 05:29:36 +0000
+++ lib/lp/archivepublisher/tests/test_publisher_documentation.py	2012-01-20 16:17:27 +0000
@@ -10,6 +10,7 @@
 import unittest
 
 from lp.services.config import config
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.testing.systemdocs import (
     LayeredDocFileSuite,
@@ -20,7 +21,7 @@
 
 def archivePublisherSetUp(test):
     setUp(test)
-    LaunchpadZopelessLayer.switchDbUser(config.archivepublisher.dbuser)
+    switch_dbuser(config.archivepublisher.dbuser)
 
 
 def test_suite():

=== modified file 'lib/lp/archiveuploader/tests/nascentupload-announcements.txt'
--- lib/lp/archiveuploader/tests/nascentupload-announcements.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-announcements.txt	2012-01-20 16:17:27 +0000
@@ -33,8 +33,8 @@
 
 We need to be logged into the security model in order to get any further
 
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> from lp.testing.dbuser import switch_dbuser
+    >>> switch_dbuser('launchpad')
     >>> login('foo.bar@xxxxxxxxxxxxx')
 
 Helper functions to examine emails that were sent:

=== modified file 'lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt'
--- lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt	2012-01-20 16:17:27 +0000
@@ -25,20 +25,17 @@
     >>> bar_src.queue_root.setAccepted()
     >>> pub_records = bar_src.queue_root.realiseUpload()
 
-    >>> from lp.services.database.sqlbase import commit
-    >>> commit()
-
 Check the current status of the bug we are supposed to fix:
 
     >>> the_bug_id = 6
 
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import switch_dbuser
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> from lp.bugs.interfaces.bugtask import IBugTaskSet
     >>> from lp.registry.interfaces.distribution import IDistributionSet
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> login('no-priv@xxxxxxxxxxxxx')
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> bugtask_owner = getUtility(IPersonSet).getByName('kinnison')
     >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
@@ -60,9 +57,8 @@
 
 Return to the original test environment:
 
-    >>> commit()
     >>> from lp.services.config import config
-    >>> LaunchpadZopelessLayer.switchDbUser(config.uploader.dbuser)
+    >>> switch_dbuser(config.uploader.dbuser)
     >>> login('foo.bar@xxxxxxxxxxxxx')
 
 

=== modified file 'lib/lp/archiveuploader/tests/nascentupload-ddebs.txt'
--- lib/lp/archiveuploader/tests/nascentupload-ddebs.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-ddebs.txt	2012-01-20 16:17:27 +0000
@@ -10,8 +10,9 @@
 for obtaining extra information about crashes in the corresponding
 feature.
 
+    >>> import transaction
     >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import switch_dbuser
 
     >>> from lp.soyuz.tests.test_publishing import (
     ...     SoyuzTestPublisher)
@@ -34,7 +35,7 @@
     >>> result = src.do_accept()
     >>> print src.queue_root.status.name
     NEW
-    >>> LaunchpadZopelessLayer.txn.commit()
+    >>> transaction.commit()
 
 We don't really care where the source ends up, so we just accept the
 default overrides. It is now pending publication.
@@ -86,8 +87,7 @@
     >>> universe = getUtility(IComponentSet)['universe']
     >>> devel = getUtility(ISectionSet)['devel']
 
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> bin.queue_root.overrideBinaries(main, devel, None, [main, universe])
     True
@@ -96,13 +96,11 @@
     >>> print bin.queue_root.status.name
     ACCEPTED
 
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.uploadqueue.dbuser)
+    >>> switch_dbuser(config.uploadqueue.dbuser)
 
     >>> bin_pubs = bin.queue_root.realiseUpload()
 
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> switch_dbuser('uploader')
 
 Now, both, binary and debug-symbol packages are pending publication.
 
@@ -155,8 +153,7 @@
     ...     owner=ubuntu.owner, purpose=ArchivePurpose.COPY,
     ...     name='test-rebuild')
 
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> initial_source = test_publisher.getPubSource(
     ...     sourcename='debug', version='0.9', archive=copy)
@@ -184,13 +181,11 @@
 When published, DDEB ends up in the same archive as the DEB, unlike
 the ones in the primary archive.
 
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.uploadqueue.dbuser)
+    >>> switch_dbuser(config.uploadqueue.dbuser)
 
     >>> bin_pubs = bin.queue_root.realiseUpload()
 
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> switch_dbuser('uploader')
 
     >>> for bin_pub in bin_pubs:
     ...     print '%s %s %s %s %s' % (
@@ -216,4 +211,4 @@
     >>> print bin.rejection_message
     Duplicated debug packages: debug-bin-dbgsym 1.0-1 (i386)
     Orphaned debug packages: not-debug-bin-dbgsym 1.0-1 (i386)
-    
+

=== modified file 'lib/lp/archiveuploader/tests/nascentupload-packageset.txt'
--- lib/lp/archiveuploader/tests/nascentupload-packageset.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-packageset.txt	2012-01-20 16:17:27 +0000
@@ -1,33 +1,31 @@
 In order to prepare the tests below some initialization is required.
 
+    >>> import transaction
     >>> from lp.archiveuploader.nascentupload import NascentUpload
     >>> from lp.archiveuploader.tests import datadir, getPolicy
-    >>> from lp.services.database.sqlbase import commit
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
     >>> from lp.soyuz.enums import ArchivePermissionType
     >>> from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
     >>> from lp.soyuz.interfaces.packageset import IPackagesetSet
+    >>> from lp.testing.dbuser import switch_dbuser
 
     >>> insecure_policy = getPolicy(
     ...     name='insecure', distro='ubuntu', distroseries='hoary')
     >>> ap_set = getUtility(IArchivePermissionSet)
     >>> name16 = getUtility(IPersonSet).getByName('name16')
     >>> bar_name = getUtility(ISourcePackageNameSet).getOrCreateByName("bar")
-    >>> commit()
 
 Let's modify the current ACL rules for ubuntu, moving the upload
 rights to all components from 'ubuntu-team' to 'mark':
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> from lp.soyuz.model.archivepermission import ArchivePermission
     >>> new_uploader = getUtility(IPersonSet).getByName('mark')
     >>> for permission in ArchivePermission.select():
     ...     permission.person = new_uploader
     ...     permission.syncUpdate()
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> switch_dbuser('uploader')
 
 This time the upload will fail because the ACLs don't let
 "name16", the key owner, upload a package.
@@ -48,8 +46,6 @@
 We can grant selective, package set based upload permissions to the user
 in order to facilitate uploads.
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
     >>> import operator
     >>> def print_permission(result_set):
     ...     for perm in result_set.order_by(
@@ -84,8 +80,7 @@
 Let's first add an empty package set, grant 'name16' an archive permission
 to it and see whether that changes things.
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> def sort_by_id(iterable):
     ...     return sorted(iterable, key=operator.attrgetter('id'))
@@ -98,7 +93,7 @@
     >>> ps_set = getUtility(IPackagesetSet)
     >>> empty_ps = ps_set.new(
     ...     u'empty-pkg-set', u'Empty package set.', name16)
-    >>> commit()
+    >>> transaction.commit()
 
 And here's name16's upload permission for it.
 
@@ -133,7 +128,7 @@
 
     >>> foo_ps = ps_set.new(
     ...     u'foo-pkg-set', u'Packages that require special care.', name16)
-    >>> commit()
+    >>> transaction.commit()
 
 Add 'bar' to the 'foo' package set.
 
@@ -166,8 +161,7 @@
 
 With the authorization above the upload should work again.
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> switch_dbuser('uploader')
     >>> bar2 = NascentUpload.from_changesfile_path(
     ...     datadir('suite/bar_1.0-1/bar_1.0-1_source.changes'),
     ...     insecure_policy, DevNullLogger())

=== modified file 'lib/lp/archiveuploader/tests/nascentupload.txt'
--- lib/lp/archiveuploader/tests/nascentupload.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/archiveuploader/tests/nascentupload.txt	2012-01-20 16:17:27 +0000
@@ -638,12 +638,9 @@
 to set hoary to CURRENT in order to do this because we're not allowed
 to upload to -UPDATES in a DEVELOPMENT series.
 
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> from lp.services.database.sqlbase import commit
-    >>> hoary.status = SeriesStatus.CURRENT
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> from lp.testing.dbuser import lp_dbuser
+    >>> with lp_dbuser():
+    ...     hoary.status = SeriesStatus.CURRENT
 
 Note that the policy do not have fixed distroseries, it will be
 overridden by the changesfile:
@@ -672,10 +669,8 @@
 
 And pop it back to development now that we're done
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> hoary.status = SeriesStatus.DEVELOPMENT
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> with lp_dbuser():
+    ...     hoary.status = SeriesStatus.DEVELOPMENT
 
 Check the uploader behaviour against a missing orig.tar.gz file,
       bug # 30741.
@@ -785,15 +780,12 @@
 Let's modify the current ACL rules for ubuntu, moving the upload
 rights to all components from 'ubuntu-team' to 'mark':
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
     >>> from lp.soyuz.model.archivepermission import ArchivePermission
-    >>> new_uploader = getUtility(IPersonSet).getByName('mark')
-    >>> for permission in ArchivePermission.select():
-    ...     permission.person = new_uploader
-    ...     permission.syncUpdate()
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> with lp_dbuser():
+    ...     new_uploader = getUtility(IPersonSet).getByName('mark')
+    ...     for permission in ArchivePermission.select():
+    ...         permission.person = new_uploader
+    ...         permission.syncUpdate()
 
 This time the upload will fail because the ACLs don't let
 "name16", the key owner, upload a package.
@@ -830,18 +822,16 @@
 he currently has no upload rights at all to Ubuntu.  However, we can add
 an ArchivePermission record to permit him to upload "bar" specifically.
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
     >>> from lp.registry.interfaces.sourcepackagename import (
     ...     ISourcePackageNameSet)
     >>> from lp.soyuz.enums import ArchivePermissionType
-    >>> bar_name = getUtility(ISourcePackageNameSet).getOrCreateByName("bar")
-    >>> discard = ArchivePermission(
-    ...     archive=ubuntu.main_archive, person=name16,
-    ...     permission=ArchivePermissionType.UPLOAD,
-    ...     sourcepackagename=bar_name, component=None)
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('uploader')
+    >>> with lp_dbuser():
+    ...     bar_name = getUtility(ISourcePackageNameSet).getOrCreateByName(
+    ...         "bar")
+    ...     discard = ArchivePermission(
+    ...         archive=ubuntu.main_archive, person=name16,
+    ...         permission=ArchivePermissionType.UPLOAD,
+    ...         sourcepackagename=bar_name, component=None)
 
 Now try the "bar" upload:
 

=== modified file 'lib/lp/archiveuploader/tests/test_nascentupload_documentation.py'
--- lib/lp/archiveuploader/tests/test_nascentupload_documentation.py	2012-01-01 02:58:52 +0000
+++ lib/lp/archiveuploader/tests/test_nascentupload_documentation.py	2012-01-20 16:17:27 +0000
@@ -25,6 +25,7 @@
     login,
     logout,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.gpgkeys import import_public_test_keys
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.testing.systemdocs import (
@@ -99,7 +100,7 @@
     login('foo.bar@xxxxxxxxxxxxx')
     testGlobalsSetup(test)
     prepareHoaryForUploads(test)
-    LaunchpadZopelessLayer.switchDbUser('uploader')
+    switch_dbuser('uploader')
 
 
 def tearDown(test):

=== modified file 'lib/lp/archiveuploader/tests/test_ppauploadprocessor.py'
--- lib/lp/archiveuploader/tests/test_ppauploadprocessor.py	2011-12-30 06:14:56 +0000
+++ lib/lp/archiveuploader/tests/test_ppauploadprocessor.py	2012-01-20 16:17:27 +0000
@@ -43,6 +43,7 @@
 from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
 from lp.soyuz.tests.fakepackager import FakePackager
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
+from lp.testing.dbuser import switch_dbuser
 
 
 class TestPPAUploadProcessorBase(TestUploadProcessorBase):
@@ -802,7 +803,6 @@
         self.switchToAdmin()
         self.breezy['i386'].supports_virtualized = False
         self.switchToUploader()
-        self.layer.commit()
 
         # Next version can't be accepted because it can't be built.
         packager.buildVersion('1.0-2', suite=self.breezy.name, arch="i386")
@@ -1126,7 +1126,6 @@
         bar_src.requestDeletion(self.name16)
         bar_src.dateremoved = UTC_NOW
         self.switchToUploader()
-        self.layer.txn.commit()
 
         # bar_1.0-3 contains an orig file of the same version with
         # different contents than the one we previously uploaded.
@@ -1196,7 +1195,7 @@
         the given size in bytes.
 
         Uses `SoyuzTestPublisher` class to create the corresponding publishing
-        record, then switchDbUser as 'librariangc' and update the size of the
+        record, then switch_dbuser as 'librariangc' and update the size of the
         source file to the given value.
         """
         self.switchToAdmin()
@@ -1207,18 +1206,16 @@
             status=PackagePublishingStatus.PUBLISHED)
         alias_id = pub_src.sourcepackagerelease.files[0].libraryfile.id
 
-        self.layer.commit()
-        self.layer.switchDbUser('librariangc')
+        switch_dbuser('librariangc')
         content = getUtility(ILibraryFileAliasSet)[alias_id].content
         content = removeSecurityProxy(content)
         # Decrement the archive index parcel automatically added by
         # IArchive.estimated_size.
         content.filesize = size - 1024
-        self.layer.commit()
         self.switchToUploader()
 
         # Re-initialize uploadprocessor since it depends on the new
-        # transaction reset by switchDbUser.
+        # transaction reset by switch_dbuser.
         self.uploadprocessor = self.getUploadProcessor(self.layer.txn)
 
     def testPPASizeQuotaSourceRejection(self):

=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
--- lib/lp/archiveuploader/tests/test_uploadprocessor.py	2012-01-11 08:52:22 +0000
+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py	2012-01-20 16:17:27 +0000
@@ -17,7 +17,6 @@
 
 from fixtures import MonkeyPatch
 from storm.locals import Store
-import transaction
 from zope.component import (
     getGlobalSiteManager,
     getUtility,
@@ -101,6 +100,7 @@
     TestCase,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.gpgkeys import import_public_test_keys
 from lp.testing.layers import LaunchpadZopelessLayer
@@ -132,12 +132,10 @@
     layer = LaunchpadZopelessLayer
 
     def switchToUploader(self):
-        transaction.commit()
-        self.layer.switchDbUser("uploader")
+        switch_dbuser("uploader")
 
     def switchToAdmin(self):
-        transaction.commit()
-        self.layer.switchDbUser("launchpad_main")
+        switch_dbuser("launchpad_main")
 
     def setUp(self):
         super(TestUploadProcessorBase, self).setUp()

=== modified file 'lib/lp/bugs/doc/bug-private-by-default.txt'
--- lib/lp/bugs/doc/bug-private-by-default.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/bugs/doc/bug-private-by-default.txt	2012-01-20 16:17:27 +0000
@@ -4,11 +4,12 @@
 A product with private bugs by default must always have a bug supervisor
 (this is enforced by a DB constraint).
 
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
     >>> from lp.bugs.interfaces.bug import CreateBugParams
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.interfaces.product import IProductSet
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> from lp.testing.layers import switch_dbuser
+
+    >>> switch_dbuser('launchpad')
     >>> no_priv = getUtility(IPersonSet).getByName('no-priv')
     >>> name16 = getUtility(IPersonSet).get(16)
     >>> landscape = getUtility(IProductSet).getByName('landscape')

=== modified file 'lib/lp/bugs/doc/bug-watch-activity.txt'
--- lib/lp/bugs/doc/bug-watch-activity.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/bugs/doc/bug-watch-activity.txt	2012-01-20 16:17:27 +0000
@@ -7,9 +7,8 @@
 We can create a new BugWatchActivity record for a bug watch using that
 BugWatch's addActivity() method.
 
-    >>> from lp.services.config import config
     >>> from lp.services.database.lpstorm import IStore
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import lp_dbuser
     >>> from lp.bugs.model.bugwatch import BugWatchActivity
 
 In order to create a BugWatch to test with we need to switch DB users.
@@ -17,13 +16,8 @@
 at the same time _only_ the checkwatches DB user can create
 BugWatchActivity instances.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> bug_watch = factory.makeBugWatch(remote_bug='42')
-
-    >>> # This commit is unavoidable, otherwise we lose the watch when we
-    >>> # switch users.
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     bug_watch = factory.makeBugWatch(remote_bug='42')
 
 When a BugWatch is first created there has been no activity on it.
 

=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
--- lib/lp/bugs/doc/bugnotification-sending.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/doc/bugnotification-sending.txt	2012-01-20 16:17:27 +0000
@@ -349,22 +349,7 @@
 Duplicates
 ----------
 
-We will need some helper functions.
-
-    >>> from lp.services.config import config
-    >>> from lp.services.database.sqlbase import commit
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-
-    >>> def switch_db_to_launchpad():
-    ...     commit()
-    ...     LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-    >>> def switch_db_to_bugnotification():
-    ...     commit()
-    ...     LaunchpadZopelessLayer.switchDbUser(
-    ...         config.malone.bugnotification_dbuser)
-
-We will also need a fresh new bug.
+We will need a fresh new bug.
 
     >>> from lp.bugs.interfaces.bug import CreateBugParams
     >>> from lp.registry.interfaces.distribution import IDistributionSet

=== modified file 'lib/lp/bugs/doc/bugtracker-person.txt'
--- lib/lp/bugs/doc/bugtracker-person.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/doc/bugtracker-person.txt	2012-01-20 16:17:27 +0000
@@ -19,14 +19,10 @@
 We'll rename the bugtracker to make the tests more readable.
 
     >>> from lp.services.database.sqlbase import commit
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-    >>> bugtracker.name = 'bugzilla-checkwatches'
-    >>> commit()
-
-    >>> from lp.services.config import config
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> from lp.testing.dbuser import lp_dbuser
+
+    >>> with lp_dbuser():
+    ...     bugtracker.name = 'bugzilla-checkwatches'
 
     >>> bugtracker_person = bugtracker.linkPersonToSelf(
     ...         'some-name-i-made-up', sample_person)
@@ -161,15 +157,9 @@
 
     >>> other_bug_tracker = new_bugtracker(BugTrackerType.BUGZILLA)
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-    >>> bugtracker.name = 'bugzilla-checkwatches-renamed'
-    >>> commit()
-
-    >>> other_bug_tracker.name = 'bugzilla-checkwatches'
-    >>> commit()
-
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     bugtracker.name = 'bugzilla-checkwatches-renamed'
+    ...     other_bug_tracker.name = 'bugzilla-checkwatches'
 
 A new Person has been created for 'noemail' on other_bug_tracker, even
 though that bug tracker's name is the same as one from which we've

=== modified file 'lib/lp/bugs/doc/checkwatches-cli-switches.txt'
--- lib/lp/bugs/doc/checkwatches-cli-switches.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/doc/checkwatches-cli-switches.txt	2012-01-20 16:17:27 +0000
@@ -27,16 +27,13 @@
 
 If a bug tracker is disabled checkwatches won't try to update it.
 
-    >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import lp_dbuser
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> bug_tracker = factory.makeBugTracker('http://example.com')
-    >>> bug_tracker.active = False
-    >>> bug_tracker_name = bug_tracker.name
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     login('foo.bar@xxxxxxxxxxxxx')
+    ...     bug_tracker = factory.makeBugTracker('http://example.com')
+    ...     bug_tracker.active = False
+    ...     bug_tracker_name = bug_tracker.name
 
     >>> updater.updateBugTrackers([bug_tracker_name])
     DEBUG...Updates are disabled for bug tracker at http://example.com
@@ -46,6 +43,8 @@
 line.
 
     >>> from lp.bugs.scripts.checkwatches import CheckWatchesCronScript
+    >>> from lp.services.config import config
+
     >>> class TestCheckWatchesCronScript(CheckWatchesCronScript):
     ...
     ...     def __init__(self, name, dbuser=None, test_args=None):
@@ -93,14 +92,12 @@
     >>> from lp.testing.factory import LaunchpadObjectFactory
 
     >>> factory = LaunchpadObjectFactory()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> savannah = getUtility(IBugTrackerSet).getByName('savannah')
-    >>> for i in range(5):
-    ...     bug_watch = factory.makeBugWatch(bugtracker=savannah)
-    ...     bug_watch.lastchecked = datetime.now(pytz.timezone('UTC'))
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     login('foo.bar@xxxxxxxxxxxxx')
+    ...     savannah = getUtility(IBugTrackerSet).getByName('savannah')
+    ...     for i in range(5):
+    ...         bug_watch = factory.makeBugWatch(bugtracker=savannah)
+    ...         bug_watch.lastchecked = datetime.now(pytz.timezone('UTC'))
 
     >>> run_cronscript_with_args(['-vvt', 'savannah', '--reset'])
     DEBUG Enabled by DEFAULT section

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bug-imports.txt'
--- lib/lp/bugs/doc/externalbugtracker-bug-imports.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bug-imports.txt	2012-01-20 16:17:27 +0000
@@ -47,17 +47,14 @@
 distributions are supported as the bug target.
 
     # Make sane data to play this test.
-    >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> debian = getUtility(IDistributionSet).getByName('debian')
-    >>> evolution_dsp = debian.getSourcePackage('evolution')
-    >>> ignore = factory.makeSourcePackagePublishingHistory(
-    ...     distroseries=debian.currentseries,
-    ...     sourcepackagename=evolution_dsp.sourcepackagename)
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> from lp.testing.dbuser import lp_dbuser
+
+    >>> with lp_dbuser():
+    ...     debian = getUtility(IDistributionSet).getByName('debian')
+    ...     evolution_dsp = debian.getSourcePackage('evolution')
+    ...     ignore = factory.makeSourcePackagePublishingHistory(
+    ...         distroseries=debian.currentseries,
+    ...         sourcepackagename=evolution_dsp.sourcepackagename)
 
     >>> from lp.bugs.scripts.checkwatches import CheckwatchesMaster
     >>> debian = getUtility(IDistributionSet).getByName('debian')

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2012-01-20 16:17:27 +0000
@@ -382,7 +382,7 @@
 Importing remote comments
 -------------------------
 
-BugzillaAPI implments the ISupportsCommentImport interface, which
+BugzillaAPI implements the ISupportsCommentImport interface, which
 means that we can use it to import comments from the remote Bugzilla
 instance.
 
@@ -395,9 +395,7 @@
 To test the comment importing methods we need to add an example bug,
 bugtracker and a couple of bugwatches.
 
-    >>> from lp.services.config import config
-    >>> from lp.services.database.sqlbase import commit
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import lp_dbuser
 
     >>> from lp.bugs.interfaces.bug import CreateBugParams
     >>> from lp.bugs.interfaces.bugtracker import BugTrackerType
@@ -408,22 +406,17 @@
 
     >>> bug_tracker = new_bugtracker(BugTrackerType.BUGZILLA)
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-    >>> sample_person = getUtility(IPersonSet).getByEmail(
-    ...     'test@xxxxxxxxxxxxx')
-    >>> firefox = getUtility(IProductSet).getByName('firefox')
-    >>> bug = firefox.createBug(
-    ...     CreateBugParams(sample_person, "Yet another test bug",
-    ...         "Yet another test description.",
-    ...         subscribe_owner=False))
-
-    >>> bug_watch = bug.addWatch(bug_tracker, '1', sample_person)
-    >>> bug_watch_two = bug.addWatch(bug_tracker, '2', sample_person)
-    >>> bug_watch_broken = bug.addWatch(bug_tracker, '42', sample_person)
-    >>> commit()
-
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     sample_person = getUtility(IPersonSet).getByEmail(
+    ...         'test@xxxxxxxxxxxxx')
+    ...     firefox = getUtility(IProductSet).getByName('firefox')
+    ...     bug = firefox.createBug(
+    ...         CreateBugParams(sample_person, "Yet another test bug",
+    ...             "Yet another test description.",
+    ...             subscribe_owner=False))
+    ...     bug_watch = bug.addWatch(bug_tracker, '1', sample_person)
+    ...     bug_watch_two = bug.addWatch(bug_tracker, '2', sample_person)
+    ...     bug_watch_broken = bug.addWatch(bug_tracker, '42', sample_person)
 
 
 getCommentIds()

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2012-01-20 16:17:27 +0000
@@ -55,17 +55,14 @@
 _handleLoginToken() method of TestBugzillaXMLRPCTransport so that it can
 work with the right database user.
 
-    >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
     >>> from lp.bugs.tests.externalbugtracker import (
     ...     TestInternalXMLRPCTransport)
+    >>> from lp.testing.dbuser import lp_dbuser
 
     >>> class ZopelessBugzillaXMLRPCTransport(TestBugzillaXMLRPCTransport):
     ...     def _handleLoginToken(self, token_text):
-    ...         LaunchpadZopelessLayer.switchDbUser('launchpad')
-    ...         self._consumeLoginToken(token_text)
-    ...         LaunchpadZopelessLayer.switchDbUser(
-    ...             config.checkwatches.dbuser)
+    ...         with lp_dbuser():
+    ...             self._consumeLoginToken(token_text)
 
     >>> test_transport = ZopelessBugzillaXMLRPCTransport(
     ...     'http://example.com/')
@@ -315,7 +312,7 @@
 -----------------------
 
 BugzillaLPPlugin doesn't have any special functionality for getting
-remote statuses. See the "Getting remote statuses" section of 
+remote statuses. See the "Getting remote statuses" section of
 externalbugtracker-bugzilla-api.txt for details of getting remote
 statuses from Bugzilla APIs.
 
@@ -342,7 +339,6 @@
 To test the comment importing methods we need to add an example bug,
 bugtracker and a couple of bugwatches.
 
-    >>> from lp.services.database.sqlbase import commit
     >>> from lp.bugs.interfaces.bug import CreateBugParams
     >>> from lp.bugs.interfaces.bugtracker import BugTrackerType
     >>> from lp.registry.interfaces.person import IPersonSet
@@ -352,22 +348,17 @@
 
     >>> bug_tracker = new_bugtracker(BugTrackerType.BUGZILLA)
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-    >>> sample_person = getUtility(IPersonSet).getByEmail(
-    ...     'test@xxxxxxxxxxxxx')
-    >>> firefox = getUtility(IProductSet).getByName('firefox')
-    >>> bug = firefox.createBug(
-    ...     CreateBugParams(sample_person, "Yet another test bug",
-    ...         "Yet another test description.",
-    ...         subscribe_owner=False))
-
-    >>> bug_watch = bug.addWatch(bug_tracker, '1', sample_person)
-    >>> bug_watch_two = bug.addWatch(bug_tracker, '2', sample_person)
-    >>> bug_watch_broken = bug.addWatch(bug_tracker, '42', sample_person)
-    >>> commit()
-
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     sample_person = getUtility(IPersonSet).getByEmail(
+    ...         'test@xxxxxxxxxxxxx')
+    ...     firefox = getUtility(IProductSet).getByName('firefox')
+    ...     bug = firefox.createBug(
+    ...         CreateBugParams(sample_person, "Yet another test bug",
+    ...             "Yet another test description.",
+    ...             subscribe_owner=False))
+    ...     bug_watch = bug.addWatch(bug_tracker, '1', sample_person)
+    ...     bug_watch_two = bug.addWatch(bug_tracker, '2', sample_person)
+    ...     bug_watch_broken = bug.addWatch(bug_tracker, '42', sample_person)
 
 
 getCommentIds()

=== modified file 'lib/lp/bugs/doc/externalbugtracker-comment-pushing.txt'
--- lib/lp/bugs/doc/externalbugtracker-comment-pushing.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/bugs/doc/externalbugtracker-comment-pushing.txt	2012-01-20 16:17:27 +0000
@@ -7,11 +7,10 @@
 BugWatch, Message and BugMessage instances with which to work.
 
     >>> from zope.interface import implements
-    >>> from lp.services.config import config
     >>> from lp.bugs.tests.externalbugtracker import (
     ...     new_bugtracker)
     >>> from lp.services.messages.interfaces.message import IMessageSet
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import lp_dbuser
     >>> from lp.bugs.interfaces.bug import CreateBugParams
     >>> from lp.bugs.interfaces.bugmessage import IBugMessageSet
     >>> from lp.bugs.interfaces.bugtracker import BugTrackerType
@@ -21,24 +20,19 @@
 
     >>> bug_tracker = new_bugtracker(BugTrackerType.TRAC)
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> sample_person = getUtility(IPersonSet).getByEmail(
-    ...     'test@xxxxxxxxxxxxx')
-    >>> firefox = getUtility(IProductSet).getByName('firefox')
-    >>> bug = firefox.createBug(
-    ...     CreateBugParams(sample_person, "A test bug",
-    ...         "With a test description.",
-    ...         subscribe_owner=False))
-
-    >>> message = getUtility(IMessageSet).fromText(
-    ...     "An example comment", "Pushing, for the purpose of.",
-    ...     sample_person)
-
-    >>> bug_watch = bug.addWatch(bug_tracker, '1234', sample_person)
-
-    >>> transaction.commit()
-
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     sample_person = getUtility(IPersonSet).getByEmail(
+    ...         'test@xxxxxxxxxxxxx')
+    ...     firefox = getUtility(IProductSet).getByName('firefox')
+    ...     bug = firefox.createBug(
+    ...         CreateBugParams(sample_person, "A test bug",
+    ...             "With a test description.",
+    ...             subscribe_owner=False))
+    ...     message = getUtility(IMessageSet).fromText(
+    ...         "An example comment", "Pushing, for the purpose of.",
+    ...         sample_person)
+    ...     bug_watch = bug.addWatch(bug_tracker, '1234', sample_person)
+
     >>> bug_watch = getUtility(IBugWatchSet).get(bug_watch.id)
     >>> bug_message = bug.linkMessage(message, bug_watch)
 
@@ -98,7 +92,6 @@
 each Launchpad comment that needs to be pushed to the remote bug
 tracker.
 
-    >>> from lp.services.scripts.logger import log
     >>> from lp.bugs.scripts.checkwatches.core import CheckwatchesMaster
     >>> from lp.bugs.scripts.checkwatches.tests.test_bugwatchupdater import (
     ...     make_bug_watch_updater)
@@ -141,15 +134,12 @@
 If more comments are added to the bug they will be pushed to the remote
 tracker the next time the bugwatch updater accesses it.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> message_two = getUtility(IMessageSet).fromText(
-    ...     "Comment the second", "Body the second.", sample_person)
-
-    >>> message_three = getUtility(IMessageSet).fromText(
-    ...     "Comment the third", "Body the third.", sample_person)
-    >>> transaction.commit()
-
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     message_two = getUtility(IMessageSet).fromText(
+    ...         "Comment the second", "Body the second.", sample_person)
+    ...     message_three = getUtility(IMessageSet).fromText(
+    ...         "Comment the third", "Body the third.", sample_person)
+
     >>> bug_watch = getUtility(IBugWatchSet).get(bug_watch.id)
     >>> bugmessage_two = bug.linkMessage(message_two, bug_watch)
     >>> bugmessage_three = bug.linkMessage(message_three, bug_watch)
@@ -170,11 +160,9 @@
 If a comment on the Launchpad bug isn't related to the bug watch, it
 won't be pushed.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> message_four = getUtility(IMessageSet).fromText(
-    ...     "Comment the fourth", "Body the fourth.", sample_person)
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    >>> with lp_dbuser():
+    ...     message_four = getUtility(IMessageSet).fromText(
+    ...         "Comment the fourth", "Body the fourth.", sample_person)
     >>> bugmessage_four = bug.linkMessage(message_four)
     >>> transaction.commit()
 
@@ -256,12 +244,10 @@
     ...         print "Pretending to add a comment to bug %s" % remote_bug
     ...         return None
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> message_five = getUtility(IMessageSet).fromText(
-    ...     "Comment the fifth", "Body the fifth.", sample_person)
-    >>> transaction.commit()
+    >>> with lp_dbuser():
+    ...     message_five = getUtility(IMessageSet).fromText(
+    ...         "Comment the fifth", "Body the fifth.", sample_person)
 
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
     >>> bug_watch = getUtility(IBugWatchSet).get(bug_watch.id)
     >>> bugmessage_five = bug.linkMessage(message_five, bug_watch)
     >>> transaction.commit()

=== modified file 'lib/lp/bugs/doc/externalbugtracker-debbugs.txt'
--- lib/lp/bugs/doc/externalbugtracker-debbugs.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/doc/externalbugtracker-debbugs.txt	2012-01-20 16:17:27 +0000
@@ -630,18 +630,15 @@
     >>> commit()
 
     # Make sane data to play this test.
-    >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import lp_dbuser
     >>> from lp.registry.interfaces.distribution import IDistributionSet
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> debian = getUtility(IDistributionSet).getByName('debian')
-    >>> evolution_dsp = debian.getSourcePackage('evolution')
-    >>> ignore = factory.makeSourcePackagePublishingHistory(
-    ...     distroseries=debian.currentseries,
-    ...     sourcepackagename=evolution_dsp.sourcepackagename)
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+
+    >>> with lp_dbuser():
+    ...     debian = getUtility(IDistributionSet).getByName('debian')
+    ...     evolution_dsp = debian.getSourcePackage('evolution')
+    ...     ignore = factory.makeSourcePackagePublishingHistory(
+    ...         distroseries=debian.currentseries,
+    ...         sourcepackagename=evolution_dsp.sourcepackagename)
 
     >>> import subprocess
     >>> process = subprocess.Popen(

=== modified file 'lib/lp/bugs/doc/externalbugtracker-linking-back.txt'
--- lib/lp/bugs/doc/externalbugtracker-linking-back.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/bugs/doc/externalbugtracker-linking-back.txt	2012-01-20 16:17:27 +0000
@@ -29,10 +29,10 @@
 
 The methods are called by the CheckwatchesMaster class:
 
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import switch_dbuser
     >>> txn = transaction
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> bug_watch = factory.makeBugWatch('42')
     >>> bug_watch.bug.default_bugtask.bugwatch = bug_watch
@@ -44,7 +44,7 @@
     >>> unlinked_bug = factory.makeBug()
 
     >>> txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('checkwatches')
+    >>> switch_dbuser('checkwatches')
 
     >>> from lp.bugs.scripts.checkwatches import CheckwatchesMaster
     >>> checkwatches_master = CheckwatchesMaster(txn)

=== modified file 'lib/lp/bugs/doc/product-update-remote-product.txt'
--- lib/lp/bugs/doc/product-update-remote-product.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/bugs/doc/product-update-remote-product.txt	2012-01-20 16:17:27 +0000
@@ -129,34 +129,20 @@
 product. Products having a bug tracker of a different type than the
 given one are ignored.
 
-    >>> import transaction
-    >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-
-    >>> def switch_db_to_launchpad():
-    ...     transaction.commit()
-    ...     LaunchpadZopelessLayer.switchDbUser('launchpad')
-
-    >>> def switch_db_to_updateremoteproduct():
-    ...     transaction.commit()
-    ...     LaunchpadZopelessLayer.switchDbUser(
-    ...         config.updateremoteproduct.dbuser)
+    >>> from lp.testing.dbuser import lp_dbuser
 
     >>> updater = NoNetworkRemoteProductUpdater(
     ...     FakeTransaction(), BufferLogger())
 
-    >>> switch_db_to_launchpad()
-
-    >>> bugzilla_bugtask = factory.makeBugTask(target=bugzilla_product)
-    >>> bugzilla_bugwatch = factory.makeBugWatch(
-    ...     '42', bugtracker=bugzilla, bug=bugzilla_bugtask.bug)
-    >>> bugzilla_bugtask.bugwatch = bugzilla_bugwatch
-    >>> rt_bugtask = factory.makeBugTask(target=rt_product)
-    >>> rt_bugwatch = factory.makeBugWatch(
-    ...     '84', bugtracker=rt, bug=rt_bugtask.bug)
-    >>> rt_bugtask.bugwatch = rt_bugwatch
-
-    >>> switch_db_to_updateremoteproduct()
+    >>> with lp_dbuser():
+    ...     bugzilla_bugtask = factory.makeBugTask(target=bugzilla_product)
+    ...     bugzilla_bugwatch = factory.makeBugWatch(
+    ...         '42', bugtracker=bugzilla, bug=bugzilla_bugtask.bug)
+    ...     bugzilla_bugtask.bugwatch = bugzilla_bugwatch
+    ...     rt_bugtask = factory.makeBugTask(target=rt_product)
+    ...     rt_bugwatch = factory.makeBugWatch(
+    ...         '84', bugtracker=rt, bug=rt_bugtask.bug)
+    ...     rt_bugtask.bugwatch = rt_bugwatch
 
     >>> updater.updateByBugTrackerType(BugTrackerType.RT)
     Initializing DB for bugs: [u'84'].

=== modified file 'lib/lp/bugs/mail/tests/test_handler.py'
--- lib/lp/bugs/mail/tests/test_handler.py	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/mail/tests/test_handler.py	2012-01-20 16:17:27 +0000
@@ -40,6 +40,7 @@
     TestCase,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.factory import GPGSigningContext
 from lp.testing.gpgkeys import import_secret_test_key
 from lp.testing.layers import (
@@ -164,15 +165,10 @@
         transaction.commit()
         return stub.test_emails[:]
 
-    def switchDbUser(self, user):
-        """Commit the transaction and switch to the new user."""
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(user)
-
     def getFailureForMessage(self, to_address, from_address=None, body=None):
         mail = self.factory.makeSignedMessage(
             body=body, email_address=from_address)
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         # Rejection email goes to the preferred email of the current user.
         # The current user is extracted from the current interaction, which is
         # set up using the authenticateEmail method.  However that expects

=== modified file 'lib/lp/bugs/model/tests/test_bugsummary.py'
--- lib/lp/bugs/model/tests/test_bugsummary.py	2012-01-01 02:58:52 +0000
+++ lib/lp/bugs/model/tests/test_bugsummary.py	2012-01-20 16:17:27 +0000
@@ -21,6 +21,7 @@
 from lp.registry.model.teammembership import TeamParticipation
 from lp.services.database.lpstorm import IMasterStore
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -33,7 +34,7 @@
 
         # Some things we are testing are impossible as mere mortals,
         # but might happen from the SQL command line.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
         self.store = IMasterStore(BugSummary)
 

=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
--- lib/lp/bugs/model/tests/test_bugtask.py	2012-01-06 16:52:46 +0000
+++ lib/lp/bugs/model/tests/test_bugtask.py	2012-01-20 16:17:27 +0000
@@ -82,6 +82,10 @@
     TestCaseWithFactory,
     ws_object,
     )
+from lp.testing.dbuser import (
+    dbuser,
+    switch_dbuser,
+    )
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import (
@@ -634,7 +638,7 @@
 
     def setUp(self):
         super(TestBugTaskHardwareSearch, self).setUp()
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
 
     def test_search_results_without_duplicates(self):
         # Searching for hardware related bugtasks returns each
@@ -645,11 +649,9 @@
         self.layer.txn.commit()
         device = getUtility(IHWDeviceSet).getByDeviceID(
             HWBus.PCI, '0x10de', '0x0455')
-        self.layer.switchDbUser('hwdb-submission-processor')
-        self.factory.makeHWSubmissionDevice(
-            new_submission, device, None, None, 1)
-        self.layer.txn.commit()
-        self.layer.switchDbUser('launchpad')
+        with dbuser('hwdb-submission-processor'):
+            self.factory.makeHWSubmissionDevice(
+                new_submission, device, None, None, 1)
         search_params = BugTaskSearchParams(
             user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
             hardware_product_id='0x0455', hardware_owner_is_bug_reporter=True)

=== modified file 'lib/lp/bugs/scripts/checkwatches/tests/test_core.py'
--- lib/lp/bugs/scripts/checkwatches/tests/test_core.py	2012-01-01 02:58:52 +0000
+++ lib/lp/bugs/scripts/checkwatches/tests/test_core.py	2012-01-20 16:17:27 +0000
@@ -47,6 +47,7 @@
     TestCaseWithFactory,
     ZopeTestInSubProcess,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -301,11 +302,10 @@
         # subscribers from a bug watch.
         question.subscribe(
             getUtility(ILaunchpadCelebrities).launchpad_developers)
-        transaction.commit()
 
         # We now need to switch to the checkwatches DB user so that
         # we're testing with the correct set of permissions.
-        self.layer.switchDbUser(config.checkwatches.dbuser)
+        switch_dbuser(config.checkwatches.dbuser)
 
         # For test_can_update_bug_with_questions we also need a bug
         # watch and by extension a bug tracker.

=== modified file 'lib/lp/bugs/scripts/tests/test_bugnotification.py'
--- lib/lp/bugs/scripts/tests/test_bugnotification.py	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/scripts/tests/test_bugnotification.py	2012-01-20 16:17:27 +0000
@@ -80,7 +80,10 @@
     TestCase,
     TestCaseWithFactory,
     )
-from lp.testing.dbuser import lp_dbuser
+from lp.testing.dbuser import (
+    lp_dbuser,
+    switch_dbuser,
+    )
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.testing.matchers import Contains
 
@@ -241,7 +244,7 @@
 
     def setUp(self):
         """Set up some mock bug notifications to use."""
-        self.layer.switchDbUser(config.malone.bugnotification_dbuser)
+        switch_dbuser(config.malone.bugnotification_dbuser)
         sample_person = getUtility(IPersonSet).getByEmail(
             'test@xxxxxxxxxxxxx')
         self.now = datetime.now(pytz.timezone('UTC'))
@@ -607,7 +610,7 @@
         [self.product_bugtask] = self.bug.bugtasks
         commit()
         login('test@xxxxxxxxxxxxx')
-        self.layer.switchDbUser(config.malone.bugnotification_dbuser)
+        switch_dbuser(config.malone.bugnotification_dbuser)
         self.now = datetime.now(pytz.UTC)
         self.ten_minutes_ago = self.now - timedelta(minutes=10)
         self.notification_set = getUtility(IBugNotificationSet)

=== modified file 'lib/lp/bugs/tests/bugs-emailinterface.txt'
--- lib/lp/bugs/tests/bugs-emailinterface.txt	2012-01-17 14:27:01 +0000
+++ lib/lp/bugs/tests/bugs-emailinterface.txt	2012-01-20 16:17:27 +0000
@@ -193,19 +193,16 @@
 It's possible to file a bug on more than product/package at once:
 
     # Make sane data to play this test.
-    >>> from lp.services.config import config
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
     >>> from zope.component import getUtility
     >>> from lp.registry.interfaces.distribution import IDistributionSet
-    >>> debian = getUtility(IDistributionSet).getByName('debian')
-    >>> evolution_dsp = debian.getSourcePackage('evolution')
-    >>> ignore = factory.makeSourcePackagePublishingHistory(
-    ...     distroseries=debian.currentseries,
-    ...     sourcepackagename=evolution_dsp.sourcepackagename)
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
+    >>> from lp.testing.dbuser import lp_dbuser
+
+    >>> with lp_dbuser():
+    ...     debian = getUtility(IDistributionSet).getByName('debian')
+    ...     evolution_dsp = debian.getSourcePackage('evolution')
+    ...     ignore = factory.makeSourcePackagePublishingHistory(
+    ...         distroseries=debian.currentseries,
+    ...         sourcepackagename=evolution_dsp.sourcepackagename)
 
     >>> submit_mail = """From: Sample Person <test@xxxxxxxxxxxxx>
     ... To: new@xxxxxxxxxxxxxxxxxx

=== modified file 'lib/lp/bugs/tests/externalbugtracker.py'
--- lib/lp/bugs/tests/externalbugtracker.py	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/tests/externalbugtracker.py	2012-01-20 16:17:27 +0000
@@ -60,11 +60,9 @@
 from lp.bugs.scripts import debbugs
 from lp.bugs.xmlrpc.bug import ExternalBugTrackerTokenAPI
 from lp.registry.interfaces.person import IPersonSet
-from lp.services.config import config
-from lp.services.database.sqlbase import commit
 from lp.services.verification.interfaces.logintoken import ILoginTokenSet
 from lp.testing import celebrity_logged_in
-from lp.testing.layers import LaunchpadZopelessLayer
+from lp.testing.dbuser import lp_dbuser
 from lp.testing.systemdocs import ordered_dict_as_string
 
 
@@ -76,23 +74,21 @@
     closed. After returning from this function, a new connection using
     the checkwatches db user is created.
     """
-    LaunchpadZopelessLayer.switchDbUser('launchpad')
-    owner = getUtility(IPersonSet).getByEmail('no-priv@xxxxxxxxxxxxx')
-    bugtracker_set = getUtility(IBugTrackerSet)
-    index = 1
-    name = '%s-checkwatches' % (bugtracker_type.name.lower())
-    while bugtracker_set.getByName("%s-%d" % (name, index)) is not None:
-        index += 1
-    name += '-%d' % index
-    BugTracker(
-        name=name,
-        title='%s *TESTING*' % (bugtracker_type.title),
-        bugtrackertype=bugtracker_type,
-        baseurl=base_url,
-        summary='-', contactdetails='-',
-        owner=owner)
-    commit()
-    LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    with lp_dbuser():
+        owner = getUtility(IPersonSet).getByEmail('no-priv@xxxxxxxxxxxxx')
+        bugtracker_set = getUtility(IBugTrackerSet)
+        index = 1
+        name = '%s-checkwatches' % (bugtracker_type.name.lower())
+        while bugtracker_set.getByName("%s-%d" % (name, index)) is not None:
+            index += 1
+        name += '-%d' % index
+        BugTracker(
+            name=name,
+            title='%s *TESTING*' % (bugtracker_type.title),
+            bugtrackertype=bugtracker_type,
+            baseurl=base_url,
+            summary='-', contactdetails='-',
+            owner=owner)
     return getUtility(IBugTrackerSet).getByName(name)
 
 
@@ -1195,11 +1191,8 @@
     def request(self, host, handler, request, verbose=None):
         args, method_name = xmlrpclib.loads(request)
         method = getattr(self, method_name)
-        LaunchpadZopelessLayer.switchDbUser('launchpad')
-        result = method(*args)
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
-        return result
+        with lp_dbuser():
+            return method(*args)
 
     def newBugTrackerToken(self):
         token_api = ExternalBugTrackerTokenAPI(None, None)

=== modified file 'lib/lp/bugs/tests/test_bugnotification.py'
--- lib/lp/bugs/tests/test_bugnotification.py	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/tests/test_bugnotification.py	2012-01-20 16:17:27 +0000
@@ -40,6 +40,7 @@
     person_logged_in,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
@@ -131,8 +132,7 @@
         question.linkBug(self.bug)
         # Flush pending jobs for question creation.
         pop_questionemailjobs()
-        transaction.commit()
-        self.layer.switchDbUser(config.malone.expiration_dbuser)
+        switch_dbuser(config.malone.expiration_dbuser)
 
     def test_notifications_for_question_subscribers(self):
         # Ensure that notifications are sent to subscribers of a

=== modified file 'lib/lp/bugs/tests/test_bugwatch.py'
--- lib/lp/bugs/tests/test_bugwatch.py	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/tests/test_bugwatch.py	2012-01-20 16:17:27 +0000
@@ -51,6 +51,7 @@
     login_person,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
@@ -641,9 +642,8 @@
         # where n is determined by checkwatches.scheduler.MAX_SAMPLE_SIZE.
         for i in range(5):
             self.bug_watch.addActivity(message="Activity %s" % i)
-        transaction.commit()
 
-        self.layer.switchDbUser('garbo')
+        switch_dbuser('garbo')
         self.pruner = BugWatchActivityPruner(BufferLogger())
         self.addCleanup(self.pruner.cleanUp)
 

=== modified file 'lib/lp/bugs/tests/test_doc.py'
--- lib/lp/bugs/tests/test_doc.py	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/tests/test_doc.py	2012-01-20 16:17:27 +0000
@@ -22,6 +22,7 @@
     login,
     logout,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseLayer,
     LaunchpadFunctionalLayer,
@@ -47,7 +48,7 @@
 def checkwatchesSetUp(test):
     """Setup the check watches script tests."""
     setUp(test)
-    LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser)
+    switch_dbuser(config.checkwatches.dbuser)
 
 
 def branchscannerBugsSetUp(test):
@@ -58,7 +59,7 @@
 
 def bugNotificationSendingSetUp(test):
     lobotomize_stevea()
-    LaunchpadZopelessLayer.switchDbUser(config.malone.bugnotification_dbuser)
+    switch_dbuser(config.malone.bugnotification_dbuser)
     setUp(test)
 
 
@@ -68,7 +69,7 @@
 
 def cveSetUp(test):
     lobotomize_stevea()
-    LaunchpadZopelessLayer.switchDbUser(config.cveupdater.dbuser)
+    switch_dbuser(config.cveupdater.dbuser)
     setUp(test)
 
 
@@ -81,7 +82,7 @@
     """
     lobotomize_stevea()
     test_dbuser = config.uploader.dbuser
-    LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    switch_dbuser(test_dbuser)
     setUp(test)
     test.globs['test_dbuser'] = test_dbuser
 
@@ -110,7 +111,7 @@
 def updateRemoteProductSetup(test):
     """Setup to use the 'updateremoteproduct' db user."""
     setUp(test)
-    LaunchpadZopelessLayer.switchDbUser(config.updateremoteproduct.dbuser)
+    switch_dbuser(config.updateremoteproduct.dbuser)
 
 
 def updateRemoteProductTeardown(test):

=== modified file 'lib/lp/buildmaster/tests/test_buildmaster_documentation.py'
--- lib/lp/buildmaster/tests/test_buildmaster_documentation.py	2011-12-29 05:29:36 +0000
+++ lib/lp/buildmaster/tests/test_buildmaster_documentation.py	2012-01-20 16:17:27 +0000
@@ -16,6 +16,7 @@
     login,
     logout,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -38,7 +39,7 @@
     login(ANONYMOUS)
     setGlobs(test)
     test.globs['test_dbuser'] = test_dbuser
-    LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    switch_dbuser(test_dbuser)
 
 
 def buildmasterTearDown(test):
@@ -82,7 +83,7 @@
 
     for filename in sorted(filenames):
         test = LayeredDocFileSuite(
-            "../doc/" +filename, setUp=buildmasterSetUp,
+            "../doc/" + filename, setUp=buildmasterSetUp,
             tearDown=buildmasterTearDown,
             stdout_logging_level=logging.WARNING,
             layer=LaunchpadZopelessLayer)

=== modified file 'lib/lp/buildmaster/tests/test_manager.py'
--- lib/lp/buildmaster/tests/test_manager.py	2012-01-11 08:52:22 +0000
+++ lib/lp/buildmaster/tests/test_manager.py	2012-01-20 16:17:27 +0000
@@ -51,6 +51,7 @@
     TestCase,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import (
@@ -139,8 +140,7 @@
         builder.failure_count = 1
 
         # Run 'scan' and check its result.
-        self.layer.txn.commit()
-        self.layer.switchDbUser(config.builddmaster.dbuser)
+        switch_dbuser(config.builddmaster.dbuser)
         scanner = self._getScanner()
         d = defer.maybeDeferred(scanner.scan)
         d.addCallback(self._checkDispatch, builder)
@@ -178,7 +178,7 @@
         login(ANONYMOUS)
 
         # Run 'scan' and check its result.
-        self.layer.switchDbUser(config.builddmaster.dbuser)
+        switch_dbuser(config.builddmaster.dbuser)
         scanner = self._getScanner()
         d = defer.maybeDeferred(scanner.singleCycle)
         d.addCallback(self._checkNoDispatch, builder)
@@ -219,7 +219,7 @@
         login(ANONYMOUS)
 
         # Run 'scan' and check its result.
-        self.layer.switchDbUser(config.builddmaster.dbuser)
+        switch_dbuser(config.builddmaster.dbuser)
         scanner = self._getScanner()
         d = defer.maybeDeferred(scanner.scan)
         d.addCallback(self._checkJobRescued, builder, job)
@@ -254,7 +254,7 @@
         self.assertBuildingJob(job, builder)
 
         # Run 'scan' and check its result.
-        self.layer.switchDbUser(config.builddmaster.dbuser)
+        switch_dbuser(config.builddmaster.dbuser)
         scanner = self._getScanner()
         d = defer.maybeDeferred(scanner.scan)
         d.addCallback(self._checkJobUpdated, builder, job)
@@ -445,7 +445,7 @@
         build.status = BuildStatus.CANCELLING
 
         # Run 'scan' and check its results.
-        self.layer.switchDbUser(config.builddmaster.dbuser)
+        switch_dbuser(config.builddmaster.dbuser)
         scanner = self._getScanner()
         d = scanner.scan()
 

=== modified file 'lib/lp/code/mail/tests/test_codehandler.py'
--- lib/lp/code/mail/tests/test_codehandler.py	2012-01-01 02:58:52 +0000
+++ lib/lp/code/mail/tests/test_codehandler.py	2012-01-20 16:17:27 +0000
@@ -73,6 +73,7 @@
     TestCase,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadZopelessLayer,
     ZopelessAppServerLayer,
@@ -164,11 +165,6 @@
         setSecurityPolicy(self._old_policy)
         super(TestCodeHandler, self).tearDown()
 
-    def switchDbUser(self, user):
-        """Commit the transaction and switch to the new user."""
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(user)
-
     def test_get(self):
         handler = mail_handlers.get(config.launchpad.code_domain)
         self.assertIsInstance(handler, CodeHandler)
@@ -178,7 +174,7 @@
         mail = self.factory.makeSignedMessage('<my-id>')
         bmp = self.factory.makeBranchMergeProposal()
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.assertTrue(self.code_handler.process(
             mail, email_addr, None), "Succeeded, but didn't return True")
         # if the message has not been created, this raises SQLObjectNotFound
@@ -191,7 +187,7 @@
         bmp = self.factory.makeBranchMergeProposal(
             target_branch=target_branch)
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         self.assertIn(
             '<my-id>', [comment.message.rfc822msgid
@@ -200,14 +196,14 @@
     def test_processBadAddress(self):
         """When a bad address is supplied, it returns False."""
         mail = self.factory.makeSignedMessage('<my-id>')
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.assertFalse(self.code_handler.process(mail,
             'foo@xxxxxxxxxxxxxxxxxx', None))
 
     def test_processNonExistantAddress(self):
         """When a non-existant address is supplied, it returns False."""
         mail = self.factory.makeSignedMessage('<my-id>')
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.assertTrue(self.code_handler.process(mail,
             'mp+0@xxxxxxxxxxxxxxxxxx', None))
         notification = pop_notifications()[0]
@@ -228,7 +224,7 @@
         # Remove the notifications sent about the new proposal.
         pop_notifications()
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.assertTrue(self.code_handler.process(
             mail, email_addr, None), "Didn't return True")
         notification = pop_notifications()[0]
@@ -263,7 +259,7 @@
     def test_getReplyAddress(self):
         """getReplyAddress should return From or Reply-to address."""
         mail = self.factory.makeSignedMessage()
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.assertEqual(
             mail['From'], self.code_handler._getReplyAddress(mail))
         mail['Reply-to'] = self.factory.getUniqueEmailAddress()
@@ -288,7 +284,7 @@
         bmp = self.factory.makeBranchMergeProposal(
             target_branch=code_import.branch)
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         pop_notifications()
         self.code_handler.process(mail, email_addr, None)
         notification = pop_notifications()[0]
@@ -304,7 +300,7 @@
         mail = self.factory.makeSignedMessage(body=' vote Abstain EBAILIWICK')
         bmp = self.factory.makeBranchMergeProposal()
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         self.assertEqual(CodeReviewVote.ABSTAIN, bmp.all_comments[0].vote)
         self.assertEqual('ebailiwick', bmp.all_comments[0].vote_tag)
@@ -315,7 +311,7 @@
             body=' vote: Abstain EBAILIWICK')
         bmp = self.factory.makeBranchMergeProposal()
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         self.assertEqual(CodeReviewVote.ABSTAIN, bmp.all_comments[0].vote)
         self.assertEqual('ebailiwick', bmp.all_comments[0].vote_tag)
@@ -325,7 +321,7 @@
         mail = self.factory.makeSignedMessage(body=' review Abstain ROAR!')
         bmp = self.factory.makeBranchMergeProposal()
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         self.assertEqual(CodeReviewVote.ABSTAIN, bmp.all_comments[0].vote)
         self.assertEqual('roar!', bmp.all_comments[0].vote_tag)
@@ -335,7 +331,7 @@
         mail = self.factory.makeSignedMessage(body=' review: Abstain ROAR!')
         bmp = self.factory.makeBranchMergeProposal()
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         self.assertEqual(CodeReviewVote.ABSTAIN, bmp.all_comments[0].vote)
         self.assertEqual('roar!', bmp.all_comments[0].vote_tag)
@@ -349,7 +345,7 @@
         [vote] = list(bmp.votes)
         self.assertEqual(sender, vote.reviewer)
         self.assertTrue(vote.comment is None)
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         # Login the sender as they are set as the message owner.
         login_person(sender)
         self.code_handler.process(mail, email_addr, None)
@@ -372,7 +368,7 @@
             subscriber, BranchSubscriptionNotificationLevel.NOEMAIL, None,
             CodeReviewNotificationLevel.FULL, subscriber)
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         job = Store.of(bmp).find(
             BranchMergeProposalJob,
@@ -386,13 +382,13 @@
     def test_getBranchMergeProposal(self):
         """The correct BranchMergeProposal is returned for the address."""
         bmp = self.factory.makeBranchMergeProposal()
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         bmp2 = self.code_handler.getBranchMergeProposal(bmp.address)
         self.assertEqual(bmp, bmp2)
 
     def test_getBranchMergeProposalInvalid(self):
         """InvalidBranchMergeProposalAddress is raised if appropriate."""
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.assertRaises(InvalidBranchMergeProposalAddress,
                           self.code_handler.getBranchMergeProposal, '')
         self.assertRaises(InvalidBranchMergeProposalAddress,
@@ -404,7 +400,7 @@
         source_branch = self.factory.makeAnyBranch()
         md = self.factory.makeMergeDirective(source_branch, target_branch)
         submitter = self.factory.makePerson()
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         mp_source, mp_target = self.code_handler._acquireBranchesForProposal(
             md, submitter)
         self.assertEqual(mp_source, source_branch)
@@ -417,7 +413,7 @@
         md = self.factory.makeMergeDirective(
             source_branch, target_branch_url='http://example.com')
         submitter = self.factory.makePerson()
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         self.assertRaises(
             NonLaunchpadTarget, self.code_handler._acquireBranchesForProposal,
             md, submitter)
@@ -436,7 +432,7 @@
         branches = getUtility(IBranchLookup)
         self.assertIs(None, branches.getByUrl(source_branch_url))
         submitter = self.factory.makePerson()
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         mp_source, mp_target = self.code_handler._acquireBranchesForProposal(
             md, submitter)
         self.assertEqual(mp_target, target_branch)
@@ -461,7 +457,7 @@
         submitter = self.factory.makePerson()
         self.factory.makeProductBranch(
             product=target_branch.product, name='suffix', owner=submitter)
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         mp_source, mp_target = self.code_handler._acquireBranchesForProposal(
             md, submitter)
         self.assertEqual('suffix-1', mp_source.name)
@@ -474,7 +470,7 @@
             body='Hi!\n', attachment_contents=''.join(md.to_lines()),
             force_transfer_encoding=True)
         code_handler = CodeHandler()
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         comment, md2 = code_handler.findMergeDirectiveAndComment(message)
         self.assertEqual('Hi!\n', comment)
         self.assertEqual(md.revision_id, md2.revision_id)
@@ -489,7 +485,7 @@
         md = self.factory.makeMergeDirective()
         message = self.factory.makeSignedMessage(
             body='', attachment_contents=''.join(md.to_lines()))
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         code_handler = CodeHandler()
         comment, md2 = code_handler.findMergeDirectiveAndComment(message)
         self.assertEqual('', comment)
@@ -503,7 +499,7 @@
         body = message.get_payload()[0]
         del body['Content-type']
         body.set_payload('body')
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         code_handler = CodeHandler()
         comment, md2 = code_handler.findMergeDirectiveAndComment(message)
         self.assertEqual('body', comment)
@@ -519,7 +515,7 @@
         del body['Content-type']
         body['Content-type'] = 'Text/Plain'
         body.set_payload('body')
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         code_handler = CodeHandler()
         comment, md2 = code_handler.findMergeDirectiveAndComment(message)
         self.assertEqual('body', comment)
@@ -529,7 +525,7 @@
         md = self.factory.makeMergeDirective()
         message = self.factory.makeSignedMessage(
             body=u'\u1234', attachment_contents=''.join(md.to_lines()))
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         code_handler = CodeHandler()
         comment, md2 = code_handler.findMergeDirectiveAndComment(message)
         self.assertEqual(u'\u1234', comment)
@@ -541,7 +537,7 @@
         MissingMergeDirective is raised when no merge directive is present.
         """
         message = self.factory.makeSignedMessage(body='Hi!\n')
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         code_handler = CodeHandler()
         self.assertRaises(MissingMergeDirective,
             code_handler.findMergeDirectiveAndComment, message)
@@ -553,7 +549,7 @@
             self.factory.makeMergeDirectiveEmail())
         # Add some revisions so the proposal is ready.
         self.factory.makeRevisionsForBranch(source, count=1)
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         pop_notifications()
         bmp = code_handler.processMergeProposal(message)
@@ -576,7 +572,7 @@
         """
         message, file_alias, source_branch, target_branch = (
             self.factory.makeMergeDirectiveEmail(body=' '))
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         bmp = code_handler.processMergeProposal(message)
         self.assertEqual(source_branch, bmp.source_branch)
@@ -591,8 +587,7 @@
             self.factory.makeMergeDirectiveEmail())
         # Ensure the message is stored in the librarian.
         # mail.incoming.handleMail also explicitly does this.
-        transaction.commit()
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         # In order to fake a non-gpg signed email, we say that the current
         # principal direcly provides IWeaklyAuthenticatePrincipal, which is
@@ -631,11 +626,11 @@
             self.factory.makeMergeDirectiveEmail())
         # Ensure the message is stored in the librarian.
         # mail.incoming.handleMail also explicitly does this.
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         code_handler = CodeHandler()
         self.assertEqual(0, source.landing_targets.count())
         code_handler.process(message, 'merge@xxxxxxxxxxxxxxxxxx', file_alias)
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         JobRunner.fromReady(CreateMergeProposalJob).runAll()
         self.assertEqual(target, source.landing_targets[0].target_branch)
         # Ensure the DB operations violate no constraints.
@@ -647,11 +642,11 @@
             self.factory.makeMergeDirectiveEmail(body=u'\u1234'))
         # Ensure the message is stored in the librarian.
         # mail.incoming.handleMail also explicitly does this.
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         code_handler = CodeHandler()
         self.assertEqual(0, source.landing_targets.count())
         code_handler.process(message, 'merge@xxxxxxxxxxxxxxxxxx', file_alias)
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         JobRunner.fromReady(CreateMergeProposalJob).runAll()
         proposal = source.landing_targets[0]
         self.assertEqual(u'\u1234', proposal.description)
@@ -667,7 +662,7 @@
 
                   reviewer eric
                 """)))
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         pop_notifications()
         bmp = code_handler.processMergeProposal(message)
@@ -695,7 +690,7 @@
         eric = self.factory.makePerson(name="eric", email="eric@xxxxxxxxxxx")
         mail = self.factory.makeSignedMessage(body=' reviewer eric')
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         [vote] = bmp.votes
         self.assertEqual(eric, vote.reviewer)
@@ -707,7 +702,7 @@
             self.factory.makeMergeDirectiveEmail(body=dedent("""\
                 This is the comment.
                 """)))
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         pop_notifications()
         bmp = code_handler.processMergeProposal(message)
@@ -732,7 +727,7 @@
         """
         message, file_alias, source, target = (
             self.factory.makeMergeDirectiveEmail())
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         code_handler.processMergeProposal(message)
         pop_notifications()
@@ -752,7 +747,7 @@
         """
         message = self.factory.makeSignedMessage(body='A body',
             subject='A subject', attachment_contents='')
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         code_handler.processMergeProposal(message)
         transaction.commit()
@@ -838,7 +833,7 @@
             subject='This is gonna fail', attachment_contents=''.join(
                 directive.to_lines()))
 
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         code_handler.processMergeProposal(message)
         transaction.commit()
@@ -863,7 +858,7 @@
         bmp = self.factory.makeBranchMergeProposal()
         pop_notifications()
         email_addr = bmp.address
-        self.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
         self.code_handler.process(mail, email_addr, None)
         [notification] = pop_notifications()
 
@@ -893,11 +888,6 @@
         setSecurityPolicy(self._old_policy)
         TestCaseWithFactory.tearDown(self)
 
-    def switchDbUser(self, user):
-        """Commit the transactionand switch to the new user."""
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(user)
-
     def _createTargetSourceAndBundle(self, format=None):
         """Create a merge directive with a bundle and associated branches.
 
@@ -939,7 +929,7 @@
 
     def _processMergeDirective(self, message):
         """Process the merge directive email."""
-        self.switchDbUser(config.create_merge_proposals.dbuser)
+        switch_dbuser(config.create_merge_proposals.dbuser)
         code_handler = CodeHandler()
         # Do the authentication dance as we do in the processing script.
         authutil = getUtility(IPlacelessAuthUtility)
@@ -1251,8 +1241,7 @@
         self.context = CodeReviewEmailCommandExecutionContext(
             self.merge_proposal, self.merge_proposal.target_branch.owner)
         self.jrandom = self.factory.makePerson()
-        transaction.commit()
-        self.layer.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
 
     def tearDown(self):
         setSecurityPolicy(self._old_policy)
@@ -1376,8 +1365,7 @@
         self.context = CodeReviewEmailCommandExecutionContext(
             self.merge_proposal, self.merge_proposal.target_branch.owner)
         self.reviewer = self.factory.makePerson()
-        transaction.commit()
-        self.layer.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
 
     def tearDown(self):
         setSecurityPolicy(self._old_policy)

=== modified file 'lib/lp/code/model/tests/test_branch_privacy_triggers.py'
--- lib/lp/code/model/tests/test_branch_privacy_triggers.py	2011-12-30 06:14:56 +0000
+++ lib/lp/code/model/tests/test_branch_privacy_triggers.py	2012-01-20 16:17:27 +0000
@@ -9,6 +9,7 @@
 import unittest
 
 from lp.services.database.sqlbase import cursor
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -17,7 +18,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.branch_ids = dict()
 
     def createBranches(self):

=== modified file 'lib/lp/code/model/tests/test_branchjob.py'
--- lib/lp/code/model/tests/test_branchjob.py	2011-12-30 06:14:56 +0000
+++ lib/lp/code/model/tests/test_branchjob.py	2012-01-20 16:17:27 +0000
@@ -73,6 +73,10 @@
 from lp.services.osutils import override_environ
 from lp.services.webapp import canonical_url
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import (
+    dbuser,
+    switch_dbuser,
+    )
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -146,9 +150,8 @@
             LaunchpadZopelessLayer.commit()
 
             job = BranchScanJob.create(db_branch)
-            LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
-            job.run()
-            LaunchpadZopelessLayer.switchDbUser(config.launchpad.dbuser)
+            with dbuser(config.branchscanner.dbuser):
+                job.run()
 
             self.assertEqual(db_branch.revision_count, 3)
 
@@ -156,8 +159,8 @@
             bzr_tree.commit('Fifth commit', rev_id='rev5')
 
         job = BranchScanJob.create(db_branch)
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
-        job.run()
+        with dbuser(config.branchscanner.dbuser):
+            job.run()
 
         self.assertEqual(db_branch.revision_count, 5)
 
@@ -416,10 +419,9 @@
                 tree.commit('rev1', rev_id='rev1')
                 tree.commit('rev2', rev_id='rev2')
                 tree.commit('rev3', rev_id='rev3')
-            transaction.commit()
-            self.layer.switchDbUser('branchscanner')
-            self.updateDBRevisions(
-                branch, tree.branch, ['rev1', 'rev2', 'rev3'])
+            with dbuser('branchscanner'):
+                self.updateDBRevisions(
+                    branch, tree.branch, ['rev1', 'rev2', 'rev3'])
         finally:
             tree.unlock()
         return branch, tree
@@ -652,9 +654,8 @@
     def test_getRevisionMessage_with_related_BMP(self):
         """Information about related proposals is displayed."""
         job, bmp = self.makeJobAndBMP()
-        transaction.commit()
-        self.layer.switchDbUser(config.sendbranchmail.dbuser)
-        message = job.getRevisionMessage('rev2d-id', 1)
+        with dbuser(config.sendbranchmail.dbuser):
+            message = job.getRevisionMessage('rev2d-id', 1)
         self.assertEqual(
         'Merge authors:\n'
         '  bar@\n'
@@ -676,9 +677,8 @@
         """Superseded proposals are skipped."""
         job, bmp = self.makeJobAndBMP()
         bmp2 = bmp.resubmit(bmp.registrant)
-        transaction.commit()
-        self.layer.switchDbUser(config.sendbranchmail.dbuser)
-        message = job.getRevisionMessage('rev2d-id', 1)
+        with dbuser(config.sendbranchmail.dbuser):
+            message = job.getRevisionMessage('rev2d-id', 1)
         self.assertEqual(
         'Merge authors:\n'
         '  bar@\n'
@@ -705,9 +705,8 @@
         job, bmp = self.makeJobAndBMP()
         reviewer = self.factory.makePerson()
         bmp.nominateReviewer(reviewer, bmp.registrant)
-        transaction.commit()
-        self.layer.switchDbUser(config.sendbranchmail.dbuser)
-        message = job.getRevisionMessage('rev2d-id', 1)
+        with dbuser(config.sendbranchmail.dbuser):
+            message = job.getRevisionMessage('rev2d-id', 1)
         self.assertEqual(
         'Merge authors:\n'
         '  bar@\n'
@@ -776,8 +775,7 @@
                 rev_id=second_revision, message="Extended contents",
                 committer="Joe Bloggs <joe@xxxxxxxxxxx>",
                 timestamp=1000100000.0, timezone=0)
-        transaction.commit()
-        self.layer.switchDbUser('branchscanner')
+        switch_dbuser('branchscanner')
         self.updateDBRevisions(db_branch, tree.branch)
         expected = (
             u"-" * 60 + '\n'
@@ -820,8 +818,7 @@
                 rev_id=rev_id, message=u"Non ASCII: \xe9",
                 committer=u"Non ASCII: \xed", timestamp=1000000000.0,
                 timezone=0)
-        transaction.commit()
-        self.layer.switchDbUser('branchscanner')
+        switch_dbuser('branchscanner')
         self.updateDBRevisions(db_branch, tree.branch)
         job = RevisionsAddedJob.create(db_branch, '', '', '')
         message = job.getRevisionMessage(rev_id, 1)
@@ -850,7 +847,7 @@
 
     def test_only_nodiff_subscribers_means_no_diff_generated(self):
         """No diff is generated when no subscribers need it."""
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
         self.useBzrBranches(direct_database=True)
         branch, tree = self.create_branch_and_tree()
         subscriptions = branch.getSubscriptionsByLevel(
@@ -1280,10 +1277,7 @@
     def runReadyJobs(self):
         """Run all ready `ReclaimBranchSpaceJob`s with the appropriate dbuser.
         """
-        # switchDbUser aborts the current transaction, so we need to commit to
-        # make sure newly added jobs are still there after we call it.
-        self.layer.txn.commit()
-        self.layer.switchDbUser(config.reclaimbranchspace.dbuser)
+        switch_dbuser(config.reclaimbranchspace.dbuser)
         job_count = 0
         for job in ReclaimBranchSpaceJob.iterReady():
             job.run()

=== modified file 'lib/lp/code/model/tests/test_branchmergeproposaljobs.py'
--- lib/lp/code/model/tests/test_branchmergeproposaljobs.py	2012-01-01 02:58:52 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposaljobs.py	2012-01-20 16:17:27 +0000
@@ -64,6 +64,7 @@
     EventRecorder,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.testing.mail_helpers import pop_notifications
 
@@ -173,9 +174,8 @@
         self.createBzrBranch(bmp.source_branch, tree.branch)
         self.factory.makeRevisionsForBranch(bmp.source_branch, count=1)
         job = MergeProposalNeedsReviewEmailJob.create(bmp)
-        transaction.commit()
-        self.layer.switchDbUser(config.merge_proposal_jobs.dbuser)
-        job.run()
+        with dbuser(config.merge_proposal_jobs.dbuser):
+            job.run()
 
 
 class TestUpdatePreviewDiffJob(DiffTestCase):
@@ -206,10 +206,8 @@
         job = UpdatePreviewDiffJob.create(bmp)
         self.factory.makeRevisionsForBranch(bmp.source_branch, count=1)
         bmp.source_branch.next_mirror_time = None
-        transaction.commit()
-        self.layer.switchDbUser(config.merge_proposal_jobs.dbuser)
-        JobRunner([job]).runAll()
-        transaction.commit()
+        with dbuser(config.merge_proposal_jobs.dbuser):
+            JobRunner([job]).runAll()
         self.checkExampleMerge(bmp.preview_diff.text)
 
     def test_run_object_events(self):
@@ -220,10 +218,9 @@
         job = UpdatePreviewDiffJob.create(bmp)
         self.factory.makeRevisionsForBranch(bmp.source_branch, count=1)
         bmp.source_branch.next_mirror_time = None
-        transaction.commit()
-        self.layer.switchDbUser(config.merge_proposal_jobs.dbuser)
-        with EventRecorder() as event_recorder:
-            JobRunner([job]).runAll()
+        with dbuser(config.merge_proposal_jobs.dbuser):
+            with EventRecorder() as event_recorder:
+                JobRunner([job]).runAll()
         bmp_object_events = [
             event for event in event_recorder.events
             if (IObjectModifiedEvent.providedBy(event) and
@@ -322,18 +319,15 @@
     def test_run(self):
         """The job runs successfully, and its results can be committed."""
         job = make_runnable_incremental_diff_job(self)
-        transaction.commit()
-        self.layer.switchDbUser(config.merge_proposal_jobs.dbuser)
-        job.run()
-        transaction.commit()
+        with dbuser(config.merge_proposal_jobs.dbuser):
+            job.run()
 
     def test_run_all(self):
         """The job can be run under the JobRunner successfully."""
         job = make_runnable_incremental_diff_job(self)
-        transaction.commit()
-        self.layer.switchDbUser(config.merge_proposal_jobs.dbuser)
-        runner = JobRunner([job])
-        runner.runAll()
+        with dbuser(config.merge_proposal_jobs.dbuser):
+            runner = JobRunner([job])
+            runner.runAll()
         self.assertEqual([job], runner.completed_jobs)
 
     def test_10_minute_lease(self):
@@ -341,9 +335,8 @@
         self.useBzrBranches(direct_database=True)
         bmp = create_example_merge(self)[0]
         job = GenerateIncrementalDiffJob.create(bmp, 'old', 'new')
-        transaction.commit()
-        self.layer.switchDbUser(config.merge_proposal_jobs.dbuser)
-        job.acquireLease()
+        with dbuser(config.merge_proposal_jobs.dbuser):
+            job.acquireLease()
         expiry_delta = job.lease_expires - datetime.now(pytz.UTC)
         self.assertTrue(500 <= expiry_delta.seconds, expiry_delta)
 

=== modified file 'lib/lp/code/model/tests/test_revisionauthor.py'
--- lib/lp/code/model/tests/test_revisionauthor.py	2012-01-01 02:58:52 +0000
+++ lib/lp/code/model/tests/test_revisionauthor.py	2012-01-20 16:17:27 +0000
@@ -18,6 +18,10 @@
 from lp.services.identity.interfaces.emailaddress import EmailAddressStatus
 from lp.services.log.logger import DevNullLogger
 from lp.testing import TestCase
+from lp.testing.dbuser import (
+    dbuser,
+    switch_dbuser,
+    )
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.layers import LaunchpadZopelessLayer
 
@@ -35,7 +39,7 @@
 
     def setUp(self):
         super(TestRevisionEmailExtraction, self).setUp()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
 
     def test_email_extracted_from_name(self):
         # Check that a valid email address is extracted from the name.
@@ -87,8 +91,7 @@
     """
 
     def _createRevisionAuthor(self):
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         return RevisionSet()._createRevisionAuthor(
             '"Harry Potter" <harry@xxxxxxxxxxxxx>')
 
@@ -135,12 +138,9 @@
     def setUp(self):
         # Create a revision author that doesn't have a user yet.
         super(TestNewlyValidatedEmailsLinkRevisionAuthors, self).setUp()
-        launchpad_dbuser = config.launchpad.dbuser
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
-        self.author = RevisionSet()._createRevisionAuthor(
-            '"Harry Potter" <harry@xxxxxxxxxxxxx>')
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(launchpad_dbuser)
+        with dbuser(config.branchscanner.dbuser):
+            self.author = RevisionSet()._createRevisionAuthor(
+                '"Harry Potter" <harry@xxxxxxxxxxxxx>')
         # Reget the revision author as we have crossed a transaction boundary.
         self.author = RevisionAuthor.byName(self.author.name)
 
@@ -175,7 +175,7 @@
 
     def setUp(self):
         super(TestRevisionAuthor, self).setUp()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
 
     def testGetNameWithoutEmailReturnsNamePart(self):
         # name_without_email is equal to the 'name' part of the revision

=== modified file 'lib/lp/code/scripts/tests/test_revisionkarma.py'
--- lib/lp/code/scripts/tests/test_revisionkarma.py	2012-01-05 00:23:45 +0000
+++ lib/lp/code/scripts/tests/test_revisionkarma.py	2012-01-20 16:17:27 +0000
@@ -16,6 +16,7 @@
 from lp.services.identity.model.emailaddress import EmailAddressSet
 from lp.services.log.logger import DevNullLogger
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -51,7 +52,7 @@
         branch.setTarget(user=branch.owner, project=project)
         # Commit and switch to the script db user.
         transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.revisionkarma.dbuser)
+        switch_dbuser(config.revisionkarma.dbuser)
         script = RevisionKarmaAllocator(
             'test', config.revisionkarma.dbuser, ['-q'])
         script.main()
@@ -76,7 +77,7 @@
         transaction.commit()
         # Run the RevisionAuthorEmailLinker garbo job.
         RevisionAuthorEmailLinker(log=DevNullLogger()).run()
-        LaunchpadZopelessLayer.switchDbUser(config.revisionkarma.dbuser)
+        switch_dbuser(config.revisionkarma.dbuser)
         script = RevisionKarmaAllocator(
             'test', config.revisionkarma.dbuser, ['-q'])
         script.main()
@@ -115,8 +116,7 @@
             [rev], list(RevisionSet.getRevisionsNeedingKarmaAllocated()))
 
         # Commit and switch to the script db user.
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.revisionkarma.dbuser)
+        switch_dbuser(config.revisionkarma.dbuser)
         script = RevisionKarmaAllocator(
             'test', config.revisionkarma.dbuser, ['-q'])
         script.main()

=== modified file 'lib/lp/code/tests/test_doc.py'
--- lib/lp/code/tests/test_doc.py	2012-01-01 02:58:52 +0000
+++ lib/lp/code/tests/test_doc.py	2012-01-20 16:17:27 +0000
@@ -12,6 +12,7 @@
 from lp.services.config import config
 from lp.services.testing import build_test_suite
 from lp.services.webapp.authorization import LaunchpadSecurityPolicy
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -29,16 +30,16 @@
 
 def branchscannerSetUp(test):
     """Setup the user for the branch scanner tests."""
-    LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+    switch_dbuser(config.branchscanner.dbuser)
     setUp(test)
 
 
 def zopelessLaunchpadSecuritySetUp(test):
     """Set up a LaunchpadZopelessLayer test to use LaunchpadSecurityPolicy.
 
-    To be able to use LaunchpadZopelessLayer.switchDbUser in a test, we need
-    to run in the Zopeless environment. The Zopeless environment normally runs
-    using the LaunchpadPermissiveSecurityPolicy. If we want the test to cover
+    To be able to use switch_dbuser in a test, we need to run in the
+    Zopeless environment. The Zopeless environment normally runs using the
+    LaunchpadPermissiveSecurityPolicy. If we want the test to cover
     functionality used in the webapp, it needs to use the
     LaunchpadSecurityPolicy.
     """

=== modified file 'lib/lp/codehosting/scanner/tests/test_buglinks.py'
--- lib/lp/codehosting/scanner/tests/test_buglinks.py	2012-01-01 02:58:52 +0000
+++ lib/lp/codehosting/scanner/tests/test_buglinks.py	2012-01-20 16:17:27 +0000
@@ -24,6 +24,10 @@
     TestCase,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import (
+    lp_dbuser,
+    switch_dbuser,
+    )
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -163,14 +167,10 @@
         self.assertBugBranchLinked(self.bug1, self.db_branch)
 
     def makePackageBranch(self):
-        LaunchpadZopelessLayer.switchDbUser(self.lp_db_user)
-        try:
+        with lp_dbuser():
             branch = self.factory.makePackageBranch()
             branch.sourcepackage.setBranch(
                 PackagePublishingPocket.RELEASE, branch, branch.owner)
-            LaunchpadZopelessLayer.txn.commit()
-        finally:
-            LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
         return branch
 
     def test_linking_bug_to_official_package_branch(self):
@@ -264,8 +264,7 @@
         self.useBzrBranches(direct_database=True)
         db_branch, tree = self.create_branch_and_tree()
         bug = self.factory.makeBug()
-        self.layer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         # XXX: AaronBentley 2010-08-06 bug=614404: a bzr username is
         # required to generate the revision-id.
         with override_environ(BZR_EMAIL='me@xxxxxxxxxxx'):

=== modified file 'lib/lp/codehosting/scanner/tests/test_bzrsync.py'
--- lib/lp/codehosting/scanner/tests/test_bzrsync.py	2012-01-01 02:58:52 +0000
+++ lib/lp/codehosting/scanner/tests/test_bzrsync.py	2012-01-20 16:17:27 +0000
@@ -18,7 +18,6 @@
 from bzrlib.uncommit import uncommit
 import pytz
 from storm.locals import Store
-import transaction
 from twisted.python.util import mergeFunctionMetadata
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
@@ -50,7 +49,11 @@
     temp_dir,
     TestCaseWithFactory,
     )
-from lp.testing.dbuser import dbuser
+from lp.testing.dbuser import (
+    dbuser,
+    lp_dbuser,
+    switch_dbuser,
+    )
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.interfaces.translations import (
     TranslationsBranchImportMode,
@@ -83,9 +86,8 @@
         SafeBranchOpener.install_hook()
         self.disable_directory_isolation()
         self.useBzrBranches(direct_database=True)
-        self.lp_db_user = config.launchpad.dbuser
         self.makeFixtures()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         # Catch both constraints and permissions for the db user.
         self.addCleanup(Store.of(self.db_branch).flush)
 
@@ -224,33 +226,30 @@
         :return: (db_trunk, trunk_tree), (db_branch, branch_tree).
         """
 
-        LaunchpadZopelessLayer.switchDbUser(self.lp_db_user)
-
-        # Make the base revision.
-        db_branch = self.makeDatabaseBranch()
-        db_branch, trunk_tree = self.create_branch_and_tree(
-            db_branch=db_branch)
-        # XXX: AaronBentley 2010-08-06 bug=614404: a bzr username is
-        # required to generate the revision-id.
-        with override_environ(BZR_EMAIL='me@xxxxxxxxxxx'):
-            trunk_tree.commit(u'base revision', rev_id=base_rev_id)
-
-            # Branch from the base revision.
-            new_db_branch = self.makeDatabaseBranch(product=db_branch.product)
-            new_db_branch, branch_tree = self.create_branch_and_tree(
-                db_branch=new_db_branch)
-            branch_tree.pull(trunk_tree.branch)
-
-            # Commit to both branches.
-            trunk_tree.commit(u'trunk revision', rev_id=trunk_rev_id)
-            branch_tree.commit(u'branch revision', rev_id=branch_rev_id)
-
-            # Merge branch into trunk.
-            trunk_tree.merge_from_branch(branch_tree.branch)
-            trunk_tree.commit(u'merge revision', rev_id=merge_rev_id)
-
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        with lp_dbuser():
+            # Make the base revision.
+            db_branch = self.makeDatabaseBranch()
+            db_branch, trunk_tree = self.create_branch_and_tree(
+                db_branch=db_branch)
+            # XXX: AaronBentley 2010-08-06 bug=614404: a bzr username is
+            # required to generate the revision-id.
+            with override_environ(BZR_EMAIL='me@xxxxxxxxxxx'):
+                trunk_tree.commit(u'base revision', rev_id=base_rev_id)
+
+                # Branch from the base revision.
+                new_db_branch = self.makeDatabaseBranch(
+                    product=db_branch.product)
+                new_db_branch, branch_tree = self.create_branch_and_tree(
+                    db_branch=new_db_branch)
+                branch_tree.pull(trunk_tree.branch)
+
+                # Commit to both branches.
+                trunk_tree.commit(u'trunk revision', rev_id=trunk_rev_id)
+                branch_tree.commit(u'branch revision', rev_id=branch_rev_id)
+
+                # Merge branch into trunk.
+                trunk_tree.merge_from_branch(branch_tree.branch)
+                trunk_tree.commit(u'merge revision', rev_id=merge_rev_id)
 
         return (db_branch, trunk_tree), (new_db_branch, branch_tree)
 
@@ -623,15 +622,11 @@
         """Switch to the Launchpad db user to create and configure a
         product series that is linked to the the branch.
         """
-        try:
-            LaunchpadZopelessLayer.switchDbUser(self.lp_db_user)
+        with lp_dbuser():
             self.product_series = self.factory.makeProductSeries()
             self.product_series.branch = self.db_branch
             if mode is not None:
                 self.product_series.translations_autoimport_mode = mode
-            transaction.commit()
-        finally:
-            LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
 
     def test_upload_on_new_revision_no_series(self):
         # Syncing a branch with a changed tip does not create a
@@ -680,8 +675,7 @@
         bmp.next_preview_diff_job.start()
         bmp.next_preview_diff_job.complete()
         self.assertIs(None, bmp.next_preview_diff_job)
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         self.makeBzrSync(self.db_branch).syncBranchAndClose()
         self.assertIsNot(None, bmp.next_preview_diff_job)
 
@@ -712,8 +706,7 @@
         revision_id = commit_file(self.db_branch, 'foo', 'baz')
         removeSecurityProxy(bmp).target_branch.last_scanned_id = 'rev'
         self.assertEqual([], self.getPending())
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         self.makeBzrSync(self.db_branch).syncBranchAndClose()
         (job,) = self.getPending()
         self.assertEqual(revision_id, job.new_revision_id)
@@ -729,8 +722,7 @@
         recipe = self.factory.makeSourcePackageRecipe(
             branches=[self.db_branch])
         removeSecurityProxy(recipe).is_stale = False
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         self.makeBzrSync(self.db_branch).syncBranchAndClose()
         self.assertEqual(True, recipe.is_stale)
 
@@ -740,8 +732,7 @@
         recipe = self.factory.makeSourcePackageRecipe(
             branches=[self.factory.makeBranch(), self.db_branch])
         removeSecurityProxy(recipe).is_stale = False
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         self.makeBzrSync(self.db_branch).syncBranchAndClose()
         self.assertEqual(True, recipe.is_stale)
 
@@ -750,8 +741,7 @@
         """On tip unrelated recipes are left alone."""
         recipe = self.factory.makeSourcePackageRecipe()
         removeSecurityProxy(recipe).is_stale = False
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         self.makeBzrSync(self.db_branch).syncBranchAndClose()
         self.assertEqual(False, recipe.is_stale)
 

=== modified file 'lib/lp/codehosting/tests/test_branchdistro.py'
--- lib/lp/codehosting/tests/test_branchdistro.py	2012-01-01 02:58:52 +0000
+++ lib/lp/codehosting/tests/test_branchdistro.py	2012-01-20 16:17:27 +0000
@@ -42,6 +42,7 @@
     )
 from lp.services.osutils import override_environ
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 # We say "RELEASE" often enough to not want to say "PackagePublishingPocket."
@@ -154,8 +155,7 @@
         self._log_file = StringIO()
         new_distroseries = self.factory.makeDistroSeries(
             distribution=distroseries.distribution)
-        transaction.commit()
-        self.layer.switchDbUser('branch-distro')
+        switch_dbuser('branch-distro')
         return DistroBrancher(
             FakeLogger(self._log_file), distroseries, new_distroseries)
 
@@ -277,9 +277,8 @@
         self.assertIs(None, new_branch.stacked_on)
         self.assertEqual(new_branch, db_branch.stacked_on)
         # The script doesn't have permission to create branch jobs, but just
-        # to be insanely paradoid.
-        transaction.commit()
-        self.layer.switchDbUser('launchpad')
+        # to be insanely paranoid.
+        switch_dbuser('launchpad')
         scan_jobs = list(getUtility(IBranchScanJobSource).iterReady())
         self.assertEqual(existing_scan_job_count, len(scan_jobs))
 
@@ -450,12 +449,11 @@
         db_branch = self.makeOfficialPackageBranch()
         brancher = self.makeNewSeriesAndBrancher(db_branch.distroseries)
         new_db_branch = brancher.makeOneNewBranch(db_branch)
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
         new_db_branch.setTarget(
             new_db_branch.owner,
             source_package=self.factory.makeSourcePackage())
-        transaction.commit()
-        self.layer.switchDbUser('branch-distro')
+        switch_dbuser('branch-distro')
         ok = brancher.checkOneBranch(new_db_branch)
         self.assertFalse(ok)
         self.assertLogMessages(

=== modified file 'lib/lp/coop/answersbugs/tests/test_doc.py'
--- lib/lp/coop/answersbugs/tests/test_doc.py	2012-01-01 02:58:52 +0000
+++ lib/lp/coop/answersbugs/tests/test_doc.py	2012-01-20 16:17:27 +0000
@@ -23,6 +23,7 @@
     ANONYMOUS,
     login,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -82,7 +83,7 @@
 
 
 def uploaderBugLinkedToQuestionSetUp(test):
-    LaunchpadZopelessLayer.switchDbUser('launchpad')
+    switch_dbuser('launchpad')
     bugLinkedToQuestionSetUp(test)
     LaunchpadZopelessLayer.commit()
     uploaderSetUp(test)
@@ -90,7 +91,7 @@
 
 
 def uploadQueueBugLinkedToQuestionSetUp(test):
-    LaunchpadZopelessLayer.switchDbUser('launchpad')
+    switch_dbuser('launchpad')
     bugLinkedToQuestionSetUp(test)
     LaunchpadZopelessLayer.commit()
     uploadQueueSetUp(test)

=== modified file 'lib/lp/hardwaredb/doc/hwdb-device-tables.txt'
--- lib/lp/hardwaredb/doc/hwdb-device-tables.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/hardwaredb/doc/hwdb-device-tables.txt	2012-01-20 16:17:27 +0000
@@ -1036,7 +1036,12 @@
 Launchpad user, because the former has no access to the table
 distribution, which we want to access in this test.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> from lp.testing.dbuser import (
+    ...     lp_dbuser,
+    ...     switch_dbuser,
+    ...     )
+
+    >>> switch_dbuser('launchpad')
     >>> sata_controller = device_set.getByDeviceID(
     ...     bus=HWBus.PCI, vendor_id='0x10de', product_id='0x045d')
     >>> for submission in sata_controller.getSubmissions():
@@ -1120,7 +1125,7 @@
 
     >>> import transaction
     >>> transaction.abort()
-    >>> LaunchpadZopelessLayer.switchDbUser('hwdb-submission-processor')
+    >>> switch_dbuser('hwdb-submission-processor')
 
 HWDevice.drivers is the set of drivers that are associated via
 HWDeviceDriverLink (see below) with this device.
@@ -1423,7 +1428,7 @@
     None
 
     >>> transaction.abort()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
 We can search all the submissions related to a driver.
 
@@ -1472,7 +1477,7 @@
     0
 
     >>> transaction.abort()
-    >>> LaunchpadZopelessLayer.switchDbUser('hwdb-submission-processor')
+    >>> switch_dbuser('hwdb-submission-processor')
 
 
 Driver names and package names
@@ -1482,7 +1487,7 @@
 HWDriverSet.all_driver_names() returns a list of distinct driver
 names used in the table HWDriver.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> for driver_name in driver_set.all_driver_names():
     ...     print driver_name.name
     ahci
@@ -1510,7 +1515,7 @@
     linux-image-2.6.24-19-generic
     linux-image-generic
 
-    >>> LaunchpadZopelessLayer.switchDbUser('hwdb-submission-processor')
+    >>> switch_dbuser('hwdb-submission-processor')
 
 
 HWDeviceDriverLink
@@ -1715,13 +1720,13 @@
 IHWDevice.removeDeviceClass().
 
     >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> optic_pro_ut12.removeDeviceClass(main_class=0x07, sub_class=0x01)
     >>> for device_class in optic_pro_ut12.classes:
     ...     print device_class.main_class, device_class.sub_class
     16 0
 
-    >>> LaunchpadZopelessLayer.switchDbUser('hwdb-submission-processor')
+    >>> switch_dbuser('hwdb-submission-processor')
 
 
 HWSubmissionDevice
@@ -1910,14 +1915,11 @@
 HWSubmissionDevice entries for the disk and the new submission, both
 for the "plain" disk and for the disk driven by the sd driver.
 
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> # Set the emailaddress and hence the owner to a non-default
-    >>> # value so that we have submissions with different owners.
-    >>> submission = factory.makeHWSubmission(
-    ...     emailaddress='foo.bar@xxxxxxxxxxxxx')
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('hwdb-submission-processor')
+    >>> with lp_dbuser():
+    ...     # Set the emailaddress and hence the owner to a non-default
+    ...     # value so that we have submissions with different owners.
+    ...     submission = factory.makeHWSubmission(
+    ...         emailaddress='foo.bar@xxxxxxxxxxxxx')
     >>> first_device = factory.makeHWSubmissionDevice(
     ...     submission, ide_disk, None, None, 1)
     >>> driver = getUtility(IHWDriverSet).getOrCreate(
@@ -1990,7 +1992,7 @@
 not counted here, because we created the new submission for a new
 distribution.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> print submission_device_set.numDevicesInSubmissions(
     ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
     ...     distro_target=ubuntu)
@@ -2434,12 +2436,10 @@
 
     >>> private_submission = factory.makeHWSubmission(
     ...     emailaddress='no-priv@xxxxxxxxxxxxx', private=True)
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('hwdb-submission-processor')
+    >>> switch_dbuser('hwdb-submission-processor')
     >>> first_device = factory.makeHWSubmissionDevice(
     ...     private_submission, ide_disk, None, None, 1)
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> no_priv = getUtility(IPersonSet).getByEmail('no-priv@xxxxxxxxxxxxx')
     >>> bug_one.subscribe(no_priv, subscribed_by=no_priv)
     <lp.bugs.model.bugsubscription.BugSubscription ...>

=== modified file 'lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py'
--- lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py	2011-12-30 01:48:17 +0000
+++ lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py	2012-01-20 16:17:27 +0000
@@ -47,6 +47,7 @@
     TestCase,
     validate_mock_class,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     BaseLayer,
     LaunchpadZopelessLayer,
@@ -4620,7 +4621,7 @@
         self.log.setLevel(logging.INFO)
         self.handler = Handler(self)
         self.handler.add(self.log.name)
-        self.layer.switchDbUser('hwdb-submission-processor')
+        switch_dbuser('hwdb-submission-processor')
 
     def getLogData(self):
         messages = [record.getMessage() for record in self.handler.records]
@@ -5079,7 +5080,7 @@
         """Create a submission."""
         if compress:
             data = bz2.compress(data)
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
         submission = getUtility(IHWSubmissionSet).createSubmission(
             date_created=datetime(2007, 9, 9, tzinfo=pytz.timezone('UTC')),
             format=HWSubmissionFormat.VERSION_1,
@@ -5092,10 +5093,7 @@
             filename='hwinfo.xml',
             filesize=len(data),
             system_fingerprint='A Machine Name')
-        # We want to access library file later: ensure that it is
-        # properly stored.
-        self.layer.txn.commit()
-        self.layer.switchDbUser('hwdb-submission-processor')
+        switch_dbuser('hwdb-submission-processor')
         return submission
 
     def getSampleData(self, filename):

=== modified file 'lib/lp/hardwaredb/tests/test_doc.py'
--- lib/lp/hardwaredb/tests/test_doc.py	2012-01-01 02:58:52 +0000
+++ lib/lp/hardwaredb/tests/test_doc.py	2012-01-20 16:17:27 +0000
@@ -8,6 +8,7 @@
 import os
 
 from lp.services.testing import build_test_suite
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -24,7 +25,7 @@
 
 def hwdbDeviceTablesSetup(test):
     setUp(test)
-    LaunchpadZopelessLayer.switchDbUser('hwdb-submission-processor')
+    switch_dbuser('hwdb-submission-processor')
 
 
 special = {

=== modified file 'lib/lp/registry/browser/tests/test_person_view.py'
--- lib/lp/registry/browser/tests/test_person_view.py	2012-01-05 00:23:45 +0000
+++ lib/lp/registry/browser/tests/test_person_view.py	2012-01-20 16:17:27 +0000
@@ -62,6 +62,7 @@
     StormStatementRecorder,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
@@ -205,10 +206,10 @@
 
         In order to create the KarmaCache record we must switch to the DB
         user 'karma', so tests that need a different user after calling
-        this method should do run switchDbUser() themselves.
+        this method should run switch_dbuser() themselves.
         """
 
-        LaunchpadZopelessLayer.switchDbUser('karma')
+        switch_dbuser('karma')
 
         cache_manager = getUtility(IKarmaCacheManager)
         karmacache = cache_manager.new(
@@ -222,7 +223,7 @@
                 value, person.id, category_id=None, product_id=product.id)
 
         # We must commit here so that the change is seen in other transactions
-        # (e.g. when the callsite issues a switchDbUser() after we return).
+        # (e.g. when the callsite issues a switch_dbuser() after we return).
         transaction.commit()
         return karmacache
 

=== modified file 'lib/lp/registry/doc/distribution-sourcepackage.txt'
--- lib/lp/registry/doc/distribution-sourcepackage.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/registry/doc/distribution-sourcepackage.txt	2012-01-20 16:17:27 +0000
@@ -279,18 +279,15 @@
     # karma for their efforts.
     >>> ppa_beta_owner_id = ppa_beta.owner.id
     >>> ppa_nightly_owner_id = ppa_nightly.owner.id
-    >>> transaction.commit()
 
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> LaunchpadZopelessLayer.switchDbUser('karma')
+    >>> from lp.testing.dbuser import switch_dbuser
+    >>> switch_dbuser('karma')
     >>> from lp.registry.model.karma import KarmaTotalCache
     >>> cache_entry = KarmaTotalCache(person=ppa_beta_owner_id,
     ...     karma_total=200)
     >>> cache_entry = KarmaTotalCache(person=ppa_nightly_owner_id,
     ...     karma_total=201)
-    >>> import transaction
-    >>> transaction.commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
 The results of findRelatedArchives() are sorted so that archive containing
 the package created by the person with the greatest karma is first:

=== modified file 'lib/lp/registry/doc/karmacache.txt'
--- lib/lp/registry/doc/karmacache.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/registry/doc/karmacache.txt	2012-01-20 16:17:27 +0000
@@ -9,12 +9,12 @@
 which runs daily. The script does that by using the IKarmaCacheManager API.
 
     >>> from zope.component import getUtility
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import switch_dbuser
     >>> from lp.registry.interfaces.karma import IKarmaCacheManager
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.model.karma import KarmaCategory
 
-    >>> LaunchpadZopelessLayer.switchDbUser('karma')
+    >>> switch_dbuser('karma')
     >>> karmacachemanager = getUtility(IKarmaCacheManager)
 
 == Creating new KarmaCache entries ==

=== modified file 'lib/lp/registry/doc/standing.txt'
--- lib/lp/registry/doc/standing.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/registry/doc/standing.txt	2012-01-20 16:17:27 +0000
@@ -54,6 +54,7 @@
     >>> from lp.registry.scripts.standing import (
     ...     UpdatePersonalStanding)
     >>> from lp.services.config import config
+    >>> from lp.testing.dbuser import switch_dbuser
     >>> from lp.testing.layers import LaunchpadZopelessLayer
     >>> from lp.services.log.logger import DevNullLogger
     >>> class TestableScript(UpdatePersonalStanding):
@@ -63,16 +64,12 @@
     ...         # Simulate Mailman acting changed state.
     ...         flush_database_updates()
     ...         mailinglists_helper.mailman.act()
-    ...         # Commit the in-progress transaction, since switching
-    ...         # database users does an abort.
-    ...         LaunchpadZopelessLayer.txn.commit()
     ...         launchpad_dbuser = config.launchpad.dbuser
-    ...         LaunchpadZopelessLayer.switchDbUser(
-    ...             config.standingupdater.dbuser)
+    ...         switch_dbuser(config.standingupdater.dbuser)
     ...         self.txn = LaunchpadZopelessLayer.txn
     ...         self.logger = DevNullLogger()
     ...         results = super(TestableScript, self).main()
-    ...         LaunchpadZopelessLayer.switchDbUser(launchpad_dbuser)
+    ...         switch_dbuser(launchpad_dbuser)
     ...         return results
     >>> script = TestableScript('update-standing', test_args=[])
 

=== modified file 'lib/lp/registry/tests/test_distributionsourcepackage.py'
--- lib/lp/registry/tests/test_distributionsourcepackage.py	2011-12-30 06:14:56 +0000
+++ lib/lp/registry/tests/test_distributionsourcepackage.py	2012-01-20 16:17:27 +0000
@@ -25,6 +25,7 @@
     StormStatementRecorder,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -203,14 +204,12 @@
         self.source_package = self.distribution.getSourcePackage('gedit')
 
         # Add slightly more soyuz karma for person_nightly for this package.
-        transaction.commit()
-        self.layer.switchDbUser('karma')
+        switch_dbuser('karma')
         self.person_beta_karma = KarmaTotalCache(
             person=self.person_beta, karma_total=200)
         self.person_nightly_karma = KarmaTotalCache(
             person=self.person_nightly, karma_total=201)
-        transaction.commit()
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
 
     def test_order_by_soyuz_package_karma(self):
         # Returned archives are ordered by the soyuz karma of the
@@ -227,10 +226,9 @@
 
         # Update the soyuz karma for person_beta for this package so that
         # it is greater than person_nightly's.
-        self.layer.switchDbUser('karma')
+        switch_dbuser('karma')
         self.person_beta_karma.karma_total = 202
-        transaction.commit()
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
 
         related_archives = self.source_package.findRelatedArchives()
         related_archive_names = [

=== modified file 'lib/lp/registry/tests/test_initderiveddistroseries.py'
--- lib/lp/registry/tests/test_initderiveddistroseries.py	2012-01-01 02:58:52 +0000
+++ lib/lp/registry/tests/test_initderiveddistroseries.py	2012-01-20 16:17:27 +0000
@@ -6,7 +6,6 @@
 
 __metaclass__ = type
 
-import transaction
 from zope.component import getUtility
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
@@ -28,6 +27,7 @@
     login_person,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
@@ -103,8 +103,7 @@
         parent1, parent2 = self.setUpParents(
             packages1={'p1': '0.1-1'}, packages2={'p2': '2.1'})
         child = self.factory.makeDistroSeries()
-        transaction.commit()
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
 
         child = self._fullInitialize(
             [parent1, parent2], child=child)
@@ -119,8 +118,7 @@
         parent1, parent2 = self.setUpParents(
             packages1={'p1': '0.1-1'}, packages2={'p2': '2.1'})
         child = self.factory.makeDistroSeries()
-        transaction.commit()
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
 
         child = self._fullInitialize(
             [parent1, parent2], child=child)
@@ -130,7 +128,7 @@
             [(u'p1', u'0.1-1'), (u'p2', u'2.1')])
         # Switch back to launchpad_main to be able to cleanup the
         # feature flags.
-        self.layer.switchDbUser('launchpad_main')
+        switch_dbuser('launchpad_main')
 
     def test_multiple_parents_do_not_close_bugs(self):
         # The initialization does not close the bugs on the copied
@@ -139,8 +137,7 @@
         parent1, parent2 = self.setUpParents(
             packages1={'p1': '0.1-1'}, packages2={'p2': '2.1'})
         child = self.factory.makeDistroSeries()
-        transaction.commit()
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
 
         # Patch close_bugs_for_sourcepublication to be able to record if
         # the method has been called.
@@ -164,7 +161,7 @@
             fakeCloseBugs.call_count)
         # Switch back to launchpad_main to be able to cleanup the
         # feature flags.
-        self.layer.switchDbUser('launchpad_main')
+        switch_dbuser('launchpad_main')
 
     def test_packageset_check_performed(self):
         # Packagesets passed to initDerivedDistroSeries are passed down

=== modified file 'lib/lp/registry/tests/test_pillarname_triggers.py'
--- lib/lp/registry/tests/test_pillarname_triggers.py	2011-12-30 06:14:56 +0000
+++ lib/lp/registry/tests/test_pillarname_triggers.py	2012-01-20 16:17:27 +0000
@@ -9,6 +9,7 @@
 import unittest
 
 from lp.services.database.sqlbase import cursor
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -16,7 +17,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
     def testDistributionTable(self):
         cur = cursor()

=== modified file 'lib/lp/registry/tests/test_prf_finder.py'
--- lib/lp/registry/tests/test_prf_finder.py	2012-01-01 02:58:52 +0000
+++ lib/lp/registry/tests/test_prf_finder.py	2012-01-20 16:17:27 +0000
@@ -28,6 +28,7 @@
     reset_logging,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -193,8 +194,7 @@
         return file_path, file_name
 
     def setUp(self):
-        LaunchpadZopelessLayer.switchDbUser(
-            config.productreleasefinder.dbuser)
+        switch_dbuser(config.productreleasefinder.dbuser)
         self.release_root = tempfile.mkdtemp()
         self.release_url = 'file://' + self.release_root
 

=== modified file 'lib/lp/registry/tests/test_ro_user.py'
--- lib/lp/registry/tests/test_ro_user.py	2011-12-30 06:14:56 +0000
+++ lib/lp/registry/tests/test_ro_user.py	2012-01-20 16:17:27 +0000
@@ -10,6 +10,7 @@
 import psycopg2
 
 from lp.services.database.sqlbase import cursor
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -18,7 +19,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        self.layer.switchDbUser('ro')
+        switch_dbuser('ro')
 
     def test(self):
         """Test that read-only users cannot make changes to the database."""

=== modified file 'lib/lp/registry/tests/test_teammembership.py'
--- lib/lp/registry/tests/test_teammembership.py	2011-12-30 06:14:56 +0000
+++ lib/lp/registry/tests/test_teammembership.py	2012-01-20 16:17:27 +0000
@@ -81,7 +81,7 @@
 class TestTeamMembershipSetScripts(TestCaseWithFactory):
     """Separate Testcase to separate out examples required dbuser switches.
 
-    This uses the LaunchpadZoplelessLayer to provide layer.switchDbUser
+    This uses the LaunchpadZopelessLayer to provide switch_dbuser.
     """
 
     layer = LaunchpadZopelessLayer

=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py	2012-01-13 16:23:10 +0000
+++ lib/lp/scripts/tests/test_garbo.py	2012-01-20 16:17:27 +0000
@@ -101,6 +101,7 @@
     TestCase,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseLayer,
     LaunchpadScriptLayer,
@@ -383,8 +384,7 @@
         self.log.addHandler(handler)
 
     def runFrequently(self, maximum_chunk_size=2, test_args=()):
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser('garbo_daily')
+        switch_dbuser('garbo_daily')
         collector = FrequentDatabaseGarbageCollector(
             test_args=list(test_args))
         collector._maximum_chunk_size = maximum_chunk_size
@@ -393,8 +393,7 @@
         return collector
 
     def runDaily(self, maximum_chunk_size=2, test_args=()):
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser('garbo_daily')
+        switch_dbuser('garbo_daily')
         collector = DailyDatabaseGarbageCollector(test_args=list(test_args))
         collector._maximum_chunk_size = maximum_chunk_size
         collector.logger = self.log
@@ -402,7 +401,7 @@
         return collector
 
     def runHourly(self, maximum_chunk_size=2, test_args=()):
-        LaunchpadZopelessLayer.switchDbUser('garbo_hourly')
+        switch_dbuser('garbo_hourly')
         collector = HourlyDatabaseGarbageCollector(test_args=list(test_args))
         collector._maximum_chunk_size = maximum_chunk_size
         collector.logger = self.log
@@ -417,7 +416,7 @@
             now - timedelta(days=1) + timedelta(seconds=60),  # Not garbage
             now,  # Not garbage
             ]
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         store = IMasterStore(OAuthNonce)
 
         # Make sure we start with 0 nonces.
@@ -461,7 +460,7 @@
             now - 1 * DAYS + 1 * MINUTES,  # Not garbage
             now,  # Not garbage
             ]
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
         store = IMasterStore(OpenIDConsumerNonce)
 
@@ -496,14 +495,14 @@
         results_to_keep_count = (
             config.codeimport.consecutive_failure_limit - 1)
 
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         code_import_id = self.factory.makeCodeImport().id
         machine_id = self.factory.makeCodeImportMachine().id
         requester_id = self.factory.makePerson().id
         transaction.commit()
 
         def new_code_import_result(timestamp):
-            LaunchpadZopelessLayer.switchDbUser('testadmin')
+            switch_dbuser('testadmin')
             CodeImportResult(
                 date_created=timestamp,
                 code_importID=code_import_id, machineID=machine_id,
@@ -550,7 +549,7 @@
         now = datetime.now(UTC)
         store = IMasterStore(CodeImportResult)
 
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         machine = self.factory.makeCodeImportMachine()
         requester = self.factory.makePerson()
         # Create 6 code import events for this machine, 3 on each side of 30
@@ -578,7 +577,7 @@
     def test_OpenIDConsumerAssociationPruner(self):
         pruner = OpenIDConsumerAssociationPruner
         table_name = pruner.table_name
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         store_selector = getUtility(IStoreSelector)
         store = store_selector.get(MAIN_STORE, MASTER_FLAVOR)
         now = time.time()
@@ -602,7 +601,7 @@
         # test is running slow.
         self.runFrequently()
 
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         store = store_selector.get(MAIN_STORE, MASTER_FLAVOR)
         # Confirm all the rows we know should have been expired have
         # been expired. These are the ones that would be expired using
@@ -620,7 +619,7 @@
         self.failUnless(num_unexpired > 0)
 
     def test_RevisionAuthorEmailLinker(self):
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         rev1 = self.factory.makeRevision('Author 1 <author-1@xxxxxxxxxxx>')
         rev2 = self.factory.makeRevision('Author 2 <author-2@xxxxxxxxxxx>')
 
@@ -636,7 +635,7 @@
 
         # Only the validated email address associated with a Person
         # causes a linkage.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(rev1.revision_author.person, person1)
         self.assertEqual(rev2.revision_author.person, None)
 
@@ -645,11 +644,11 @@
         self.assertEqual(rev2.revision_author.person, None)
 
         self.runDaily()
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(rev2.revision_author.person, person2)
 
     def test_HWSubmissionEmailLinker(self):
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         sub1 = self.factory.makeHWSubmission(
             emailaddress='author-1@xxxxxxxxxxx')
         sub2 = self.factory.makeHWSubmission(
@@ -667,7 +666,7 @@
 
         # Only the validated email address associated with a Person
         # causes a linkage.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(sub1.owner, person1)
         self.assertEqual(sub2.owner, None)
 
@@ -676,14 +675,14 @@
         self.assertEqual(sub2.owner, None)
 
         self.runDaily()
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(sub2.owner, person2)
 
     def test_PersonPruner(self):
         personset = getUtility(IPersonSet)
         # Switch the DB user because the garbo_daily user isn't allowed to
         # create person entries.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
         # Create two new person entries, both not linked to anything. One of
         # them will have the present day as its date created, and so will not
@@ -711,7 +710,7 @@
 
     def test_BugNotificationPruner(self):
         # Create some sample data
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         notification = BugNotification(
             messageID=1,
             bugID=1,
@@ -777,7 +776,7 @@
     def _test_AnswerContactPruner(self, status, interval, expected_count=0):
         # Garbo should remove answer contacts for accounts with given 'status'
         # which was set more than 'interval' days ago.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         store = IMasterStore(AnswerContact)
 
         person = self.factory.makePerson()
@@ -802,7 +801,7 @@
 
         self.runDaily()
 
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(
             store.find(
                 AnswerContact,
@@ -830,7 +829,7 @@
 
     def test_BranchJobPruner(self):
         # Garbo should remove jobs completed over 30 days ago.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         store = IMasterStore(Job)
 
         db_branch = self.factory.makeAnyBranch()
@@ -849,7 +848,7 @@
 
         self.runDaily()
 
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(
             store.find(
                 BranchJob,
@@ -859,7 +858,7 @@
     def test_BranchJobPruner_doesnt_prune_recent_jobs(self):
         # Check to make sure the garbo doesn't remove jobs that aren't more
         # than thirty days old.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         store = IMasterStore(Job)
 
         db_branch = self.factory.makeAnyBranch(
@@ -877,13 +876,13 @@
 
         self.runDaily()
 
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(store.find(BranchJob).count(), 1)
 
     def test_ObsoleteBugAttachmentPruner(self):
         # Bug attachments without a LibraryFileContent record are removed.
 
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         bug = self.factory.makeBug()
         attachment = self.factory.makeBugAttachment(bug=bug)
         transaction.commit()
@@ -896,11 +895,11 @@
 
         # But once we delete the LfC record, the attachment is deleted
         # in the next daily garbo run.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         removeSecurityProxy(attachment.libraryfile).content = None
         transaction.commit()
         self.runDaily()
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         self.assertEqual(bug.attachments.count(), 0)
 
     def test_TimeLimitedTokenPruner(self):
@@ -924,7 +923,7 @@
             path="sample path", token="bar"))))
 
     def test_CacheSuggestivePOTemplates(self):
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         template = self.factory.makePOTemplate()
         self.runDaily()
 
@@ -938,7 +937,7 @@
         self.assertEqual(1, count)
 
     def test_BugSummaryJournalRollup(self):
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
 
         # Generate a load of entries in BugSummaryJournal.
@@ -960,7 +959,7 @@
     def test_UnusedPOTMsgSetPruner_removes_obsolete_message_sets(self):
         # UnusedPOTMsgSetPruner removes any POTMsgSet that are
         # participating in a POTemplate only as obsolete messages.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         pofile = self.factory.makePOFile()
         translation_message = self.factory.makeCurrentTranslationMessage(
             pofile=pofile)
@@ -979,7 +978,7 @@
     def test_UnusedPOTMsgSetPruner_removes_unreferenced_message_sets(self):
         # If a POTMsgSet is not referenced by any templates the
         # UnusedPOTMsgSetPruner will remove it.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
         potmsgset = self.factory.makePOTMsgSet()
         # Cheekily drop any references to the POTMsgSet we just created.
         store = IMasterStore(POTMsgSet)
@@ -1003,7 +1002,7 @@
     def test_LoginTokenPruner(self):
         store = IMasterStore(LoginToken)
         now = datetime.now(UTC)
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
         # It is configured as a daily task.
         self.assertTrue(
@@ -1024,7 +1023,7 @@
 
         # Run the pruner. Batching is tested by the BulkPruner tests so
         # no need to repeat here.
-        LaunchpadZopelessLayer.switchDbUser('garbo_daily')
+        switch_dbuser('garbo_daily')
         pruner = LoginTokenPruner(logging.getLogger('garbo'))
         while not pruner.isDone():
             pruner(10)

=== modified file 'lib/lp/services/apachelogparser/tests/test_apachelogparser.py'
--- lib/lp/services/apachelogparser/tests/test_apachelogparser.py	2011-12-30 01:04:24 +0000
+++ lib/lp/services/apachelogparser/tests/test_apachelogparser.py	2012-01-20 16:17:27 +0000
@@ -29,6 +29,7 @@
     MAIN_STORE,
     )
 from lp.testing import TestCase
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadZopelessLayer,
     ZopelessLayer,
@@ -388,7 +389,7 @@
 
     def setUp(self):
         super(TestParsedFilesDetection, self).setUp()
-        self.layer.switchDbUser(DBUSER)
+        switch_dbuser(DBUSER)
 
     def test_not_parsed_file(self):
         # A file that has never been parsed will have to be parsed from the
@@ -470,7 +471,7 @@
 
     def setUp(self):
         super(Test_create_or_update_parsedlog_entry, self).setUp()
-        self.layer.switchDbUser(DBUSER)
+        switch_dbuser(DBUSER)
 
     def test_creation_of_new_entries(self):
         # When given a first_line that doesn't exist in the ParsedApacheLog

=== modified file 'lib/lp/services/database/tests/test_transaction_decorators.py'
--- lib/lp/services/database/tests/test_transaction_decorators.py	2011-12-30 01:48:17 +0000
+++ lib/lp/services/database/tests/test_transaction_decorators.py	2012-01-20 16:17:27 +0000
@@ -17,6 +17,7 @@
     IStoreSelector,
     MAIN_STORE,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -26,7 +27,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        self.layer.switchDbUser('librarian')
+        switch_dbuser('librarian')
         self.store = getUtility(IStoreSelector).get(
                 MAIN_STORE, DEFAULT_FLAVOR)
         self.content_id = db.Library().add('deadbeef', 1234, 'abababab')

=== modified file 'lib/lp/services/librarianserver/tests/test_db.py'
--- lib/lp/services/librarianserver/tests/test_db.py	2011-12-30 02:19:42 +0000
+++ lib/lp/services/librarianserver/tests/test_db.py	2012-01-20 16:17:27 +0000
@@ -13,6 +13,7 @@
     IStoreSelector,
     MAIN_STORE,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -20,7 +21,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        self.layer.switchDbUser('librarian')
+        switch_dbuser('librarian')
 
     def test_lookupByDigest(self):
         # Create library
@@ -53,7 +54,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        self.layer.switchDbUser('librarian')
+        switch_dbuser('librarian')
         self.store = getUtility(IStoreSelector).get(
                 MAIN_STORE, DEFAULT_FLAVOR)
         self.content_id = db.Library().add('deadbeef', 1234, 'abababab')

=== modified file 'lib/lp/services/librarianserver/tests/test_gc.py'
--- lib/lp/services/librarianserver/tests/test_gc.py	2011-12-30 06:14:56 +0000
+++ lib/lp/services/librarianserver/tests/test_gc.py	2012-01-20 16:17:27 +0000
@@ -35,6 +35,7 @@
 from lp.services.log.logger import BufferLogger
 from lp.services.utils import utc_now
 from lp.testing import TestCase
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -61,7 +62,7 @@
 
         self.f1_id, self.f2_id = self._makeDupes()
 
-        self.layer.switchDbUser(config.librarian_gc.dbuser)
+        switch_dbuser(config.librarian_gc.dbuser)
         self.ztm = self.layer.txn
 
         # Make sure the files exist. We do this in setup, because we
@@ -102,7 +103,7 @@
         # Connect to the database as a user with file upload privileges,
         # in this case the PostgreSQL default user who happens to be an
         # administrator on launchpad development boxes.
-        self.layer.switchDbUser(dbuser='testadmin')
+        switch_dbuser(dbuser='testadmin')
         ztm = self.layer.txn
 
         ztm.begin()
@@ -542,14 +543,14 @@
     def test_delete_unwanted_files_bug437084(self):
         # There was a bug where delete_unwanted_files() would die
         # if the last file found on disk was unwanted.
-        self.layer.switchDbUser(dbuser='testadmin')
+        switch_dbuser(dbuser='testadmin')
         content = 'foo'
         self.client.addFile(
             'foo.txt', len(content), StringIO(content), 'text/plain')
         # Roll back the database changes, leaving the file on disk.
         transaction.abort()
 
-        self.layer.switchDbUser(config.librarian_gc.dbuser)
+        switch_dbuser(config.librarian_gc.dbuser)
 
         # This should cope.
         librariangc.delete_unwanted_files(self.con)
@@ -558,14 +559,14 @@
         # In production, our tree has symlinks in it now.  We need to be able
         # to cope.
         # First, let's make sure we have some trash.
-        self.layer.switchDbUser(dbuser='testadmin')
+        switch_dbuser(dbuser='testadmin')
         content = 'foo'
         self.client.addFile(
             'foo.txt', len(content), StringIO(content), 'text/plain')
         # Roll back the database changes, leaving the file on disk.
         transaction.abort()
 
-        self.layer.switchDbUser(config.librarian_gc.dbuser)
+        switch_dbuser(config.librarian_gc.dbuser)
 
         # Now, we will move the directory containing the trash somewhere else
         # and make a symlink to it.
@@ -752,7 +753,7 @@
                 open(path, 'w').write('whatever')
         self.layer.txn.abort()
 
-        self.layer.switchDbUser(config.librarian_gc.dbuser)
+        switch_dbuser(config.librarian_gc.dbuser)
 
         # Open a connection for our test
         self.con = connect(

=== modified file 'lib/lp/services/librarianserver/tests/test_storage_db.py'
--- lib/lp/services/librarianserver/tests/test_storage_db.py	2011-12-30 06:14:56 +0000
+++ lib/lp/services/librarianserver/tests/test_storage_db.py	2012-01-20 16:17:27 +0000
@@ -15,6 +15,7 @@
     LibrarianStorage,
     LibraryFileUpload,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -22,7 +23,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        self.layer.switchDbUser('librarian')
+        switch_dbuser('librarian')
         self.directory = tempfile.mkdtemp()
         self.storage = LibrarianStorage(self.directory, db.Library())
 

=== modified file 'lib/lp/services/librarianserver/tests/test_web.py'
--- lib/lp/services/librarianserver/tests/test_web.py	2011-12-30 06:14:56 +0000
+++ lib/lp/services/librarianserver/tests/test_web.py	2012-01-20 16:17:27 +0000
@@ -34,6 +34,7 @@
     LibraryFileAlias,
     TimeLimitedToken,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -380,7 +381,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        LaunchpadZopelessLayer.switchDbUser(config.librarian.dbuser)
+        switch_dbuser(config.librarian.dbuser)
 
     def commit(self):
         LaunchpadZopelessLayer.commit()
@@ -440,20 +441,18 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        LaunchpadZopelessLayer.switchDbUser(config.librarian.dbuser)
+        switch_dbuser(config.librarian.dbuser)
 
     def test_deletedContentNotFound(self):
         # Use a user with rights to change the deleted flag in the db.
         # This currently means a superuser.
-        LaunchpadZopelessLayer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
         alias = getUtility(ILibraryFileAliasSet).create(
                 'whatever', 8, StringIO('xxx\nxxx\n'), 'text/plain')
         alias_id = alias.id
         transaction.commit()
 
-        client = LibrarianClient()
-
         # This works
         alias = getUtility(ILibraryFileAliasSet)[alias_id]
         alias.open()
@@ -465,7 +464,6 @@
         retrieved_content = urlopen(url).read()
         self.failUnlessEqual(retrieved_content, 'xxx\nxxx\n')
 
-
         # But when we flag the content as deleted
         cur = cursor()
         cur.execute("""

=== modified file 'lib/lp/services/mail/doc/emailauthentication.txt'
--- lib/lp/services/mail/doc/emailauthentication.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/services/mail/doc/emailauthentication.txt	2012-01-20 16:17:27 +0000
@@ -10,15 +10,11 @@
 signature. First we have to import the OpenPGP keys we will use in the
 emails:
 
-    >>> from lp.services.config import config
-    >>> from lp.services.database.sqlbase import commit
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import lp_dbuser
     >>> from lp.testing.gpgkeys import import_public_test_keys
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> import_public_test_keys()
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
+    >>> with lp_dbuser():
+    ...     import_public_test_keys()
 
 For most of these tests, we don't care whether the timestamps are out of
 date:

=== modified file 'lib/lp/services/mail/tests/incomingmail.txt'
--- lib/lp/services/mail/tests/incomingmail.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/services/mail/tests/incomingmail.txt	2012-01-20 16:17:27 +0000
@@ -45,10 +45,9 @@
 
 Now we send a few test mails to foo.com, bar.com, and baz.com:
 
-    >>> from lp.services.database.sqlbase import commit
     >>> from lp.services.mail.tests.helpers import read_test_message
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
     >>> from lp.services.mail.sendmail import sendmail as original_sendmail
+    >>> from lp.testing.dbuser import switch_dbuser
 
 For these examples, we don't want the Precedence header added. Domains
 are treated without regard to case: for incoming mail, foo.com and
@@ -57,7 +56,7 @@
     >>> def sendmail(msg, to_addrs=None):
     ...     return original_sendmail(msg, to_addrs=to_addrs, bulk=False)
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> msgids = {'foo.com': [], 'bar.com': [], 'baz.com': []}
     >>> for domain in ('foo.com', 'bar.com', 'FOO.COM', 'baz.com'):
     ...     msg = read_test_message('signed_detached.txt')
@@ -73,16 +72,7 @@
 Since the User gets authenticated using OpenPGP signatures we have to
 import the keys before handleMail is called.
 
-    >>> from lp.services.config import config
-    >>> from lp.testing.gpgkeys import import_public_test_keys
-    >>> import_public_test_keys()
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
-    >>> zopeless_transaction = LaunchpadZopelessLayer.txn
-
-    >>> handleMailForTest = lambda: handleMail(
-    ...     zopeless_transaction,
-    ...     signature_timestamp_checker=accept_any_timestamp)
+from lp.testing.dbuser import switch_dbuser
 
 
 We temporarily override the error mails' From address, so that they will

=== modified file 'lib/lp/services/mail/tests/test_doc.py'
--- lib/lp/services/mail/tests/test_doc.py	2012-01-01 02:58:52 +0000
+++ lib/lp/services/mail/tests/test_doc.py	2012-01-20 16:17:27 +0000
@@ -12,6 +12,7 @@
 from lp.services.config import config
 from lp.services.testing import build_test_suite
 from lp.services.webapp.authorization import LaunchpadSecurityPolicy
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -37,7 +38,7 @@
         connects as a specific DB user.
         """
         cls._old_policy = setSecurityPolicy(LaunchpadSecurityPolicy)
-        LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
+        switch_dbuser(config.processmail.dbuser)
 
     @classmethod
     def testTearDown(cls):

=== modified file 'lib/lp/services/mail/tests/test_incoming.py'
--- lib/lp/services/mail/tests/test_incoming.py	2012-01-04 03:23:19 +0000
+++ lib/lp/services/mail/tests/test_incoming.py	2012-01-20 16:17:27 +0000
@@ -27,6 +27,7 @@
 from lp.services.mail.tests.helpers import testmails_path
 from lp.services.webapp.authorization import LaunchpadSecurityPolicy
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.factory import GPGSigningContext
 from lp.testing.gpgkeys import import_secret_test_key
 from lp.testing.layers import LaunchpadZopelessLayer
@@ -147,7 +148,7 @@
 
 def setUp(test):
     test._old_policy = setSecurityPolicy(LaunchpadSecurityPolicy)
-    LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser)
+    switch_dbuser(config.processmail.dbuser)
 
 
 def tearDown(test):

=== modified file 'lib/lp/services/scripts/doc/script-monitoring.txt'
--- lib/lp/services/scripts/doc/script-monitoring.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/services/scripts/doc/script-monitoring.txt	2012-01-20 16:17:27 +0000
@@ -25,10 +25,10 @@
   >>> from zope.component import getUtility
   >>> from lp.services.scripts.interfaces.scriptactivity import (
   ...     IScriptActivitySet)
-  >>> from lp.testing.layers import LaunchpadZopelessLayer
+  >>> from lp.testing.dbuser import switch_dbuser
 
   >>> UTC = pytz.timezone('UTC')
-  >>> LaunchpadZopelessLayer.switchDbUser('garbo_daily') # A script db user
+  >>> switch_dbuser('garbo_daily') # A script db user
 
   >>> activity = getUtility(IScriptActivitySet).recordSuccess(
   ...     name='script-name',

=== modified file 'lib/lp/services/statistics/tests/test_update_stats.py'
--- lib/lp/services/statistics/tests/test_update_stats.py	2011-12-30 06:14:56 +0000
+++ lib/lp/services/statistics/tests/test_update_stats.py	2012-01-20 16:17:27 +0000
@@ -17,6 +17,7 @@
 from lp.services.config import config
 from lp.services.database.sqlbase import cursor
 from lp.services.worlddata.interfaces.language import ILanguageSet
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.interfaces.potemplate import IPOTemplateSet
 
@@ -33,7 +34,7 @@
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        self.layer.switchDbUser('statistician')
+        switch_dbuser('statistician')
 
     def tearDown(self):
         # Test uses a subprocess, so force the database to be dirty

=== modified file 'lib/lp/services/worlddata/doc/language.txt'
--- lib/lp/services/worlddata/doc/language.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/services/worlddata/doc/language.txt	2012-01-20 16:17:27 +0000
@@ -249,12 +249,12 @@
   >>> translator_30.addLanguage(sr)
   >>> translator_40 = factory.makePerson(name=u'serbian-translator-karma-40')
   >>> translator_40.addLanguage(sr)
-  >>> from lp.testing.layers import LaunchpadZopelessLayer
-  >>> LaunchpadZopelessLayer.commit()
 
   # We need to fake some Karma.
   >>> from lp.registry.model.karma import KarmaCategory, KarmaCache
-  >>> LaunchpadZopelessLayer.switchDbUser('karma')
+  >>> from lp.testing.dbuser import switch_dbuser
+
+  >>> switch_dbuser('karma')
   >>> translations_category = KarmaCategory.selectOne(
   ...     KarmaCategory.name=='translations')
   >>> karma = KarmaCache(person=translator_30,
@@ -269,8 +269,7 @@
   >>> karma = KarmaCache(person=translator_40,
   ...                    category=translations_category,
   ...                    karmavalue=40)
-  >>> LaunchpadZopelessLayer.commit()
-  >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+  >>> switch_dbuser('launchpad')
   >>> for translator in sr.translators:
   ...   print translator.name
   serbian-translator-karma-40

=== modified file 'lib/lp/soyuz/doc/buildd-scoring.txt'
--- lib/lp/soyuz/doc/buildd-scoring.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/doc/buildd-scoring.txt	2012-01-20 16:17:27 +0000
@@ -28,21 +28,20 @@
     >>> hoary386.title
     u'The Hoary Hedgehog Release for i386 (x86)'
 
-    >>> from lp.services.database.sqlbase import commit
     >>> from lp.registry.interfaces.pocket import PackagePublishingPocket
     >>> from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
     >>> from lp.soyuz.enums import PackagePublishingStatus
     >>> from lp.soyuz.tests.test_publishing import (
     ...     SoyuzTestPublisher)
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import (
+    ...     lp_dbuser,
+    ...     switch_dbuser,
+    ...     )
 
     >>> test_publisher = SoyuzTestPublisher()
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
-    >>> test_publisher.prepareBreezyAutotest()
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> with lp_dbuser():
+    ...     test_publisher.prepareBreezyAutotest()
 
     >>> version = 1
 
@@ -51,15 +50,12 @@
     ...     pocket=PackagePublishingPocket.RELEASE,
     ...     date_created=LOCAL_NOW, manual=False, archive=None):
     ...     global version
-    ...     commit()
-    ...     LaunchpadZopelessLayer.switchDbUser('launchpad')
-    ...     pub = test_publisher.getPubSource(
-    ...        sourcename='test-build', version=str(version),
-    ...        distroseries=hoary, component=component_name,
-    ...        urgency=urgency, pocket=pocket,
-    ...        status=PackagePublishingStatus.PUBLISHED, archive=archive)
-    ...     commit()
-    ...     LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    ...     with lp_dbuser():
+    ...         pub = test_publisher.getPubSource(
+    ...             sourcename='test-build', version=str(version),
+    ...             distroseries=hoary, component=component_name,
+    ...             urgency=urgency, pocket=pocket,
+    ...             status=PackagePublishingStatus.PUBLISHED, archive=archive)
     ...     version += 1
     ...     build = pub.sourcepackagerelease.createBuild(
     ...         hoary386, pub.pocket, pub.archive)
@@ -86,8 +82,7 @@
 
 If the archive is private, its score is boosted by 10000:
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> private_ppa = factory.makeArchive()
     >>> private_ppa.buildd_secret = "secret"
     >>> private_ppa.private = True
@@ -113,7 +108,7 @@
     12415
 
     >>> private_ppa.relative_build_score = 0
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> switch_dbuser(test_dbuser)
 
 
  * 1500 points for pocket 'RELEASE',
@@ -251,7 +246,7 @@
 be considered when there is nothing else to build. Even language-packs
 and build retries will be built before them.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> from lp.soyuz.enums import ArchivePurpose
     >>> from lp.soyuz.interfaces.archive import IArchiveSet
     >>> copy = getUtility(IArchiveSet).new(

=== modified file 'lib/lp/soyuz/doc/closing-bugs-from-changelogs.txt'
--- lib/lp/soyuz/doc/closing-bugs-from-changelogs.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/doc/closing-bugs-from-changelogs.txt	2012-01-20 16:17:27 +0000
@@ -22,12 +22,13 @@
 package, but the bugs they reference may be filed on the generic
 distribution package.
 
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
     >>> from lp.bugs.interfaces.bug import CreateBugParams
     >>> from lp.registry.interfaces.distribution import IDistributionSet
+    >>> from lp.testing.dbuser import switch_dbuser
+
     >>> login('no-priv@xxxxxxxxxxxxx')
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
     >>> ubuntu_hoary = ubuntu.getSeries('hoary')
@@ -83,16 +84,14 @@
     >>> queue_item = add_package_upload(pmount_release, pmount_bug_id)
 
     # Need to commit the transaction so that the changes file can be
-    # downloaded from the Librarian.
-    >>> from lp.services.database.sqlbase import commit
-    >>> commit()
+    # downloaded from the Librarian.  switch_dbuser takes care of this.
+    >>> switch_dbuser(test_dbuser)
 
 Right after the queue items have been processed by the publishing
 scripts, close_bugs_for_queue_item() is called with the id of each queue item
 that has been published. Passing a queue item with a Launchpad-bugs-fixed
 header will close the specified bug.
 
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
     >>> from lp.bugs.interfaces.bug import IBugSet
 
     >>> def print_single_task_status(bug_id):
@@ -112,8 +111,7 @@
 The changelog associated with the SourcePackageRelease is automatically
 added as a comment from the janitor.
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
     >>> pmount_bug = getUtility(IBugSet).get(pmount_bug_id)
     >>> last_comment = pmount_bug.messages[-1]
     >>> print pmount_release.creator.displayname
@@ -194,11 +192,9 @@
     ...     print "Before:"
     ...     for bug_id in bug_id_list:
     ...         print print_single_task_status(bug_id)
-    ...     commit()
-    ...     LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    ...     switch_dbuser(test_dbuser)
     ...     close_bugs_for_queue_item(queue_item)
-    ...     commit()
-    ...     LaunchpadZopelessLayer.switchDbUser('launchpad')
+    ...     switch_dbuser('launchpad')
     ...     print "After:"
     ...     for bug_id in bug_id_list:
     ...         print print_single_task_status(bug_id)
@@ -291,8 +287,7 @@
 The closing of bugs are done in process-accepted.py, right after the
 queue items have been processed.
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> queue_item = add_package_upload(pmount_release, fixing_text)
     >>> queue_item.setAccepted()
@@ -316,8 +311,7 @@
     >>> print_single_task_status(another_pmount_bug_id)
     'CONFIRMED'
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> switch_dbuser(test_dbuser)
 
     >>> import os.path
     >>> import subprocess

=== modified file 'lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt'
--- lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt	2012-01-20 16:17:27 +0000
@@ -205,8 +205,9 @@
 First, update the cache tables for Celso's PPA:
 
     >>> from lp.services.config import config
+    >>> from lp.testing.dbuser import switch_dbuser
     >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> LaunchpadZopelessLayer.switchDbUser(config.statistician.dbuser)
+    >>> switch_dbuser(config.statistician.dbuser)
 
     >>> from lp.services.log.logger import FakeLogger
     >>> from lp.soyuz.model.distributionsourcepackagecache import (
@@ -234,7 +235,7 @@
 Then, supersede all pmount publications in warty for pmount (this sets
 us up to demonstrate bug 208233).
 
-    >>> LaunchpadZopelessLayer.switchDbUser('archivepublisher')
+    >>> switch_dbuser('archivepublisher')
     >>> from lp.soyuz.model.binarypackagename import BinaryPackageName
     >>> from lp.soyuz.model.distroarchseries import DistroArchSeries
     >>> from lp.soyuz.model.distroarchseriesbinarypackage import (
@@ -253,7 +254,7 @@
     ...          s = p.supersede()
     >>> commit()
     >>> flush_database_updates()
-    >>> LaunchpadZopelessLayer.switchDbUser(config.statistician.dbuser)
+    >>> switch_dbuser(config.statistician.dbuser)
 
 Now, if that bug is actually fixed, this works:
 

=== modified file 'lib/lp/soyuz/doc/package-arch-specific.txt'
--- lib/lp/soyuz/doc/package-arch-specific.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/doc/package-arch-specific.txt	2012-01-20 16:17:27 +0000
@@ -16,11 +16,14 @@
 
     >>> from lp.soyuz.tests.test_publishing import (
     ...     SoyuzTestPublisher)
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import (
+    ...     lp_dbuser,
+    ...     switch_dbuser,
+    ...     )
 
 Publication will be added as 'launchpad' DB user.
 
-    >>> LaunchpadZopelessLayer.switchDbUser("launchpad")
+    >>> switch_dbuser("launchpad")
 
     >>> test_publisher = SoyuzTestPublisher()
     >>> ignore = test_publisher.setUpDefaultDistroSeries(hoary)
@@ -43,9 +46,7 @@
 Good, all done, we can commit the publications and continue the tests
 with the buildmaster DB user.
 
-    >>> from lp.services.database.sqlbase import commit
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> switch_dbuser(test_dbuser)
 
 
 == Check the architectures to build ==
@@ -153,18 +154,13 @@
     >>> from lp.soyuz.enums import (
     ...     PackagePublishingStatus)
 
-    >>> LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-    >>> pub_single = test_publisher.getPubSource(
-    ...     sourcename='single', version='1.0',
-    ...     architecturehintlist="any")
-
-    >>> binaries = test_publisher.getPubBinaries(
-    ...     binaryname='single-bin', pub_source=pub_single,
-    ...     status=PackagePublishingStatus.PUBLISHED)
-
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> with lp_dbuser():
+    ...     pub_single = test_publisher.getPubSource(
+    ...         sourcename='single', version='1.0',
+    ...         architecturehintlist="any")
+    ...     binaries = test_publisher.getPubBinaries(
+    ...         binaryname='single-bin', pub_source=pub_single,
+    ...         status=PackagePublishingStatus.PUBLISHED)
 
     >>> len(set(pub.binarypackagerelease.name
     ...         for pub in pub_single.getPublishedBinaries()))
@@ -210,16 +206,11 @@
 Binary PAS lines are ignored for PPAs as well. We create a binary for
 the existing test PPA source publication.
 
-    >>> LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-    >>> pub_ppa.archive.require_virtualized = False
-
-    >>> binaries = test_publisher.getPubBinaries(
-    ...     binaryname='ppa-bin', pub_source=pub_ppa,
-    ...     status=PackagePublishingStatus.PUBLISHED)
-
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> with lp_dbuser():
+    ...     pub_ppa.archive.require_virtualized = False
+    ...     binaries = test_publisher.getPubBinaries(
+    ...         binaryname='ppa-bin', pub_source=pub_ppa,
+    ...         status=PackagePublishingStatus.PUBLISHED)
 
 And it will build in the same architectures with or without a
 corresponding PAS binary line.
@@ -241,26 +232,21 @@
 We will create a 'multiple' source publication build two binaries,
 'bin-one' and 'bin-two'.
 
-    >>> LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-    >>> pub_multiple = test_publisher.getPubSource(
-    ...     sourcename='multiple', version='1.1',
-    ...     architecturehintlist="any")
-
-    >>> for build in pub_multiple.createMissingBuilds():
-    ...     bin_one = test_publisher.uploadBinaryForBuild(
-    ...         build, 'bin-one')
-    ...     pub_bin_one = test_publisher.publishBinaryInArchive(
-    ...         bin_one, pub_multiple.archive,
-    ...         status=PackagePublishingStatus.PUBLISHED)
-    ...     bin_two = test_publisher.uploadBinaryForBuild(
-    ...         build, 'bin-two')
-    ...     pub_bin_two = test_publisher.publishBinaryInArchive(
-    ...         bin_two, pub_multiple.archive,
-    ...         status=PackagePublishingStatus.PUBLISHED)
-
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> with lp_dbuser():
+    ...     pub_multiple = test_publisher.getPubSource(
+    ...         sourcename='multiple', version='1.1',
+    ...         architecturehintlist="any")
+    ...     for build in pub_multiple.createMissingBuilds():
+    ...         bin_one = test_publisher.uploadBinaryForBuild(
+    ...             build, 'bin-one')
+    ...         pub_bin_one = test_publisher.publishBinaryInArchive(
+    ...             bin_one, pub_multiple.archive,
+    ...             status=PackagePublishingStatus.PUBLISHED)
+    ...         bin_two = test_publisher.uploadBinaryForBuild(
+    ...             build, 'bin-two')
+    ...         pub_bin_two = test_publisher.publishBinaryInArchive(
+    ...             bin_two, pub_multiple.archive,
+    ...             status=PackagePublishingStatus.PUBLISHED)
 
     >>> len(set(pub.binarypackagerelease.name
     ...         for pub in pub_multiple.getPublishedBinaries()))

=== modified file 'lib/lp/soyuz/doc/package-cache.txt'
--- lib/lp/soyuz/doc/package-cache.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/doc/package-cache.txt	2012-01-20 16:17:27 +0000
@@ -487,16 +487,16 @@
 
 We will use `SoyuzTestPublisher` for creating convenient publications.
 
-    >>> from lp.services.database.sqlbase import commit
-    >>> from lp.soyuz.enums import (
-    ...              PackagePublishingStatus)
-    >>> from lp.soyuz.tests.test_publishing import (
-    ...              SoyuzTestPublisher)
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.soyuz.enums import PackagePublishingStatus
+    >>> from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
+    >>> from lp.testing.layers import (
+    ...     lp_dbuser,
+    ...     switch_dbuser,
+    ...     )
 
     >>> test_publisher = SoyuzTestPublisher()
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> unused = test_publisher.setUpDefaultDistroSeries(warty)
     >>> test_publisher.addFakeChroots()
@@ -529,8 +529,7 @@
     ...      set(pub.binarypackagerelease.name for pub in published_binaries))
     1
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> switch_dbuser(test_dbuser)
 
 Exactly 2 new sources and 2 new binaries will be accounted.
 
@@ -543,7 +542,7 @@
 
 Let's create one source with a single binary in DELETED status.
 
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
     >>> deleted_source = test_publisher.getPubSource(
     ...              sourcename = 'pending-source',
@@ -557,7 +556,7 @@
     ...      set(pub.binarypackagerelease.name for pub in deleted_binaries))
     1
 
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> switch_dbuser(test_dbuser)
 
 Distroseries package counters will not account DELETED publications.
 
@@ -702,23 +701,9 @@
     ...      print '%d binaries cached [%d]' % (
     ...          archive.binaries_cached, binary_caches.count())
     >>> def print_search_results(text, user=None):
-    ...      commit()
-    ...      LaunchpadZopelessLayer.switchDbUser('launchpad')
-    ...      for ppa in ubuntu.searchPPAs(text, user=user):
-    ...          print ppa.displayname
-    ...      LaunchpadZopelessLayer.switchDbUser(test_dbuser)
-    >>> def enable_archive(archive):
-    ...      commit()
-    ...      LaunchpadZopelessLayer.switchDbUser('launchpad')
-    ...      archive.enable()
-    ...      commit()
-    ...      LaunchpadZopelessLayer.switchDbUser(test_dbuser)
-    >>> def disable_archive(archive):
-    ...      commit()
-    ...      LaunchpadZopelessLayer.switchDbUser('launchpad')
-    ...      archive.disable()
-    ...      commit()
-    ...      LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    ...      with lp_dbuser():
+    ...          for ppa in ubuntu.searchPPAs(text, user=user):
+    ...              print ppa.displayname
 
     >>> rebuild_caches(cprov.archive)
 
@@ -731,7 +716,8 @@
 
 When Celso's PPA gets disabled, the indexes remain in the DB.
 
-    >>> disable_archive(cprov.archive)
+    >>> with lp_dbuser():
+    ...     cprov.archive.disable()
 
     >>> print_caches(cprov.archive)
     3 sources cached [3]
@@ -766,7 +752,8 @@
 will be re-created when the indexes are rebuilt and the ppa becomes
 publicly searchable again.
 
-    >>> enable_archive(cprov.archive)
+    >>> with lp_dbuser():
+    ...     cprov.archive.enable()
 
     >>> rebuild_caches(cprov.archive)
 

=== modified file 'lib/lp/soyuz/doc/queuebuilder.txt'
--- lib/lp/soyuz/doc/queuebuilder.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/doc/queuebuilder.txt	2012-01-20 16:17:27 +0000
@@ -130,13 +130,11 @@
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> cprov = getUtility(IPersonSet).getByName('cprov')
 
-    >>> from lp.services.database.sqlbase import commit
     >>> from lp.soyuz.tests.test_publishing import (
     ...     SoyuzTestPublisher)
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import switch_dbuser
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
+    >>> switch_dbuser('launchpad')
 
 Let's use SoyuzTestPublisher to create a set of publications in the
 exact state to expose the features that we want to test.
@@ -288,8 +286,7 @@
     >>> old_build_ids.append(failed_build.id)
     >>> old_build_ids.append(ok_build.id)
 
-    >>> commit()
-    >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    >>> switch_dbuser(test_dbuser)
 
 
 == Create and verify the new builds ==

=== modified file 'lib/lp/soyuz/doc/soyuz-set-of-uploads.txt'
--- lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2012-01-20 16:17:27 +0000
@@ -129,6 +129,7 @@
     >>> from lp.services.log.logger import FakeLogger
     >>> from lp.soyuz.scripts.soyuz_process_upload import (
     ...     ProcessUpload)
+    >>> from lp.testing.dbuser import switch_dbuser
     >>> from lp.testing.layers import LaunchpadZopelessLayer
     >>> def process_uploads(upload_policy, series, loglevel):
     ...     """Simulate process-upload.py script run.
@@ -145,8 +146,7 @@
     ...         args.extend(["-s", series])
     ...     # Run script under 'uploader' DB user.  The dbuser argument to the
     ...     # script constructor is ignored, so we must change DB users here.
-    ...     LaunchpadZopelessLayer.txn.commit()
-    ...     LaunchpadZopelessLayer.switchDbUser(config.uploader.dbuser)
+    ...     switch_dbuser(config.uploader.dbuser)
     ...     process = ProcessUpload(
     ...         'process-upload', dbuser='ignored', test_args=args)
     ...     process.logger = FakeLogger()
@@ -154,7 +154,7 @@
     ...         process.logger.setLevel(loglevel)
     ...     process.txn = LaunchpadZopelessLayer.txn
     ...     process.main()
-    ...     LaunchpadZopelessLayer.switchDbUser('launchpad')
+    ...     switch_dbuser('launchpad')
 
 And we need a way to process the accepted queue
 

=== modified file 'lib/lp/soyuz/scripts/tests/test_copypackage.py'
--- lib/lp/soyuz/scripts/tests/test_copypackage.py	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/scripts/tests/test_copypackage.py	2012-01-20 16:17:27 +0000
@@ -83,6 +83,7 @@
     StormStatementRecorder,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseLayer,
     LaunchpadFunctionalLayer,
@@ -1389,8 +1390,7 @@
             archive=archive, version='1.0-2', architecturehintlist='any')
         dsp = self.factory.makeDistroSeriesParent()
         target_archive = dsp.derived_series.main_archive
-        self.layer.txn.commit()
-        self.layer.switchDbUser('archivepublisher')
+        switch_dbuser('archivepublisher')
         # The real test is that the doCopy doesn't fail.
         [copied_source] = self.doCopy(
             source, target_archive, dsp.derived_series, source.pocket, False)
@@ -1409,8 +1409,7 @@
             self.factory.makeSection())
         getUtility(ISourcePackageFormatSelectionSet).add(
             dsp.derived_series, SourcePackageFormat.FORMAT_1_0)
-        self.layer.txn.commit()
-        self.layer.switchDbUser('archivepublisher')
+        switch_dbuser('archivepublisher')
         [copied_source] = do_copy(
             [source], target_archive, dsp.derived_series, source.pocket,
             check_permissions=False, overrides=[override])
@@ -1724,14 +1723,13 @@
     def do_delayed_copy(self, source):
         """Execute and return the delayed copy."""
 
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
 
         delayed_copy = _do_delayed_copy(
             source, self.copy_archive, self.copy_series, self.copy_pocket,
             True)
 
-        self.layer.txn.commit()
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
         return delayed_copy
 
     def test_do_delayed_copy_simple(self):
@@ -1992,7 +1990,7 @@
         self.binaries_pending_ids = [pub.id for pub in pending_binaries]
 
         # Run test cases in the production context.
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
 
     def getCopier(self, sourcename='mozilla-firefox', sourceversion=None,
                   from_distribution='ubuntu', from_suite='warty',
@@ -3353,14 +3351,11 @@
         test2_tar = test_publisher.addMockFile(
             orig_tarball, filecontent='aaabbbccc')
         test2_source.sourcepackagerelease.addFile(test2_tar)
-        # Commit to ensure librarian files are written.
-        self.layer.txn.commit()
-        # And set test1 source tarball to be expired
-        self.layer.switchDbUser('librarian')
+        # Set test1 source tarball to be expired.
+        switch_dbuser('librarian')
         naked_test1 = removeSecurityProxy(test1_tar)
         naked_test1.content = None
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
 
         checker = CopyChecker(dest_ppa, include_binaries=False)
         self.assertIs(

=== modified file 'lib/lp/soyuz/scripts/tests/test_expire_archive_files.py'
--- lib/lp/soyuz/scripts/tests/test_expire_archive_files.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/scripts/tests/test_expire_archive_files.py	2012-01-20 16:17:27 +0000
@@ -18,6 +18,7 @@
 from lp.soyuz.scripts.expire_archive_files import ArchiveExpirer
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -30,7 +31,7 @@
         """Set up some test publications."""
         super(ArchiveExpiryTestBase, self).setUp()
         # Configure the test publisher.
-        self.layer.switchDbUser("launchpad")
+        switch_dbuser("launchpad")
         self.stp = SoyuzTestPublisher()
         self.stp.prepareBreezyAutotest()
 
@@ -52,8 +53,7 @@
     def runScript(self):
         """Run the expiry script and return."""
         script = self.getScript()
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         script.main()
 
     def _setUpExpirablePublications(self, archive=None):
@@ -203,7 +203,7 @@
         # will remove the test publications we just created.
         self.layer.txn.commit()
         script = self.getScript(['--dry-run'])
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         script.main()
         self.assertSourceNotExpired(source)
         self.assertBinaryNotExpired(binary)
@@ -238,8 +238,7 @@
             archive=self.archive)
         script = self.getScript()
         script.blacklist = [self.archive.owner.name, ]
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         script.main()
         self.assertSourceNotExpired(source)
         self.assertBinaryNotExpired(binary)

=== modified file 'lib/lp/soyuz/scripts/tests/test_publishdistro.py'
--- lib/lp/soyuz/scripts/tests/test_publishdistro.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/scripts/tests/test_publishdistro.py	2012-01-20 16:17:27 +0000
@@ -37,6 +37,7 @@
 from lp.soyuz.scripts.publishdistro import PublishDistro
 from lp.soyuz.tests.test_publishing import TestNativePublishingBase
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.faketransaction import FakeTransaction
 from lp.testing.layers import ZopelessDatabaseLayer
@@ -57,9 +58,9 @@
         publish_distro = PublishDistro(test_args=args)
         publish_distro.logger = BufferLogger()
         publish_distro.txn = self.layer.txn
-        self.layer.switchDbUser(config.archivepublisher.dbuser)
+        switch_dbuser(config.archivepublisher.dbuser)
         publish_distro.main()
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
 
     def runPublishDistroScript(self):
         """Run publish-distro.py, returning the result and output."""

=== modified file 'lib/lp/soyuz/scripts/tests/test_queue.py'
--- lib/lp/soyuz/scripts/tests/test_queue.py	2011-12-30 01:48:17 +0000
+++ lib/lp/soyuz/scripts/tests/test_queue.py	2012-01-20 16:17:27 +0000
@@ -63,6 +63,11 @@
     person_logged_in,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import (
+    dbuser,
+    lp_dbuser,
+    switch_dbuser,
+    )
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
@@ -77,7 +82,7 @@
     def setUp(self):
         # Switch database user and set isolation level to READ COMMIITTED
         # to avoid SERIALIZATION exceptions with the Librarian.
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
 
     def _test_display(self, text):
         """Store output from queue tool for inspection."""
@@ -123,14 +128,13 @@
         insertFakeChangesFileForAllPackageUploads()
         fake_chroot = LibraryFileAlias.get(1)
 
-        LaunchpadZopelessLayer.switchDbUser("testadmin")
+        switch_dbuser("testadmin")
 
         ubuntu = getUtility(IDistributionSet)['ubuntu']
         breezy_autotest = ubuntu.getSeries('breezy-autotest')
         breezy_autotest['i386'].addOrUpdateChroot(fake_chroot)
 
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
 
         TestQueueBase.setUp(self)
 
@@ -141,16 +145,14 @@
     def uploadPackage(self,
             changesfile="suite/bar_1.0-1/bar_1.0-1_source.changes"):
         """Helper function to upload a package."""
-        LaunchpadZopelessLayer.switchDbUser("uploader")
-        sync_policy = getPolicy(
-            name='sync', distro='ubuntu', distroseries='breezy-autotest')
-        bar_src = NascentUpload.from_changesfile_path(
-            datadir(changesfile),
-            sync_policy, DevNullLogger())
-        bar_src.process()
-        bar_src.do_accept()
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with dbuser("uploader"):
+            sync_policy = getPolicy(
+                name='sync', distro='ubuntu', distroseries='breezy-autotest')
+            bar_src = NascentUpload.from_changesfile_path(
+                datadir(changesfile),
+                sync_policy, DevNullLogger())
+            bar_src.process()
+            bar_src.do_accept()
         return bar_src
 
     def testBrokenAction(self):
@@ -279,16 +281,11 @@
 
         # Add a chroot to breezy-autotest/i386, so the system can create
         # builds for it.
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-        a_file = getUtility(ILibraryFileAliasSet)[1]
-        breezy_autotest = getUtility(
-            IDistributionSet)['ubuntu']['breezy-autotest']
-        breezy_autotest['i386'].addOrUpdateChroot(a_file)
-
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            a_file = getUtility(ILibraryFileAliasSet)[1]
+            breezy_autotest = getUtility(
+                IDistributionSet)['ubuntu']['breezy-autotest']
+            breezy_autotest['i386'].addOrUpdateChroot(a_file)
 
         queue_action = self.execute_command(
             'accept bar', no_mail=False)
@@ -308,16 +305,11 @@
 
         # Add a chroot to breezy-autotest/i386, so the system can create
         # builds for it.
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-        a_file = getUtility(ILibraryFileAliasSet)[1]
-        breezy_autotest = getUtility(
-            IDistributionSet)['ubuntu']['breezy-autotest']
-        breezy_autotest['i386'].addOrUpdateChroot(a_file)
-
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            a_file = getUtility(ILibraryFileAliasSet)[1]
+            breezy_autotest = getUtility(
+                IDistributionSet)['ubuntu']['breezy-autotest']
+            breezy_autotest['i386'].addOrUpdateChroot(a_file)
 
         queue_action = self.execute_command(
             'accept bar', no_mail=False)
@@ -353,19 +345,14 @@
         bar_src.queue_root.realiseUpload()
 
         # Now make a new bugtask for the "bar" package.
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-        the_bug_id = 6
-        bugtask_owner = getUtility(IPersonSet).getByName('kinnison')
-        ubuntu = getUtility(IDistributionSet)['ubuntu']
-        ubuntu_bar = ubuntu.getSourcePackage('bar')
-        the_bug = getUtility(IBugSet).get(the_bug_id)
-        bugtask = getUtility(IBugTaskSet).createTask(
-            the_bug, bugtask_owner, ubuntu_bar)
-
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            the_bug_id = 6
+            bugtask_owner = getUtility(IPersonSet).getByName('kinnison')
+            ubuntu = getUtility(IDistributionSet)['ubuntu']
+            ubuntu_bar = ubuntu.getSourcePackage('bar')
+            the_bug = getUtility(IBugSet).get(the_bug_id)
+            bugtask = getUtility(IBugTaskSet).createTask(
+                the_bug, bugtask_owner, ubuntu_bar)
 
         # The bugtask starts life as NEW.
         the_bug = getUtility(IBugSet).get(the_bug_id)
@@ -495,16 +482,12 @@
 
         Further details in bug #59443
         """
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-        # Make breezy-autotest CURRENT in order to accept upload
-        # to BACKPORTS.
-        breezy_autotest = getUtility(
-            IDistributionSet)['ubuntu']['breezy-autotest']
-        breezy_autotest.status = SeriesStatus.CURRENT
-
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            # Make breezy-autotest CURRENT in order to accept upload
+            # to BACKPORTS.
+            breezy_autotest = getUtility(
+                IDistributionSet)['ubuntu']['breezy-autotest']
+            breezy_autotest.status = SeriesStatus.CURRENT
 
         # Store the targeted queue item for future inspection.
         # Ensure it is what we expect.
@@ -543,16 +526,12 @@
 
         Further details in bug #57708
         """
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-        # Make breezy-autotest CURRENT in order to accept upload
-        # to PROPOSED.
-        breezy_autotest = getUtility(
-            IDistributionSet)['ubuntu']['breezy-autotest']
-        breezy_autotest.status = SeriesStatus.CURRENT
-
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            # Make breezy-autotest CURRENT in order to accept upload
+            # to PROPOSED.
+            breezy_autotest = getUtility(
+                IDistributionSet)['ubuntu']['breezy-autotest']
+            breezy_autotest.status = SeriesStatus.CURRENT
 
         # Store the targeted queue item for future inspection.
         # Ensure it is what we expect.
@@ -611,17 +590,13 @@
         Step 4: the remaining duplicated cnews item in UNAPPROVED queue can
         only be rejected.
         """
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-        # Add a chroot to breezy-autotest/i386, so the system can create
-        # builds for it.
-        a_file = getUtility(ILibraryFileAliasSet)[1]
-        breezy_autotest = getUtility(
-            IDistributionSet)['ubuntu']['breezy-autotest']
-        breezy_autotest['i386'].addOrUpdateChroot(a_file)
-
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            # Add a chroot to breezy-autotest/i386, so the system can create
+            # builds for it.
+            a_file = getUtility(ILibraryFileAliasSet)[1]
+            breezy_autotest = getUtility(
+                IDistributionSet)['ubuntu']['breezy-autotest']
+            breezy_autotest['i386'].addOrUpdateChroot(a_file)
 
         # Certify we have a 'cnews' upload duplication in UNAPPROVED.
         self.assertQueueLength(
@@ -794,15 +769,12 @@
         When overriding the component, the archive may change to a
         non-existent one so ensure if fails.
         """
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
-
-        ubuntu = getUtility(IDistributionSet)['ubuntu']
-        proxied_archive = getUtility(IArchiveSet).getByDistroPurpose(
-            ubuntu, ArchivePurpose.PARTNER)
-        comm_archive = removeSecurityProxy(proxied_archive)
-        comm_archive.purpose = ArchivePurpose.PPA
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser(self.dbuser)
+        with lp_dbuser():
+            ubuntu = getUtility(IDistributionSet)['ubuntu']
+            proxied_archive = getUtility(IArchiveSet).getByDistroPurpose(
+                ubuntu, ArchivePurpose.PARTNER)
+            comm_archive = removeSecurityProxy(proxied_archive)
+            comm_archive.purpose = ArchivePurpose.PPA
 
         self.assertRaises(CommandRunnerError,
                           self.execute_command,
@@ -865,7 +837,7 @@
         """
         # Start off by setting up a packageuploadbuild that points to
         # a build with two binaries.
-        LaunchpadZopelessLayer.switchDbUser("launchpad")
+        switch_dbuser("launchpad")
 
         breezy_autotest = getUtility(
             IDistributionSet)['ubuntu']['breezy-autotest']
@@ -878,8 +850,7 @@
 
         # Switching db users starts a new transaction.  We must re-fetch
         # breezy-autotest.
-        LaunchpadZopelessLayer.txn.commit()
-        LaunchpadZopelessLayer.switchDbUser("queued")
+        switch_dbuser("queued")
         breezy_autotest = getUtility(
             IDistributionSet)['ubuntu']['breezy-autotest']
 
@@ -987,8 +958,7 @@
         # the database user change requires a costly commit.
         upload = self.factory.makeCopyJobPackageUpload()
         action = self.makeQueueAction(upload)
-        self.layer.txn.commit()
-        self.layer.switchDbUser(config.uploadqueue.dbuser)
+        switch_dbuser(config.uploadqueue.dbuser)
 
         action.displayItem(upload)
         self.assertNotEqual(0, action.display.call_count)
@@ -1002,8 +972,7 @@
         distroseries = self.factory.makeDistroSeries(
             status=SeriesStatus.CURRENT)
         upload = self.factory.makeCopyJobPackageUpload(distroseries)
-        self.layer.txn.commit()
-        self.layer.switchDbUser(config.uploadqueue.dbuser)
+        switch_dbuser(config.uploadqueue.dbuser)
         upload.acceptFromQueue(DevNullLogger(), dry_run=True)
         # Flush changes to make sure we're not caching any updates that
         # the database won't allow.  If this passes, we've got the
@@ -1054,8 +1023,7 @@
         # it's unnecessary anyway.
         self.patch(action, "displayTitle", FakeMethod)
         action.terms = ["source", str(upload.id)]
-        self.layer.txn.commit()
-        self.layer.switchDbUser(config.uploadqueue.dbuser)
+        switch_dbuser(config.uploadqueue.dbuser)
         action.initialize()
         action.run()
 

=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuildbehavior.py'
--- lib/lp/soyuz/tests/test_binarypackagebuildbehavior.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuildbehavior.py	2012-01-20 16:17:27 +0000
@@ -39,6 +39,7 @@
     )
 from lp.soyuz.enums import ArchivePurpose
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -57,7 +58,7 @@
 
     def setUp(self):
         super(TestBinaryBuildPackageBehavior, self).setUp()
-        self.layer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
     def assertExpectedInteraction(self, ignored, call_log, builder, build,
                                   chroot, archive, archive_purpose,
@@ -280,7 +281,7 @@
 
     def setUp(self):
         super(TestBinaryBuildPackageBehaviorBuildCollection, self).setUp()
-        self.layer.switchDbUser('testadmin')
+        switch_dbuser('testadmin')
 
         self.builder = self.factory.makeBuilder()
         self.build = self.factory.makeBinaryPackageBuild(

=== modified file 'lib/lp/soyuz/tests/test_distroseriesbinarypackage.py'
--- lib/lp/soyuz/tests/test_distroseriesbinarypackage.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/tests/test_distroseriesbinarypackage.py	2012-01-20 16:17:27 +0000
@@ -12,7 +12,6 @@
     Equals,
     NotEquals,
     )
-import transaction
 
 from lp.services.config import config
 from lp.services.log.logger import BufferLogger
@@ -23,6 +22,7 @@
     StormStatementRecorder,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.testing.matchers import HasQueryCount
 
@@ -59,15 +59,14 @@
             archive=distro_archive_2)
 
         logger = BufferLogger()
-        transaction.commit()
-        LaunchpadZopelessLayer.switchDbUser(config.statistician.dbuser)
-        DistroSeriesPackageCache._update(
-            self.distroseries, self.binary_package_name, distro_archive_1,
-            logger)
+        with dbuser(config.statistician.dbuser):
+            DistroSeriesPackageCache._update(
+                self.distroseries, self.binary_package_name, distro_archive_1,
+                logger)
 
-        DistroSeriesPackageCache._update(
-            self.distroseries, self.binary_package_name, distro_archive_2,
-            logger)
+            DistroSeriesPackageCache._update(
+                self.distroseries, self.binary_package_name, distro_archive_2,
+                logger)
 
         self.failUnlessEqual(
             'Foo is the best', self.distroseries_binary_package.summary)

=== modified file 'lib/lp/soyuz/tests/test_distroseriesdifferencejob.py'
--- lib/lp/soyuz/tests/test_distroseriesdifferencejob.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/tests/test_distroseriesdifferencejob.py	2012-01-20 16:17:27 +0000
@@ -42,6 +42,7 @@
     may_require_job,
     )
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadZopelessLayer,
     ZopelessDatabaseLayer,
@@ -679,14 +680,12 @@
             source_package_name)
 
     def runJob(self, job):
-        transaction.commit()
-        self.layer.switchDbUser('distroseriesdifferencejob')
+        switch_dbuser('distroseriesdifferencejob')
         dsdjob = DistroSeriesDifferenceJob(job)
         dsdjob.start()
         dsdjob.run()
         dsdjob.complete()
-        transaction.commit()
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
 
     def test_parent_gets_newer(self):
         # When a new source package is uploaded to the parent distroseries,
@@ -940,9 +939,8 @@
         packages = dict(
             (user, self.factory.makeSourcePackageName())
             for user in script_users)
-        transaction.commit()
         for user in script_users:
-            self.layer.switchDbUser(user)
+            switch_dbuser(user)
             try:
                 create_job(derived, packages[user], parent)
             except ProgrammingError, e:
@@ -958,9 +956,8 @@
         # Check that DB users can query derived series.
         script_users = ['queued']
         dsp = self.factory.makeDistroSeriesParent()
-        transaction.commit()
         for user in script_users:
-            self.layer.switchDbUser(user)
+            switch_dbuser(user)
             list(dsp.parent_series.getDerivedSeries())
 
     def test_passesPackagesetFilter(self):
@@ -973,9 +970,8 @@
         dsdj = create_job(
             dsp.derived_series, spph.sourcepackagerelease.sourcepackagename,
             dsp.parent_series)
-        transaction.commit()
 
-        self.layer.switchDbUser('distroseriesdifferencejob')
+        switch_dbuser('distroseriesdifferencejob')
 
         dsdj.passesPackagesetFilter()
 

=== modified file 'lib/lp/soyuz/tests/test_doc.py'
--- lib/lp/soyuz/tests/test_doc.py	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/tests/test_doc.py	2012-01-20 16:17:27 +0000
@@ -12,6 +12,7 @@
 from lp.services.config import config
 from lp.services.database.sqlbase import commit
 from lp.testing import logout
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -53,21 +54,21 @@
 def uploaderSetUp(test):
     """setup the package uploader script tests."""
     setUp(test)
-    LaunchpadZopelessLayer.switchDbUser('uploader')
+    switch_dbuser('uploader')
 
 
 def builddmasterSetUp(test):
     """Setup the connection for the build master tests."""
     test_dbuser = config.builddmaster.dbuser
     test.globs['test_dbuser'] = test_dbuser
-    LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    switch_dbuser(test_dbuser)
     setGlobs(test)
 
 
 def statisticianSetUp(test):
     test_dbuser = config.statistician.dbuser
     test.globs['test_dbuser'] = test_dbuser
-    LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    switch_dbuser(test_dbuser)
     setUp(test)
 
 
@@ -96,7 +97,7 @@
 def uploadQueueSetUp(test):
     lobotomize_stevea()
     test_dbuser = config.uploadqueue.dbuser
-    LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    switch_dbuser(test_dbuser)
     setUp(test)
     test.globs['test_dbuser'] = test_dbuser
 
@@ -110,7 +111,7 @@
     """
     lobotomize_stevea()
     test_dbuser = config.uploader.dbuser
-    LaunchpadZopelessLayer.switchDbUser(test_dbuser)
+    switch_dbuser(test_dbuser)
     setUp(test)
     test.globs['test_dbuser'] = test_dbuser
 
@@ -126,7 +127,7 @@
 def manageChrootSetup(test):
     """Set up the manage-chroot.txt test."""
     setUp(test)
-    LaunchpadZopelessLayer.switchDbUser("fiera")
+    switch_dbuser("fiera")
 
 
 special = {

=== modified file 'lib/lp/soyuz/tests/test_initializedistroseriesjob.py'
--- lib/lp/soyuz/tests/test_initializedistroseriesjob.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/tests/test_initializedistroseriesjob.py	2012-01-20 16:17:27 +0000
@@ -27,6 +27,7 @@
 from lp.soyuz.scripts.initialize_distroseries import InitializationError
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadZopelessLayer,
@@ -276,7 +277,7 @@
     def test_job(self):
         parent, child = self._create_child()
         job = self.job_source.create(child, [parent.id])
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
 
         job.run()
         child.updatePackageCount()
@@ -289,7 +290,7 @@
         job = self.job_source.create(
             child, [parent.id], packagesets=(self.test1_packageset_id,),
             arches=(arch,), rebuild=True)
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
 
         job.run()
         child.updatePackageCount()
@@ -306,7 +307,7 @@
             child, [parent.id], archindep_archtag=None, packagesets=None,
             arches=None, overlays=None, overlay_pockets=None,
             overlay_components=None, rebuild=True)
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
         job.run()
         child.updatePackageCount()
 
@@ -318,7 +319,7 @@
             child, [parent.id], archindep_archtag=None, packagesets=None,
             arches=None, overlays=None, overlay_pockets=None,
             overlay_components=None, rebuild=True)
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
         job.run()
 
         self.assertEqual(
@@ -333,7 +334,7 @@
             child, [parent.id], archindep_archtag='amd64', packagesets=None,
             arches=None, overlays=None, overlay_pockets=None,
             overlay_components=None, rebuild=True)
-        self.layer.switchDbUser('initializedistroseries')
+        switch_dbuser('initializedistroseries')
         job.run()
 
         self.assertEqual(

=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
--- lib/lp/soyuz/tests/test_packagecopyjob.py	2012-01-06 15:59:20 +0000
+++ lib/lp/soyuz/tests/test_packagecopyjob.py	2012-01-20 16:17:27 +0000
@@ -60,6 +60,7 @@
     run_script,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import (
     LaunchpadFunctionalLayer,
@@ -115,8 +116,7 @@
     def runJob(self, job):
         """Helper to switch to the right DB user and run the job."""
         # We are basically mimicking the job runner here.
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         # Set the state to RUNNING.
         job.start()
         # Commit the RUNNING state.
@@ -395,10 +395,7 @@
         self.assertEqual("libc", job.package_name)
         self.assertEqual("2.8-1", job.package_version)
 
-        # Make sure everything hits the database, switching db users
-        # aborts.
-        transaction.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         job.run()
 
         published_sources = target_archive.getPublishedSources(
@@ -413,7 +410,7 @@
 
         # Switch back to a db user that has permission to clean up
         # featureflag.
-        self.layer.switchDbUser('launchpad_main')
+        switch_dbuser('launchpad_main')
 
     def test_getOopsVars(self):
         distroseries = self.factory.makeDistroSeries()
@@ -731,8 +728,7 @@
         # Simulate the job runner suspending after getting a
         # SuspendJobException
         job.suspend()
-        self.layer.txn.commit()
-        self.layer.switchDbUser("launchpad_main")
+        switch_dbuser("launchpad_main")
 
         # Add some overrides to the job.
         package = source_package.sourcepackagerelease.sourcepackagename
@@ -961,8 +957,7 @@
         # Simulate the job runner suspending after getting a
         # SuspendJobException
         job.suspend()
-        self.layer.txn.commit()
-        self.layer.switchDbUser("launchpad_main")
+        switch_dbuser("launchpad_main")
 
         # Accept the upload to release the job then run it.
         pu = getUtility(IPackageUploadSet).getByPackageCopyJobIDs(
@@ -1113,8 +1108,7 @@
         component = self.factory.makeComponent()
         section = self.factory.makeSection()
         pcj = self.factory.makePlainPackageCopyJob()
-        self.layer.txn.commit()
-        self.layer.switchDbUser('sync_packages')
+        switch_dbuser('sync_packages')
 
         override = SourceOverride(
             source_package_name=name,
@@ -1173,8 +1167,7 @@
         section = self.factory.makeSection()
         pcj = self.factory.makePlainPackageCopyJob(
             package_name=name.name, package_version="1.0")
-        self.layer.txn.commit()
-        self.layer.switchDbUser('sync_packages')
+        switch_dbuser('sync_packages')
 
         override = SourceOverride(
             source_package_name=name,
@@ -1222,8 +1215,7 @@
         # Simulate the job runner suspending after getting a
         # SuspendJobException
         job.suspend()
-        self.layer.txn.commit()
-        self.layer.switchDbUser("launchpad_main")
+        switch_dbuser("launchpad_main")
 
         # Patch the job's attemptCopy() method so it just raises an
         # exception.
@@ -1293,14 +1285,12 @@
 
     def test_findMatchingDSDs(self):
         job = self.makeJob()
-        transaction.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         removeSecurityProxy(job).findMatchingDSDs()
 
     def test_reportFailure(self):
         job = self.makeJob()
-        transaction.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         removeSecurityProxy(job).reportFailure(CannotCopy("Mommy it hurts"))
 
 

=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
--- lib/lp/soyuz/tests/test_packageupload.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/tests/test_packageupload.py	2012-01-20 16:17:27 +0000
@@ -37,6 +37,7 @@
 from lp.soyuz.interfaces.section import ISectionSet
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.testing.matchers import Provides
 
@@ -220,8 +221,7 @@
         # Make sure no announcement email was sent at this point.
         self.assertEquals(len(stub.test_emails), 0)
 
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
 
         logger = BufferLogger()
         # realiseUpload() assumes a umask of 022, which is normally true in
@@ -262,7 +262,7 @@
             'http://launchpad.dev/ubuntutest/breezy-autotest/+source/'
             'foocomm/1.0-2\n')
 
-        self.layer.switchDbUser('launchpad')
+        switch_dbuser('launchpad')
 
         # One source and 2 binaries are pending publication. They all were
         # overridden to multiverse and had their files moved to the public

=== modified file 'lib/lp/soyuz/tests/test_processaccepted.py'
--- lib/lp/soyuz/tests/test_processaccepted.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/tests/test_processaccepted.py	2012-01-20 16:17:27 +0000
@@ -27,6 +27,7 @@
     )
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 
 
@@ -81,8 +82,7 @@
             distribution=self.distro)
         self.createWaitingAcceptancePackage(distroseries=other_distroseries)
         script = self.getScript([])
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         script.main()
 
         # The other source should be published now.
@@ -119,8 +119,7 @@
 
         # Accept the packages.
         script = self.getScript(['--copy-archives'])
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         script.main()
 
         # Packages in main archive should not be accepted and published.
@@ -164,8 +163,7 @@
                     done_count)
 
         script = self.getScript([])
-        self.layer.txn.commit()
-        self.layer.switchDbUser(self.dbuser)
+        switch_dbuser(self.dbuser)
         synch = UploadCheckingSynchronizer()
         transaction.manager.registerSynch(synch)
         script.main()

=== modified file 'lib/lp/soyuz/tests/test_publishing.py'
--- lib/lp/soyuz/tests/test_publishing.py	2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/tests/test_publishing.py	2012-01-20 16:17:27 +0000
@@ -62,7 +62,10 @@
     StormStatementRecorder,
     TestCaseWithFactory,
     )
-from lp.testing.dbuser import dbuser
+from lp.testing.dbuser import (
+    dbuser,
+    switch_dbuser,
+    )
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.layers import (
     DatabaseFunctionalLayer,
@@ -574,7 +577,7 @@
     def setUp(self):
         """Setup a pool dir, the librarian, and instantiate the DiskPool."""
         super(TestNativePublishingBase, self).setUp()
-        self.layer.switchDbUser(config.archivepublisher.dbuser)
+        switch_dbuser(config.archivepublisher.dbuser)
         self.prepareBreezyAutotest()
         self.config = getPubConfig(self.ubuntutest.main_archive)
         self.config.setupArchiveDirs()

=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py	2012-01-15 13:32:27 +0000
+++ lib/lp/testing/__init__.py	2012-01-20 16:17:27 +0000
@@ -175,6 +175,7 @@
     launchpadlib_for,
     oauth_access_token_for,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.fixture import CaptureOops
 from lp.testing.karma import KarmaRecorder
 
@@ -421,8 +422,7 @@
         user, or you'll hit privilege violations later on.
         """
         assert self.layer, "becomeDbUser requires a layer."
-        transaction.commit()
-        self.layer.switchDbUser(dbuser)
+        switch_dbuser(dbuser)
 
     def __str__(self):
         """The string representation of a test is its id.

=== modified file 'lib/lp/testing/layers.py'
--- lib/lp/testing/layers.py	2011-12-30 08:13:14 +0000
+++ lib/lp/testing/layers.py	2012-01-20 16:17:27 +0000
@@ -149,7 +149,6 @@
     logout,
     reset_logging,
     )
-from lp.testing.dbuser import switch_dbuser
 from lp.testing.pgsql import PgTestSetup
 from lp.testing.smtpd import SMTPController
 
@@ -1553,12 +1552,6 @@
     def abort(cls):
         transaction.abort()
 
-    @classmethod
-    @profiled
-    def switchDbUser(cls, dbuser):
-        # DEPRECATED: use switch_dbuser directly.
-        switch_dbuser(dbuser)
-
 
 class ExperimentalLaunchpadZopelessLayer(LaunchpadZopelessLayer):
     """LaunchpadZopelessLayer using the mock database."""

=== modified file 'lib/lp/testing/tests/test_dbuser.py'
--- lib/lp/testing/tests/test_dbuser.py	2012-01-01 02:58:52 +0000
+++ lib/lp/testing/tests/test_dbuser.py	2012-01-20 16:17:27 +0000
@@ -13,6 +13,7 @@
 from lp.testing.dbuser import (
     dbuser,
     lp_dbuser,
+    switch_dbuser,
     )
 from lp.testing.layers import LaunchpadZopelessLayer
 
@@ -29,7 +30,7 @@
         return result
 
     def test_dbuser(self):
-        LaunchpadZopelessLayer.switchDbUser(config.uploader.dbuser)
+        switch_dbuser(config.uploader.dbuser)
         self.assertEqual(config.uploader.dbuser, self.get_current_dbuser())
         with dbuser(config.archivepublisher.dbuser):
             self.assertEqual(config.archivepublisher.dbuser,
@@ -37,9 +38,8 @@
         self.assertEqual(config.uploader.dbuser, self.get_current_dbuser())
 
     def test_lp_dpuser(self):
-        LaunchpadZopelessLayer.switchDbUser(config.uploader.dbuser)
+        switch_dbuser(config.uploader.dbuser)
         self.assertEqual(config.uploader.dbuser, self.get_current_dbuser())
         with lp_dbuser():
             self.assertEqual('launchpad', self.get_current_dbuser())
         self.assertEqual(config.uploader.dbuser, self.get_current_dbuser())
-

=== modified file 'lib/lp/translations/doc/translationmessage-destroy.txt'
--- lib/lp/translations/doc/translationmessage-destroy.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/translations/doc/translationmessage-destroy.txt	2012-01-20 16:17:27 +0000
@@ -11,8 +11,8 @@
 
     >>> from lp.translations.model.translationmessage import (
     ...     TranslationMessage)
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> LaunchpadZopelessLayer.switchDbUser('rosettaadmin')
+    >>> from lp.testing.dbuser import switch_dbuser
+    >>> switch_dbuser('rosettaadmin')
 
 Select an existing ITranslationMessage and try to remove it.
 
@@ -35,7 +35,7 @@
 
     >>> # We need to be able to create persons and projects so let's just use
     >>> # a global 'postgres' permission which allows everything.
-    >>> LaunchpadZopelessLayer.switchDbUser('postgres')
+    >>> switch_dbuser('postgres')
     >>> from lp.services.database.sqlbase import sqlvalues
     >>> from lp.app.enums import ServiceUsage
     >>> from lp.testing.factory import LaunchpadObjectFactory

=== modified file 'lib/lp/translations/doc/translationsoverview.txt'
--- lib/lp/translations/doc/translationsoverview.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/translations/doc/translationsoverview.txt	2012-01-20 16:17:27 +0000
@@ -21,7 +21,8 @@
     >>> from lp.registry.model.sourcepackagename import SourcePackageName
     >>> from lp.translations.interfaces.translationsoverview import (
     ...     ITranslationsOverview)
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
+    >>> from lp.testing.dbuser import switch_dbuser
+
     >>> karmacachemanager = getUtility(IKarmaCacheManager)
     >>> person_set = getUtility(IPersonSet)
     >>> product_set = getUtility(IProductSet)
@@ -30,13 +31,11 @@
 
     >>> def start_karma_update():
     ...     """Prepare for update to karma cache."""
-    ...     transaction.commit()
-    ...     LaunchpadZopelessLayer.switchDbUser('testadmin')
+    ...     switch_dbuser('testadmin')
 
     >>> def finish_karma_update():
     ...     """Return to normal after updating karma cache."""
-    ...     transaction.commit()
-    ...     LaunchpadZopelessLayer.switchDbUser('launchpad')
+    ...     switch_dbuser('launchpad')
 
 ITranslationOverview defines two constants regulating minimum and maximum
 contribution weights.

=== modified file 'lib/lp/translations/scripts/tests/test_migrate_current_flag.py'
--- lib/lp/translations/scripts/tests/test_migrate_current_flag.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/scripts/tests/test_migrate_current_flag.py	2012-01-20 16:17:27 +0000
@@ -6,6 +6,7 @@
 import logging
 
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.scripts.migrate_current_flag import (
     MigrateCurrentFlagProcess,
@@ -21,7 +22,7 @@
         # This test needs the privileges of rosettaadmin (to update
         # TranslationMessages) but it also needs to set up test conditions
         # which requires other privileges.
-        self.layer.switchDbUser('postgres')
+        switch_dbuser('postgres')
         super(TestMigrateCurrentFlag, self).setUp(user='mark@xxxxxxxxxxx')
         self.migrate_process = MigrateCurrentFlagProcess(self.layer.txn)
 
@@ -65,8 +66,7 @@
             potemplate=potemplate,
             sequence=1)
         pofile = self.factory.makePOFile(potemplate=potemplate)
-        translation = self.factory.makeSuggestion(
-            pofile=pofile, potmsgset=potmsgset)
+        self.factory.makeSuggestion(pofile=pofile, potmsgset=potmsgset)
         results = list(
             self.migrate_process.getCurrentNonimportedTranslations(
                 potemplate.productseries.product))
@@ -113,7 +113,7 @@
         # This test needs the privileges of rosettaadmin (to update
         # TranslationMessages) but it also needs to set up test conditions
         # which requires other privileges.
-        self.layer.switchDbUser('postgres')
+        switch_dbuser('postgres')
         super(TestUpdaterLoop, self).setUp(user='mark@xxxxxxxxxxx')
         self.logger = logging.getLogger("migrate-current-flag")
         self.migrate_loop = TranslationMessageImportedFlagUpdater(

=== modified file 'lib/lp/translations/scripts/tests/test_remove_translations.py'
--- lib/lp/translations/scripts/tests/test_remove_translations.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/scripts/tests/test_remove_translations.py	2012-01-20 16:17:27 +0000
@@ -26,6 +26,7 @@
     TestCase,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.interfaces.translationmessage import (
@@ -58,7 +59,7 @@
         super(TestRemoveTranslationsConstraints, self).setUp()
         # Acquire privileges to delete TranslationMessages.  We won't
         # actually do that here, but we'll go through all the motions.
-        self.layer.switchDbUser('postgres')
+        switch_dbuser('postgres')
 
     def _check_options(self, opts):
         """Get `_check_constraints_safety`'s answer for given options."""
@@ -247,7 +248,7 @@
         # remove-translations-by.txt tests a realistic run of the
         # remove-translations-by.py script under the actual rosettaadmin
         # db user.
-        self.layer.switchDbUser('postgres')
+        switch_dbuser('postgres')
 
         # Set up a template with Dutch and German translations.  The
         # messages we set up here are invariant; they remain untouched
@@ -586,7 +587,7 @@
 
     def setUp(self):
         super(TestRemoveTranslationsUnmasking, self).setUp()
-        self.layer.switchDbUser('postgres')
+        switch_dbuser('postgres')
 
         # Set up a template with a Laotian translation file.  There's
         # one message to be translated.

=== modified file 'lib/lp/translations/scripts/tests/test_verify_pofile_stats.py'
--- lib/lp/translations/scripts/tests/test_verify_pofile_stats.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/scripts/tests/test_verify_pofile_stats.py	2012-01-20 16:17:27 +0000
@@ -5,9 +5,8 @@
 
 __metaclass__ = type
 
-import transaction
-
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.interfaces.side import TranslationSide
 
@@ -25,8 +24,6 @@
         sides = [TranslationSide.UPSTREAM, TranslationSide.UBUNTU]
         pofiles = [
             self._makeNonemptyPOFile(side) for side in sides]
-        transaction.commit()
-        self.layer.switchDbUser(dbuser='pofilestats')
-
-        for pofile in pofiles:
-            pofile.updateStatistics()
+        with dbuser('pofilestats'):
+            for pofile in pofiles:
+                pofile.updateStatistics()

=== modified file 'lib/lp/translations/tests/test_translationbranchapprover.py'
--- lib/lp/translations/tests/test_translationbranchapprover.py	2011-12-30 01:48:17 +0000
+++ lib/lp/translations/tests/test_translationbranchapprover.py	2012-01-20 16:17:27 +0000
@@ -5,13 +5,13 @@
 
 __metaclass__ = type
 
-import transaction
 from zope.component import getUtility
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.validators.name import valid_name
 from lp.services.librarianserver.testing.fake import FakeLibrarian
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import (
     LaunchpadZopelessLayer,
     ZopelessDatabaseLayer,
@@ -295,8 +295,7 @@
         This is the role that the TranslationsBranchApprover is actually
         run under.
         """
-        transaction.commit()
-        self.layer.switchDbUser('translationsbranchscanner')
+        switch_dbuser('translationsbranchscanner')
 
     def test_approve_new_product_template(self):
         # The approver has sufficient privileges to create a new

=== modified file 'lib/lp/translations/tests/test_translationbuildapprover.py'
--- lib/lp/translations/tests/test_translationbuildapprover.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/tests/test_translationbuildapprover.py	2012-01-20 16:17:27 +0000
@@ -5,11 +5,11 @@
 
 __metaclass__ = type
 
-import transaction
 from zope.component import getUtility
 
 from lp.services.config import config
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.enums import RosettaImportStatus
 from lp.translations.interfaces.translationimportqueue import (
@@ -37,8 +37,7 @@
 
     def _becomeBuilddMaster(self):
         """Switch db identity to the script that uses this approver."""
-        transaction.commit()
-        self.layer.switchDbUser(config.builddmaster.dbuser)
+        switch_dbuser(config.builddmaster.dbuser)
 
     def test_approve_all_new(self):
         # A happy approval case, all new templates.

=== modified file 'lib/lp/translations/tests/test_translationimportqueue.py'
--- lib/lp/translations/tests/test_translationimportqueue.py	2011-12-30 01:48:17 +0000
+++ lib/lp/translations/tests/test_translationimportqueue.py	2012-01-20 16:17:27 +0000
@@ -22,6 +22,7 @@
     person_logged_in,
     TestCaseWithFactory,
     )
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import (
@@ -62,8 +63,7 @@
 
     def _switch_dbuser(self):
         if self.dbuser != None:
-            transaction.commit()
-            self.layer.switchDbUser(self.dbuser)
+            switch_dbuser(self.dbuser)
 
     def _assertCanSetStatus(self, user, entry, expected_list):
         # Helper to check for all statuses.

=== modified file 'lib/lp/translations/tests/test_translationtemplatesbuild.py'
--- lib/lp/translations/tests/test_translationtemplatesbuild.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/tests/test_translationtemplatesbuild.py	2012-01-20 16:17:27 +0000
@@ -6,7 +6,6 @@
 __metaclass__ = type
 
 from storm.store import Store
-import transaction
 from zope.component import getUtility
 from zope.interface.verify import verifyObject
 
@@ -17,6 +16,7 @@
     )
 from lp.services.config import config
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.interfaces.translationtemplatesbuild import (
     ITranslationTemplatesBuild,
@@ -55,8 +55,7 @@
         # The branch scanner creates TranslationTemplatesBuilds.  It has
         # the database privileges it needs for that.
         branch = self.factory.makeBranch()
-        transaction.commit()
-        self.layer.switchDbUser(config.branchscanner.dbuser)
+        switch_dbuser(config.branchscanner.dbuser)
         utility = getUtility(ITranslationTemplatesBuildSource)
         build_farm_job = self._makeBuildFarmJob()
         utility.create(build_farm_job, branch)

=== modified file 'lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py'
--- lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py	2012-01-11 08:52:22 +0000
+++ lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py	2012-01-20 16:17:27 +0000
@@ -9,7 +9,6 @@
 
 import pytz
 from testtools.deferredruntest import AsynchronousDeferredRunTest
-import transaction
 from twisted.internet import defer
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
@@ -28,6 +27,7 @@
 from lp.services.librarian.interfaces import ILibraryFileAliasSet
 from lp.services.librarian.utils import copy_and_close
 from lp.testing import TestCaseWithFactory
+from lp.testing.dbuser import switch_dbuser
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.enums import RosettaImportStatus
@@ -106,11 +106,6 @@
         super(TestTranslationTemplatesBuildBehavior, self).setUp()
         self.slave_helper = self.useFixture(SlaveTestHelpers())
 
-    def _becomeBuilddMaster(self):
-        """Log into the database as the buildd master."""
-        transaction.commit()
-        self.layer.switchDbUser(config.builddmaster.dbuser)
-
     def _getBuildQueueItem(self, behavior):
         """Get `BuildQueue` for an `IBuildFarmJobBehavior`."""
         job = removeSecurityProxy(behavior.buildfarmjob.job)
@@ -123,7 +118,7 @@
         behavior = self.makeBehavior()
         buildqueue_item = self._getBuildQueueItem(behavior)
 
-        self._becomeBuilddMaster()
+        switch_dbuser(config.builddmaster.dbuser)
         d = behavior.dispatchBuildToSlave(buildqueue_item, logging)
 
         def got_dispatch((status, info)):


Follow ups