← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/snapshot-modifying-helper-use into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/snapshot-modifying-helper-use into lp:launchpad.

Commit message:
Finish conversion to notify_modified.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/snapshot-modifying-helper-use/+merge/362262
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/snapshot-modifying-helper-use into lp:launchpad.
=== modified file 'lib/lp/blueprints/browser/specification.py'
--- lib/lp/blueprints/browser/specification.py	2018-07-15 16:23:15 +0000
+++ lib/lp/blueprints/browser/specification.py	2019-01-25 18:33:51 +0000
@@ -45,8 +45,6 @@
     Popen,
     )
 
-from lazr.lifecycle.event import ObjectModifiedEvent
-from lazr.lifecycle.snapshot import Snapshot
 from lazr.restful.interface import (
     copy_field,
     use_template,
@@ -59,7 +57,6 @@
 from zope import component
 from zope.component import getUtility
 from zope.error.interfaces import IErrorReportingUtility
-from zope.event import notify
 from zope.formlib import form
 from zope.formlib.form import Fields
 from zope.formlib.widget import CustomWidgetFactory
@@ -70,7 +67,6 @@
 from zope.interface import (
     implementer,
     Interface,
-    providedBy,
     )
 from zope.schema import (
     Bool,
@@ -141,6 +137,7 @@
     Link,
     NavigationMenu,
     )
+from lp.services.webapp.snapshot import notify_modified
 
 
 class INewSpecification(Interface):
@@ -826,10 +823,8 @@
 
     @action(_('Change'), name='change')
     def change_action(self, action, data):
-        old_spec = Snapshot(self.context, providing=providedBy(self.context))
-        self.context.setWorkItems(data['workitems_text'])
-        notify(ObjectModifiedEvent(
-            self.context, old_spec, edited_fields=['workitems_text']))
+        with notify_modified(self.context, ['workitems_text']):
+            self.context.setWorkItems(data['workitems_text'])
         self.next_url = canonical_url(self.context)
 
 

=== modified file 'lib/lp/blueprints/model/specification.py'
--- lib/lp/blueprints/model/specification.py	2016-09-19 08:35:06 +0000
+++ lib/lp/blueprints/model/specification.py	2019-01-25 18:33:51 +0000
@@ -13,10 +13,7 @@
 
 import operator
 
-from lazr.lifecycle.event import (
-    ObjectCreatedEvent,
-    ObjectModifiedEvent,
-    )
+from lazr.lifecycle.event import ObjectCreatedEvent
 from lazr.lifecycle.objectdelta import ObjectDelta
 from sqlobject import (
     BoolCol,
@@ -109,6 +106,7 @@
     get_property_cache,
     )
 from lp.services.webapp.interfaces import ILaunchBag
+from lp.services.webapp.snapshot import notify_modified
 from lp.services.xref.interfaces import IXRefSet
 
 
@@ -736,13 +734,8 @@
                 # 'essential' changes, there's no need to create a new
                 # subscription, but we modify the existing subscription
                 # and notify the user about the change.
-                sub.essential = essential
-                # The second argument should really be a copy of sub with
-                # only the essential attribute changed, but we know
-                # that we can get away with not examining the attribute
-                # at all - it's a boolean!
-                notify(ObjectModifiedEvent(
-                        sub, sub, ['essential'], user=subscribed_by))
+                with notify_modified(sub, ['essential'], user=subscribed_by):
+                    sub.essential = essential
             return sub
         # since no previous subscription existed, create and return a new one
         sub = SpecificationSubscription(specification=self,

=== modified file 'lib/lp/blueprints/model/tests/test_specification.py'
--- lib/lp/blueprints/model/tests/test_specification.py	2018-01-02 16:10:26 +0000
+++ lib/lp/blueprints/model/tests/test_specification.py	2019-01-25 18:33:51 +0000
@@ -1,4 +1,4 @@
-# Copyright 2011-2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2011-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Unit tests for blueprints here."""
@@ -7,8 +7,6 @@
 
 __metaclass__ = type
 
-from lazr.lifecycle.event import ObjectModifiedEvent
-from lazr.lifecycle.snapshot import Snapshot
 from testtools.matchers import (
     Equals,
     MatchesStructure,
@@ -16,8 +14,6 @@
 from testtools.testcase import ExpectedException
 import transaction
 from zope.component import getUtility
-from zope.event import notify
-from zope.interface import providedBy
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
@@ -36,9 +32,10 @@
     )
 from lp.registry.errors import CannotChangeInformationType
 from lp.registry.model.milestone import Milestone
+from lp.services.mail import stub
 from lp.services.propertycache import get_property_cache
-from lp.services.mail import stub
 from lp.services.webapp import canonical_url
+from lp.services.webapp.snapshot import notify_modified
 from lp.testing import (
     ANONYMOUS,
     login,
@@ -225,21 +222,18 @@
         specification."""
         stub.test_emails = []
         spec = self.factory.makeSpecification()
-        old_spec = Snapshot(spec, providing=providedBy(spec))
-        new_work_item = {
-            'title': 'A work item',
-            'status': SpecificationWorkItemStatus.TODO,
-            'assignee': None,
-            'milestone': None,
-            'sequence': 0
-        }
-
-        login_person(spec.owner)
-        spec.updateWorkItems([new_work_item])
-        # For API requests, lazr.restful does the notify() call, for this test
-        # we need to call ourselves.
-        notify(ObjectModifiedEvent(
-            spec, old_spec, edited_fields=['workitems_text']))
+        # For API requests, lazr.restful does the notification; for this
+        # test we need to call ourselves.
+        with notify_modified(spec, ['workitems_text']):
+            new_work_item = {
+                'title': 'A work item',
+                'status': SpecificationWorkItemStatus.TODO,
+                'assignee': None,
+                'milestone': None,
+                'sequence': 0
+            }
+            login_person(spec.owner)
+            spec.updateWorkItems([new_work_item])
         transaction.commit()
 
         self.assertEqual(1, len(stub.test_emails))
@@ -256,13 +250,11 @@
         stub.test_emails = []
         wi = self.factory.makeSpecificationWorkItem()
         spec = wi.specification
-        old_spec = Snapshot(spec, providing=providedBy(spec))
-        login_person(spec.owner)
-        spec.updateWorkItems([])
         # In production this notification is fired by lazr.restful, but we
         # need to do it ourselves in this test.
-        notify(ObjectModifiedEvent(
-            spec, old_spec, edited_fields=['workitems_text']))
+        with notify_modified(spec, ['workitems_text']):
+            login_person(spec.owner)
+            spec.updateWorkItems([])
         transaction.commit()
 
         self.assertEqual(1, len(stub.test_emails))
@@ -294,14 +286,11 @@
         }
         login_person(spec.owner)
         spec.updateWorkItems([original_work_item])
-        old_spec = Snapshot(spec, providing=providedBy(spec))
-
-        stub.test_emails = []
-        spec.updateWorkItems([new_work_item])
         # In production this notification is fired by lazr.restful, but we
         # need to do it ourselves in this test.
-        notify(ObjectModifiedEvent(
-            spec, old_spec, edited_fields=['workitems_text']))
+        with notify_modified(spec, ['workitems_text']):
+            stub.test_emails = []
+            spec.updateWorkItems([new_work_item])
         transaction.commit()
 
         self.assertEqual(1, len(stub.test_emails))

=== modified file 'lib/lp/code/doc/branch-notifications.txt'
--- lib/lp/code/doc/branch-notifications.txt	2018-05-13 09:59:59 +0000
+++ lib/lp/code/doc/branch-notifications.txt	2019-01-25 18:33:51 +0000
@@ -97,7 +97,6 @@
   * Send the entire diff
 
     >>> from lp.registry.interfaces.person import IPersonSet
-    >>> from lp.code.interfaces.branch import IBranch
     >>> personset = getUtility(IPersonSet)
 
     >>> def subscribe_user_by_email(branch, email, level, size, level2):
@@ -420,13 +419,11 @@
     ...     BranchSubscriptionDiffSize.NODIFF,
     ...     CodeReviewNotificationLevel.NOEMAIL)
 
-    >>> from zope.event import notify
-    >>> from lazr.lifecycle.event import ObjectModifiedEvent
-    >>> from lazr.lifecycle.snapshot import Snapshot
     >>> from lp.code.interfaces.branchjob import IBranchModifiedMailJobSource
     >>> from lp.services.config import config
     >>> from lp.services.job.runner import JobRunner
     >>> from lp.services.log.logger import DevNullLogger
+    >>> from lp.services.webapp.snapshot import notify_modified
     >>> from lp.testing.dbuser import dbuser
 
     >>> def run_modified_mail_jobs():
@@ -436,16 +433,12 @@
     ...         JobRunner.fromReady(job_source, logger=logger).runAll()
 
     >>> login('test@xxxxxxxxxxxxx')
-    >>> before_modification = Snapshot(branch, providing=IBranch)
-
-    >>> branch.whiteboard = 'This is the new whiteboard'
 
 Even though the branch notification emails don't use the field
-names just now, we'll pass them through anyway
+names just now, we'll pass them through anyway.
 
-    >>> notify(ObjectModifiedEvent(branch,
-    ...                               before_modification,
-    ...                               ['whiteboard']))
+    >>> with notify_modified(branch, ['whiteboard']):
+    ...     branch.whiteboard = 'This is the new whiteboard'
     >>> run_modified_mail_jobs()
 
     >>> notifications = pop_notifications()
@@ -483,19 +476,20 @@
     >>> branch = getUtility(IBranchLookup).getByUniqueName(
     ...     '~name12/firefox/main')
 
-    >>> before_modification = Snapshot(branch, providing=IBranch)
-
-    >>> branch.name = 'new-name'
-    >>> branch.url = 'http://example.com/foo'
-    >>> branch.whiteboard = 'This is a multiline whiteboard\n' \
-    ...  'with a really long line that should invoke the splitting ' \
-    ...  'algorithm in the mail wrapper to make sure that the line ' \
-    ...  'is not too long'
     >>> from lp.code.enums import BranchLifecycleStatus
-    >>> branch.lifecycle_status = BranchLifecycleStatus.EXPERIMENTAL
-    >>> updated_fields = ['name','title','summary','url','whiteboard','lifecycle_status']
-    >>> notify(ObjectModifiedEvent(
-    ...     branch, before_modification, updated_fields))
+    >>> updated_fields = [
+    ...     'name', 'title', 'summary', 'url', 'whiteboard',
+    ...     'lifecycle_status',
+    ...     ]
+    >>> with notify_modified(branch, updated_fields):
+    ...     branch.name = 'new-name'
+    ...     branch.url = 'http://example.com/foo'
+    ...     branch.whiteboard = (
+    ...         'This is a multiline whiteboard\n'
+    ...         'with a really long line that should invoke the splitting '
+    ...         'algorithm in the mail wrapper to make sure that the line '
+    ...         'is not too long')
+    ...     branch.lifecycle_status = BranchLifecycleStatus.EXPERIMENTAL
     >>> run_modified_mail_jobs()
 
     >>> notifications = pop_notifications()
@@ -530,12 +524,8 @@
 All the text fields of a branch are considered unicode, so the email
 must also handle the unicode.
 
-    >>> before_modification = Snapshot(branch, providing=IBranch)
-
-    >>> branch.whiteboard = u'A new \ua000 summary'
-    >>> updated_fields = ['whiteboard']
-    >>> notify(ObjectModifiedEvent(
-    ...     branch, before_modification, updated_fields))
+    >>> with notify_modified(branch, ['whiteboard']):
+    ...     branch.whiteboard = u'A new \ua000 summary'
     >>> run_modified_mail_jobs()
 
     >>> notifications = pop_notifications()
@@ -572,13 +562,10 @@
 Login as an admin user so we can alter the branch.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> before_modification = Snapshot(branch, providing=IBranch)
 
-    >>> branch.whiteboard = (
-    ...     'Please refrain from bad language in a public arena.')
-    >>> updated_fields = ['whiteboard']
-    >>> notify(ObjectModifiedEvent(
-    ...     branch, before_modification, updated_fields))
+    >>> with notify_modified(branch, ['whiteboard']):
+    ...     branch.whiteboard = (
+    ...         'Please refrain from bad language in a public arena.')
     >>> run_modified_mail_jobs()
 
     >>> notifications = pop_notifications()

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py	2018-07-23 17:15:40 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py	2019-01-25 18:33:51 +0000
@@ -14,13 +14,11 @@
 import textwrap
 
 from bzrlib.plugins.builder.recipe import ForbiddenInstructionError
-from lazr.lifecycle.event import ObjectModifiedEvent
 from pytz import UTC
 from storm.locals import Store
 from testtools.matchers import Equals
 import transaction
 from zope.component import getUtility
-from zope.event import notify
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
@@ -63,6 +61,7 @@
 from lp.services.propertycache import clear_property_cache
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.publisher import canonical_url
+from lp.services.webapp.snapshot import notify_modified
 from lp.soyuz.enums import ArchivePurpose
 from lp.soyuz.interfaces.archive import (
     ArchiveDisabled,
@@ -1161,9 +1160,8 @@
     def test_modifiedevent_sets_date_last_updated(self):
         # We publish an object modified event to check that the last modified
         # date is set to UTC_NOW.
-        field = ISourcePackageRecipe['name']
-        notify(ObjectModifiedEvent(
-            removeSecurityProxy(self.recipe), self.recipe, [field]))
+        with notify_modified(removeSecurityProxy(self.recipe), ['name']):
+            pass
         self.assertSqlAttributeEqualsDate(
             self.recipe, 'date_last_modified', UTC_NOW)
 

=== modified file 'lib/lp/coop/answersbugs/browser.py'
--- lib/lp/coop/answersbugs/browser.py	2015-10-26 14:54:43 +0000
+++ lib/lp/coop/answersbugs/browser.py	2019-01-25 18:33:51 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Views for linking bugs and questions."""
@@ -6,12 +6,6 @@
 __metaclass__ = type
 __all__ = []
 
-
-from lazr.lifecycle.event import ObjectModifiedEvent
-from lazr.lifecycle.snapshot import Snapshot
-from zope.event import notify
-from zope.interface import providedBy
-
 from lp import _
 from lp.app.browser.launchpadform import (
     action,
@@ -22,6 +16,7 @@
     IBug,
     )
 from lp.services.webapp.publisher import canonical_url
+from lp.services.webapp.snapshot import notify_modified
 
 
 class QuestionMakeBugView(LaunchpadFormView):
@@ -63,16 +58,13 @@
         """Create a Bug from a Question."""
         question = self.context
 
-        unmodifed_question = Snapshot(
-            question, providing=providedBy(question))
-        params = CreateBugParams(
-            owner=self.user, title=data['title'], comment=data['description'])
-        bug = question.target.createBug(params)
-        question.linkBug(bug, user=self.user)
-        bug.subscribe(question.owner, self.user)
-        bug_added_event = ObjectModifiedEvent(
-            question, unmodifed_question, ['bugs'])
-        notify(bug_added_event)
+        with notify_modified(question, ['bugs']):
+            params = CreateBugParams(
+                owner=self.user, title=data['title'],
+                comment=data['description'])
+            bug = question.target.createBug(params)
+            question.linkBug(bug, user=self.user)
+            bug.subscribe(question.owner, self.user)
         self.request.response.addNotification(
             _('Thank you! Bug #$bugid created.', mapping={'bugid': bug.id}))
         self.next_url = canonical_url(bug)

=== modified file 'lib/lp/coop/answersbugs/tests/notifications-linked-bug.txt'
--- lib/lp/coop/answersbugs/tests/notifications-linked-bug.txt	2018-05-31 12:36:54 +0000
+++ lib/lp/coop/answersbugs/tests/notifications-linked-bug.txt	2019-01-25 18:33:51 +0000
@@ -4,23 +4,17 @@
 While a bug is linked to a question , its subscribers will be notified
 of changes to the bug status:
 
-    >>> from zope.event import notify
-    >>> from zope.interface import providedBy
-    >>> from lazr.lifecycle.event import ObjectModifiedEvent
-    >>> from lazr.lifecycle.snapshot import Snapshot
     >>> from lp.answers.tests.test_question_notifications import (
     ...     pop_questionemailjobs)
     >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.services.webapp.snapshot import notify_modified
 
     >>> no_priv = getUtility(IPersonSet).getByName('no-priv')
     >>> bugtask = get_bugtask_linked_to_question()
-    >>> original_bugtask = Snapshot(bugtask, providing=providedBy(bugtask))
-    >>> bugtask.transitionToStatus(BugTaskStatus.CONFIRMED, no_priv)
-    >>> ignore = pop_questionemailjobs()
-    >>> notify(ObjectModifiedEvent(
-    ...     bugtask, original_bugtask, ['status'],
-    ...     user=no_priv))
+    >>> with notify_modified(bugtask, ['status'], user=no_priv):
+    ...     bugtask.transitionToStatus(BugTaskStatus.CONFIRMED, no_priv)
+    ...     ignore = pop_questionemailjobs()
 
     >>> notifications = pop_questionemailjobs()
     >>> len(notifications)
@@ -50,11 +44,9 @@
     >>> sample_person = getUtility(IPersonSet).getByEmail(
     ...     'test@xxxxxxxxxxxxx')
     >>> ignored = login_person(sample_person)
-    >>> original_bugtask = Snapshot(bugtask, providing=providedBy(bugtask))
-    >>> bugtask.transitionToAssignee(sample_person)
-    >>> notify(ObjectModifiedEvent(
-    ...     bugtask, original_bugtask, ['assignee', 'dateassigned'],
-    ...     user=sample_person))
+    >>> with notify_modified(
+    ...         bugtask, ['assignee', 'dateassigned'], user=sample_person):
+    ...     bugtask.transitionToAssignee(sample_person)
 
     >>> len(pop_questionemailjobs())
     0

=== modified file 'lib/lp/coop/answersbugs/tests/notifications-linked-private-bug.txt'
--- lib/lp/coop/answersbugs/tests/notifications-linked-private-bug.txt	2014-01-30 15:04:06 +0000
+++ lib/lp/coop/answersbugs/tests/notifications-linked-private-bug.txt	2019-01-25 18:33:51 +0000
@@ -6,25 +6,20 @@
 Question subscribers are not sent notifications about private bugs, because
 they are indirect subscribers.
 
-    >>> from zope.event import notify
-    >>> from zope.interface import providedBy
-    >>> from lazr.lifecycle.event import ObjectModifiedEvent
-    >>> from lazr.lifecycle.snapshot import Snapshot
     >>> from lp.answers.tests.test_question_notifications import (
     ...     pop_questionemailjobs)
     >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.services.webapp.snapshot import notify_modified
 
     >>> no_priv = getUtility(IPersonSet).getByName('no-priv')
     >>> bugtask = get_bugtask_linked_to_question()
 
     >>> bugtask.bug.setPrivate(True, bugtask.bug.owner)
     True
-    >>> original_bugtask = Snapshot(bugtask, providing=providedBy(bugtask))
-    >>> bugtask.transitionToStatus(BugTaskStatus.FIXCOMMITTED, no_priv)
-    >>> ignore = pop_questionemailjobs()
-    >>> notify(ObjectModifiedEvent(
-    ...     bugtask, original_bugtask, ['status'], user=no_priv))
+    >>> with notify_modified(bugtask, ['status'], user=no_priv):
+    ...     bugtask.transitionToStatus(BugTaskStatus.FIXCOMMITTED, no_priv)
+    ...     ignore = pop_questionemailjobs()
     >>> notifications = pop_questionemailjobs()
     >>> len(notifications)
     0

=== modified file 'lib/lp/registry/doc/milestone.txt'
--- lib/lp/registry/doc/milestone.txt	2014-06-12 03:09:57 +0000
+++ lib/lp/registry/doc/milestone.txt	2019-01-25 18:33:51 +0000
@@ -319,13 +319,10 @@
 
     >>> from lp.bugs.model.bugnotification import (
     ...     BugNotification)
-    >>> from zope.event import notify
-    >>> from zope.interface import providedBy
-    >>> from lazr.lifecycle.event import ObjectModifiedEvent
-    >>> from lazr.lifecycle.snapshot import Snapshot
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.interfaces.product import IProductSet
+    >>> from lp.services.webapp.snapshot import notify_modified
     >>> firefox = getUtility(IProductSet).getByName('firefox')
     >>> firefox_trunk = firefox.getSeries('trunk')
     >>> [milestone_one] = [milestone
@@ -350,14 +347,8 @@
 We change the milestone for the task from 1.0 to 2.0, and fire the
 change event.
 
-    >>> bugtask_before_modification = Snapshot(
-    ...     bug_task, providing=providedBy(bug_task))
-    >>> bug_task.milestone = milestone_two
-    >>> notify(
-    ...     ObjectModifiedEvent(
-    ...         object=bug_task,
-    ...         object_before_modification=bugtask_before_modification,
-    ...         edited_fields=['milestone']))
+    >>> with notify_modified(bug_task, ['milestone']):
+    ...     bug_task.milestone = milestone_two
 
 A new bug notification is created, and both Celso and David are in the
 list of recipients.

=== modified file 'lib/lp/registry/model/mailinglist.py'
--- lib/lp/registry/model/mailinglist.py	2016-01-26 15:47:37 +0000
+++ lib/lp/registry/model/mailinglist.py	2019-01-25 18:33:51 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -18,11 +18,7 @@
 from socket import getfqdn
 from string import Template
 
-from lazr.lifecycle.event import (
-    ObjectCreatedEvent,
-    ObjectModifiedEvent,
-    )
-from lazr.lifecycle.snapshot import Snapshot
+from lazr.lifecycle.event import ObjectCreatedEvent
 from sqlobject import (
     ForeignKey,
     StringCol,
@@ -39,10 +35,7 @@
     queryAdapter,
     )
 from zope.event import notify
-from zope.interface import (
-    implementer,
-    providedBy,
-    )
+from zope.interface import implementer
 from zope.security.proxy import removeSecurityProxy
 
 from lp import _
@@ -90,6 +83,7 @@
 from lp.services.messages.model.message import Message
 from lp.services.privacy.interfaces import IObjectPrivacy
 from lp.services.propertycache import cachedproperty
+from lp.services.webapp.snapshot import notify_modified
 
 
 EMAIL_ADDRESS_STATUSES = (
@@ -314,12 +308,8 @@
         if self.date_activated is not None:
             return
 
-        old_mailinglist = Snapshot(self, providing=providedBy(self))
-        self.date_activated = UTC_NOW
-        notify(ObjectModifiedEvent(
-                self,
-                object_before_modification=old_mailinglist,
-                edited_fields=['date_activated']))
+        with notify_modified(self, ['date_activated']):
+            self.date_activated = UTC_NOW
 
     def deactivate(self):
         """See `IMailingList`."""

=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py	2016-09-20 13:35:32 +0000
+++ lib/lp/registry/model/product.py	2019-01-25 18:33:51 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2016 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Database classes including and related to Product."""
@@ -19,7 +19,6 @@
 import operator
 
 from lazr.lifecycle.event import ObjectModifiedEvent
-from lazr.lifecycle.snapshot import Snapshot
 from lazr.restful.declarations import error_status
 from lazr.restful.utils import safe_hasattr
 import pytz
@@ -48,10 +47,7 @@
     )
 from zope.component import getUtility
 from zope.event import notify
-from zope.interface import (
-    implementer,
-    providedBy,
-    )
+from zope.interface import implementer
 
 from lp.answers.enums import QUESTION_STATUS_DEFAULT_SEARCH
 from lp.answers.model.faq import (
@@ -205,6 +201,7 @@
     )
 from lp.services.statistics.interfaces.statistic import ILaunchpadStatisticSet
 from lp.services.webapp.interfaces import ILaunchBag
+from lp.services.webapp.snapshot import notify_modified
 from lp.translations.enums import TranslationPermission
 from lp.translations.interfaces.customlanguagecode import (
     IHasCustomLanguageCodes,
@@ -1082,11 +1079,8 @@
             # This is being initialized.
             self._owner = new_owner
         elif self.owner != new_owner:
-            old_product = Snapshot(self, providing=providedBy(self))
-            self._owner = new_owner
-            notify(ObjectModifiedEvent(
-                self, object_before_modification=old_product,
-                edited_fields=['_owner']))
+            with notify_modified(self, ['_owner']):
+                self._owner = new_owner
         else:
             # The new owner is the same as the current owner.
             pass

=== modified file 'lib/lp/services/webhooks/tests/test_model.py'
--- lib/lp/services/webhooks/tests/test_model.py	2016-06-28 21:10:18 +0000
+++ lib/lp/services/webhooks/tests/test_model.py	2019-01-25 18:33:51 +0000
@@ -1,7 +1,6 @@
 # Copyright 2015-2016 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-from lazr.lifecycle.event import ObjectModifiedEvent
 from storm.store import Store
 from testtools.matchers import (
     Equals,
@@ -10,7 +9,6 @@
     )
 import transaction
 from zope.component import getUtility
-from zope.event import notify
 from zope.security.checker import getChecker
 from zope.security.proxy import removeSecurityProxy
 
@@ -18,10 +16,8 @@
 from lp.registry.enums import BranchSharingPolicy
 from lp.services.database.interfaces import IStore
 from lp.services.webapp.authorization import check_permission
-from lp.services.webhooks.interfaces import (
-    IWebhook,
-    IWebhookSet,
-    )
+from lp.services.webapp.snapshot import notify_modified
+from lp.services.webhooks.interfaces import IWebhookSet
 from lp.services.webhooks.model import (
     WebhookJob,
     WebhookSet,
@@ -50,8 +46,8 @@
         transaction.commit()
         with admin_logged_in():
             old_mtime = webhook.date_last_modified
-        notify(ObjectModifiedEvent(
-            webhook, webhook, [IWebhook["delivery_url"]]))
+        with notify_modified(webhook, ['delivery_url']):
+            pass
         with admin_logged_in():
             self.assertThat(
                 webhook.date_last_modified,

=== modified file 'lib/lp/snappy/tests/test_snap.py'
--- lib/lp/snappy/tests/test_snap.py	2018-11-07 14:45:31 +0000
+++ lib/lp/snappy/tests/test_snap.py	2019-01-25 18:33:51 +0000
@@ -18,7 +18,6 @@
 
 from fixtures import MockPatch
 import iso8601
-from lazr.lifecycle.event import ObjectModifiedEvent
 from pymacaroons import Macaroon
 import pytz
 import responses
@@ -38,7 +37,6 @@
     )
 import transaction
 from zope.component import getUtility
-from zope.event import notify
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.enums import InformationType
@@ -85,6 +83,7 @@
 from lp.services.timeout import default_timeout
 from lp.services.webapp.interfaces import OAuthPermission
 from lp.services.webapp.publisher import canonical_url
+from lp.services.webapp.snapshot import notify_modified
 from lp.snappy.interfaces.snap import (
     BadSnapSearchContext,
     CannotFetchSnapcraftYaml,
@@ -191,8 +190,8 @@
         # When a Snap receives an object modified event, the last modified
         # date is set to UTC_NOW.
         snap = self.factory.makeSnap(date_created=ONE_DAY_AGO)
-        notify(ObjectModifiedEvent(
-            removeSecurityProxy(snap), snap, [ISnap["name"]]))
+        with notify_modified(removeSecurityProxy(snap), ["name"]):
+            pass
         self.assertSqlAttributeEqualsDate(snap, "date_last_modified", UTC_NOW)
 
     def makeBuildableDistroArchSeries(self, **kwargs):

=== modified file 'lib/lp/soyuz/tests/test_livefs.py'
--- lib/lp/soyuz/tests/test_livefs.py	2018-02-02 03:14:35 +0000
+++ lib/lp/soyuz/tests/test_livefs.py	2019-01-25 18:33:51 +0000
@@ -12,13 +12,11 @@
     timedelta,
     )
 
-from lazr.lifecycle.event import ObjectModifiedEvent
 import pytz
 from storm.locals import Store
 from testtools.matchers import Equals
 import transaction
 from zope.component import getUtility
-from zope.event import notify
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
@@ -34,6 +32,7 @@
 from lp.services.database.constants import UTC_NOW
 from lp.services.features.testing import FeatureFixture
 from lp.services.webapp.interfaces import OAuthPermission
+from lp.services.webapp.snapshot import notify_modified
 from lp.soyuz.interfaces.livefs import (
     CannotDeleteLiveFS,
     ILiveFS,
@@ -108,8 +107,8 @@
         # date is set to UTC_NOW.
         livefs = self.factory.makeLiveFS(
             date_created=datetime(2014, 4, 25, 10, 38, 0, tzinfo=pytz.UTC))
-        notify(ObjectModifiedEvent(
-            removeSecurityProxy(livefs), livefs, [ILiveFS["name"]]))
+        with notify_modified(removeSecurityProxy(livefs), ["name"]):
+            pass
         self.assertSqlAttributeEqualsDate(
             livefs, "date_last_modified", UTC_NOW)
 

=== modified file 'lib/lp/translations/tests/test_translationpackagingjob.py'
--- lib/lp/translations/tests/test_translationpackagingjob.py	2014-08-22 08:01:26 +0000
+++ lib/lp/translations/tests/test_translationpackagingjob.py	2019-01-25 18:33:51 +0000
@@ -1,17 +1,13 @@
-# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2011-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for merging translations."""
 
 __metaclass__ = type
 
-
-from lazr.lifecycle.event import ObjectModifiedEvent
-from lazr.lifecycle.snapshot import Snapshot
 from storm.expr import Desc
 import transaction
 from zope.component import getUtility
-from zope.event import notify
 
 from lp.registry.interfaces.packaging import IPackagingUtil
 from lp.services.database.interfaces import IStore
@@ -21,6 +17,7 @@
     JobStatus,
     )
 from lp.services.job.tests import block_on_job
+from lp.services.webapp.snapshot import notify_modified
 from lp.testing import (
     celebrity_logged_in,
     EventRecorder,
@@ -32,7 +29,6 @@
     CeleryJobLayer,
     LaunchpadZopelessLayer,
     )
-from lp.translations.interfaces.potemplate import IPOTemplate
 from lp.translations.interfaces.side import TranslationSide
 from lp.translations.interfaces.translationpackagingjob import (
     ITranslationPackagingJobSource,
@@ -318,9 +314,8 @@
             None, None, TranslationTemplateChangeJob, potemplate)
         self.assertEqual([], finder.find())
         with person_logged_in(potemplate.owner):
-            snapshot = Snapshot(potemplate, providing=IPOTemplate)
-            potemplate.name = self.factory.getUniqueString()
-            notify(ObjectModifiedEvent(potemplate, snapshot, ["name"]))
+            with notify_modified(potemplate, ["name"]):
+                potemplate.name = self.factory.getUniqueString()
 
         (job,) = finder.find()
         self.assertIsInstance(job, TranslationTemplateChangeJob)


Follow ups