Merge lp:~flacoste/lazr.lifecycle/devel into lp:~launchpad-pqm/lazr.lifecycle/trunk

Proposed by Francis J. Lacoste
Status: Merged
Merged at revision: not available
Proposed branch: lp:~flacoste/lazr.lifecycle/devel
Merge into: lp:~launchpad-pqm/lazr.lifecycle/trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~flacoste/lazr.lifecycle/devel
Reviewer Review Type Date Requested Status
Gary Poster Approve
Gary Arel Pending
Review via email: mp+4207@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Francis J. Lacoste (flacoste) wrote :

Hi Gary,

Here is the initial version of lazr.lifecycle. It brings ObjectDelta, Snapshot and the SQLObject events which have been renamed to the more generic names.

Revision history for this message
Gary Poster (gary) wrote :
Download full text (4.6 KiB)

merge-conditional

Looks good. I have a few changes for readability, below. Other than that, ship it. :-)

Thanks

Gary

...

> === modified file 'src/lazr/lifecycle/README.txt'
> --- src/lazr/lifecycle/README.txt 2009-03-02 21:29:43 +0000
> +++ src/lazr/lifecycle/README.txt 2009-03-05 16:35:08 +0000
> @@ -15,8 +15,14 @@
> along with lazr.lifecycle. If not, see <http://www.gnu.org/licenses/>.
>
> LAZR lifecycle
> -***********
> -
> -This is a pure template for new lazr namespace packages. Whenever you want to
> -make a new lazr subpackage, just branch this code and change all occurrences
> -of 'lifecycle' with whatever your subpackage's name is.
> +**************
> +
> +This package defines three "lifecycle" events that notifies about object

...that notify...

> +creation, modification and deletion. The events include information about the
> +user responsible for the changes.
> +
> +The modification event also includes information about the state of the object
> +before the changes.
> +
> +The module also contains Snapshot support (to save the state of an object for
> +notification) and to compute deltas between version of objects.

Remove parens, add a comma:

The module also contains Snapshot support to save the state of an object for
notification, and to compute deltas between version of objects.

>
> === modified file 'src/lazr/lifecycle/docs/snapshot.txt'
> --- src/lazr/lifecycle/docs/snapshot.txt 2009-03-03 19:34:49 +0000
> +++ src/lazr/lifecycle/docs/snapshot.txt 2009-03-05 15:19:18 +0000
> @@ -117,50 +117,53 @@
> >>> IBar.providedBy(snapshot)
> True
>
> -Snapshots of Iterables
> -----------------------
> -
> -If your some fields is actually an iterable (but not a string), they will be
> -automatically converted to lists when attaching them to
> -the Snapshot object. The example below uses a property that returns
> -a generator for example.
> -
> - >>> class IterableFoo(Foo):
> - ... implements(IFoo)
> +
> +ISnapshotValueFactory
> +---------------------
> +
> +For some fields, assigning the existing value to the snapshot object
> +isn't appropriate. For these case, one can provide a factory registered
> +as an adapter for the value to ISnapshotValueFactory. The result of the
> +adaptation lookup will be stored in the snapshot attribute.
> +
> + >>> from zope.interface import implementer, Interface
> + >>> from zope.component import adapter, getSiteManager
> +
> + >>> class IIterable(Interface):
> + ... """Marker for a value that needs special snapshot need."""

"...that needs a special snapshot"?

> +
> + >>> class EvenOrOddIterable:
> + ... """An object that will be snapshotted specially."""
> + ... implements(IIterable)
> ...
> ... even = True
> ... max = 10
> ...
> - ... @property
> - ... def remotes(self):
> + ... def __iter__(self):
> ... for i in range(self.max):
> ... if i % 2 == 0 and self.even:
> - ... yield 1
> + ... yield i
> ... elif i % 2 == 1 and not self.even:
> - ... yield 1
> + ... ...

Read more...

review: Approve
lp:~flacoste/lazr.lifecycle/devel updated
34. By Francis J. Lacoste

Make example clearer.

35. By Francis J. Lacoste

Updates based on Gary's review.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.txt'
2--- README.txt 2009-03-02 21:29:43 +0000
3+++ README.txt 2009-03-05 16:35:08 +0000
4@@ -1,8 +1,6 @@
5-This is a template for your lazr package. To start your own lazr package,
6-branch this code and change 'lifecycle' to your package's actual name in all
7-files.
8-
9-
10+Richer lifecycle events API.
11+
12+..
13 This file is part of lazr.lifecycle.
14
15 lazr.lifecycle is free software: you can redistribute it and/or modify it
16
17=== modified file 'src/lazr/lifecycle/README.txt'
18--- src/lazr/lifecycle/README.txt 2009-03-02 21:29:43 +0000
19+++ src/lazr/lifecycle/README.txt 2009-03-05 16:35:08 +0000
20@@ -15,8 +15,14 @@
21 along with lazr.lifecycle. If not, see <http://www.gnu.org/licenses/>.
22
23 LAZR lifecycle
24-***********
25-
26-This is a pure template for new lazr namespace packages. Whenever you want to
27-make a new lazr subpackage, just branch this code and change all occurrences
28-of 'lifecycle' with whatever your subpackage's name is.
29+**************
30+
31+This package defines three "lifecycle" events that notifies about object
32+creation, modification and deletion. The events include information about the
33+user responsible for the changes.
34+
35+The modification event also includes information about the state of the object
36+before the changes.
37+
38+The module also contains Snapshot support (to save the state of an object for
39+notification) and to compute deltas between version of objects.
40
41=== modified file 'src/lazr/lifecycle/docs/snapshot.txt'
42--- src/lazr/lifecycle/docs/snapshot.txt 2009-03-03 19:34:49 +0000
43+++ src/lazr/lifecycle/docs/snapshot.txt 2009-03-05 15:19:18 +0000
44@@ -117,50 +117,53 @@
45 >>> IBar.providedBy(snapshot)
46 True
47
48-Snapshots of Iterables
49-----------------------
50-
51-If your some fields is actually an iterable (but not a string), they will be
52-automatically converted to lists when attaching them to
53-the Snapshot object. The example below uses a property that returns
54-a generator for example.
55-
56- >>> class IterableFoo(Foo):
57- ... implements(IFoo)
58+
59+ISnapshotValueFactory
60+---------------------
61+
62+For some fields, assigning the existing value to the snapshot object
63+isn't appropriate. For these case, one can provide a factory registered
64+as an adapter for the value to ISnapshotValueFactory. The result of the
65+adaptation lookup will be stored in the snapshot attribute.
66+
67+ >>> from zope.interface import implementer, Interface
68+ >>> from zope.component import adapter, getSiteManager
69+
70+ >>> class IIterable(Interface):
71+ ... """Marker for a value that needs special snapshot need."""
72+
73+ >>> class EvenOrOddIterable:
74+ ... """An object that will be snapshotted specially."""
75+ ... implements(IIterable)
76 ...
77 ... even = True
78 ... max = 10
79 ...
80- ... @property
81- ... def remotes(self):
82+ ... def __iter__(self):
83 ... for i in range(self.max):
84 ... if i % 2 == 0 and self.even:
85- ... yield 1
86+ ... yield i
87 ... elif i % 2 == 1 and not self.even:
88- ... yield 1
89+ ... yield i
90 ... else:
91 ... continue
92- >>> even_generator = IterableFoo()
93+
94+ >>> from lazr.lifecycle.interfaces import ISnapshotValueFactory
95+ >>> @implementer(ISnapshotValueFactory)
96+ ... @adapter(IIterable)
97+ ... def snapshot_iterable(value):
98+ ... return list(value)
99+ >>> getSiteManager().registerAdapter(snapshot_iterable)
100+
101+ >>> even_generator = Foo()
102 >>> even_generator.title = 'Even'
103 >>> even_generator.description = 'Generates even number below 10.'
104+ >>> even_generator.remotes = EvenOrOddIterable()
105
106 >>> snapshot = Snapshot(even_generator, providing=IFoo)
107 >>> snapshot.remotes == list(even_generator.remotes)
108 True
109
110-shortlist is used under the hood to convert this, so a warning will be issued
111-if the iterables has more than 100 items and an error will be raised if there
112-are more than 1000.
113-
114- >>> even_generator.max = 202
115- >>> snapshot = Snapshot(even_generator, providing=IFoo)
116- UserWarning: ...
117-
118-
119- >>> even_generator.max = 2002
120- >>> snapshot = Snapshot(even_generator, providing=IFoo)
121- Traceback (most recent call last):
122- ...
123- ShortListTooBigError: ...
124-
125+ >>> getSiteManager().unregisterAdapter(snapshot_iterable)
126+ True
127
128
129=== modified file 'src/lazr/lifecycle/interfaces.py'
130--- src/lazr/lifecycle/interfaces.py 2009-03-03 20:38:02 +0000
131+++ src/lazr/lifecycle/interfaces.py 2009-03-05 15:19:18 +0000
132@@ -21,7 +21,8 @@
133 __all__ = [
134 'IObjectCreatedEvent',
135 'IObjectDeletedEvent',
136- 'IObjectModifiedEvent'
137+ 'IObjectModifiedEvent',
138+ 'ISnapshotValueFactory',
139 ]
140
141 import zope.lifecycleevent.interfaces as z3lifecycle
142@@ -49,3 +50,13 @@
143 "changed.")
144 user = Attribute("The user who modified the object.")
145
146+
147+class ISnapshotValueFactory(Interface):
148+ """This is a marker interface used to obtain snapshot of values.
149+
150+ The interface isn't meant to be provided, but is only used as a factory
151+ lookup. The snapshot value is what should be returned from the adapter
152+ lookup.
153+ """
154+
155+
156
157=== removed file 'src/lazr/lifecycle/shortlist.py'
158--- src/lazr/lifecycle/shortlist.py 2009-03-03 23:12:10 +0000
159+++ src/lazr/lifecycle/shortlist.py 1970-01-01 00:00:00 +0000
160@@ -1,89 +0,0 @@
161-# Copyright 2009 Canonical Ltd. All rights reserved.
162-#
163-# This file is part of lazr.lifecycle
164-#
165-# lazr.lifecycle is free software: you can redistribute it and/or modify it
166-# under the terms of the GNU Lesser General Public License as published by
167-# the Free Software Foundation, either version 3 of the License, or (at your
168-# option) any later version.
169-#
170-# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
171-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
172-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
173-# License for more details.
174-#
175-# You should have received a copy of the GNU Lesser General Public License
176-# along with lazr.lifecycle. If not, see <http://www.gnu.org/licenses/>.
177-
178-"""A function to listify iterables but with a maximum size."""
179-
180-# This should probably live in its own package as it is generally useful.
181-# But since none of the other lazr libraries requires it, it's fine here for
182-# the moment.
183-
184-__metaclass__ = type
185-
186-__all__ = [
187- 'shortlist',
188- 'ShortListTooBigError'
189- ]
190-
191-import warnings
192-
193-
194-class ShortListTooBigError(Exception):
195- """This error is raised when the shortlist hardlimit is reached"""
196-
197-
198-def shortlist(sequence, longest_expected=15, hardlimit=None):
199- """Return a listified version of sequence.
200-
201- If <sequence> has more than <longest_expected> items, a warning is issued.
202-
203- >>> shortlist([1, 2])
204- [1, 2]
205-
206- >>> shortlist([1, 2, 3], 2)
207- UserWarning: shortlist() should not be used here. It's meant to listify sequences with no more than 2 items. There were 3 items.
208- [1, 2, 3]
209-
210- >>> shortlist([1, 2, 3, 4], hardlimit=2)
211- Traceback (most recent call last):
212- ...
213- ShortListTooBigError: Hard limit of 2 exceeded.
214-
215- >>> shortlist([1, 2, 3, 4], 2, hardlimit=4)
216- UserWarning: shortlist() should not be used here. It's meant to listify sequences with no more than 2 items. There were 4 items.
217- [1, 2, 3, 4]
218-
219- It works on iterable also.
220-
221- >>> shortlist(range(10), 5, hardlimit=8) #doctest: +ELLIPSIS
222- Traceback (most recent call last):
223- ...
224- ShortListTooBigError: ...
225-
226- """
227- if hardlimit is not None:
228- last = hardlimit + 1
229- else:
230- last = longest_expected + 1
231- if getattr(sequence, '__getitem__', False):
232- results = list(sequence[:last])
233- else:
234- results = []
235- for idx, item in enumerate(sequence):
236- if hardlimit and idx > hardlimit:
237- break
238- results.append(item)
239-
240- size = len(results)
241- if hardlimit and size > hardlimit:
242- raise ShortListTooBigError(
243- 'Hard limit of %d exceeded.' % hardlimit)
244- elif size > longest_expected:
245- warnings.warn(
246- "shortlist() should not be used here. It's meant to listify"
247- " sequences with no more than %d items. There were %s items."
248- % (longest_expected, size), stacklevel=2)
249- return results
250
251=== modified file 'src/lazr/lifecycle/snapshot.py'
252--- src/lazr/lifecycle/snapshot.py 2009-03-03 20:38:02 +0000
253+++ src/lazr/lifecycle/snapshot.py 2009-03-05 15:19:18 +0000
254@@ -26,13 +26,13 @@
255 'Snapshot',
256 ]
257
258+from zope.component import queryAdapter
259 from zope.interface.interfaces import IInterface
260 from zope.interface import directlyProvides
261 from zope.schema.interfaces import IField
262 from zope.security.proxy import removeSecurityProxy
263
264-from lazr.lifecycle.shortlist import shortlist
265-
266+from lazr.lifecycle.interfaces import ISnapshotValueFactory
267
268 _marker = object()
269
270@@ -48,6 +48,9 @@
271 will also provide the interfaces listed in providing. If no names
272 are supplied but an interface is provided, all Fields of that
273 interface will be included in the snapshot.
274+
275+ The attributes are copied by passing them through a ISnapshotValueFactory.
276+ The default implementation of that adapter just returns the value itself.
277 """
278 def __init__(self, ob, names=None, providing=None):
279 ob = removeSecurityProxy(ob)
280@@ -72,15 +75,11 @@
281 if value is _marker:
282 raise AssertionError("Attribute %s not in object %r"
283 % (name, ob))
284- def should_listify(value):
285- # We listify using shortlist any iterable that isn't one of
286- # the basic types.
287- return (
288- not isinstance(value, (str, unicode, tuple, list))
289- and getattr(value, '__iter__', False))
290- if should_listify(value):
291- value = shortlist(value, longest_expected=100, hardlimit=1000)
292- setattr(self, name, value)
293+ snapshot_value = queryAdapter(
294+ value, ISnapshotValueFactory, default=_marker)
295+ if snapshot_value is _marker:
296+ snapshot_value = value
297+ setattr(self, name, snapshot_value)
298
299 if providing is not None:
300 directlyProvides(self, providing)
301
302=== modified file 'src/lazr/lifecycle/tests/test_documentation.py'
303--- src/lazr/lifecycle/tests/test_documentation.py 2009-03-03 19:29:52 +0000
304+++ src/lazr/lifecycle/tests/test_documentation.py 2009-03-05 16:05:12 +0000
305@@ -12,8 +12,6 @@
306 import unittest
307 import warnings
308
309-import lazr.lifecycle.shortlist
310-
311 DOCTEST_FLAGS = (
312 doctest.ELLIPSIS |
313 doctest.NORMALIZE_WHITESPACE |
314@@ -50,6 +48,4 @@
315 optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS
316 )
317 return unittest.TestSuite((
318- doctest.DocFileSuite(*doctest_files, **options),
319- doctest.DocTestSuite(
320- lazr.lifecycle.shortlist, setUp=setUp, tearDown=tearDown)))
321+ doctest.DocFileSuite(*doctest_files, **options)))

Subscribers

People subscribed via source and target branches