Merge lp:~jkakar/storm/json-property into lp:storm

Proposed by Jamu Kakar
Status: Merged
Approved by: Thomas Herve
Approved revision: 389
Merged at revision: 399
Proposed branch: lp:~jkakar/storm/json-property
Merge into: lp:storm
Diff against target: 316 lines (+158/-14)
6 files modified
storm/compat.py (+32/-0)
storm/locals.py (+2/-2)
storm/properties.py (+5/-2)
storm/variables.py (+34/-6)
tests/properties.py (+62/-0)
tests/variables.py (+23/-4)
To merge this branch: bzr merge lp:~jkakar/storm/json-property
Reviewer Review Type Date Requested Status
Thomas Herve (community) Approve
Stuart Bishop (community) Approve
Review via email: mp+51634@code.launchpad.net

Description of the change

This branch introduces the following changes:

- Most of the logic in the PickleVariable code has been split out into
  a new EncodedValueVariable class.

- A new JSONVariable, based on EncodedValueVariable, is available.

- A new JSON property is available.

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

This all looks good.

There is a lot of cut-and-paste code in test_properties that should be shared, but that isn't an issue you need to deal with on this branch.

review: Approve
Revision history for this message
Jamu Kakar (jkakar) wrote :

Stuart:

Thanks for the review! I thought about fixing the cut-and-paste issue
in the property tests, but it would be a bit messy, so I decided to
avoid it for now.

Revision history for this message
Thomas Herve (therve) wrote :

[1] The json module is only available in 2.6, we should at least keep 2.5 compatibility for now. I suggest moving the import at the class level (so that it's only used when a person use the variable), and fallback on simplejson for 2.5. The tests should be skipped if json/simplejson is not available.

review: Needs Fixing
lp:~jkakar/storm/json-property updated
389. By Jamu Kakar

- Merged Gavin's improvements.

Revision history for this message
Thomas Herve (therve) wrote :

Looks good, +1!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'storm/compat.py'
2--- storm/compat.py 1970-01-01 00:00:00 +0000
3+++ storm/compat.py 2011-09-07 14:15:40 +0000
4@@ -0,0 +1,32 @@
5+#
6+# Copyright (c) 2011 Canonical
7+#
8+# Written by Gustavo Niemeyer <gustavo@niemeyer.net>
9+#
10+# This file is part of Storm Object Relational Mapper.
11+#
12+# Storm is free software; you can redistribute it and/or modify
13+# it under the terms of the GNU Lesser General Public License as
14+# published by the Free Software Foundation; either version 2.1 of
15+# the License, or (at your option) any later version.
16+#
17+# Storm is distributed in the hope that it will be useful,
18+# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+# GNU Lesser General Public License for more details.
21+#
22+# You should have received a copy of the GNU Lesser General Public License
23+# along with this program. If not, see <http://www.gnu.org/licenses/>.
24+#
25+
26+__all__ = [
27+ "json",
28+ ]
29+
30+try:
31+ import json
32+except ImportError:
33+ try:
34+ import simplejson as json
35+ except ImportError:
36+ json = None
37
38=== modified file 'storm/locals.py'
39--- storm/locals.py 2011-02-14 12:17:54 +0000
40+++ storm/locals.py 2011-09-07 14:15:40 +0000
41@@ -18,9 +18,9 @@
42 # You should have received a copy of the GNU Lesser General Public License
43 # along with this program. If not, see <http://www.gnu.org/licenses/>.
44 #
45-from storm.properties import Bool, Int, Float, RawStr, Chars, Unicode, Pickle
46+from storm.properties import Bool, Int, Float, RawStr, Chars, Unicode
47 from storm.properties import List, Decimal, DateTime, Date, Time, Enum, UUID
48-from storm.properties import TimeDelta
49+from storm.properties import TimeDelta, Pickle, JSON
50 from storm.references import Reference, ReferenceSet, Proxy
51 from storm.database import create_database
52 from storm.exceptions import StormError
53
54=== modified file 'storm/properties.py'
55--- storm/properties.py 2009-02-04 06:33:24 +0000
56+++ storm/properties.py 2011-09-07 14:15:40 +0000
57@@ -29,14 +29,14 @@
58 Variable, VariableFactory, BoolVariable, IntVariable, FloatVariable,
59 DecimalVariable, RawStrVariable, UnicodeVariable, DateTimeVariable,
60 DateVariable, TimeVariable, TimeDeltaVariable, UUIDVariable,
61- PickleVariable, ListVariable, EnumVariable)
62+ PickleVariable, JSONVariable, ListVariable, EnumVariable)
63
64
65
66 __all__ = ["Property", "SimpleProperty",
67 "Bool", "Int", "Float", "Decimal", "RawStr", "Unicode",
68 "DateTime", "Date", "Time", "TimeDelta", "UUID", "Enum",
69- "Pickle", "List", "PropertyRegistry"]
70+ "Pickle", "JSON", "List", "PropertyRegistry"]
71
72
73 class Property(object):
74@@ -172,6 +172,9 @@
75 class Pickle(SimpleProperty):
76 variable_class = PickleVariable
77
78+class JSON(SimpleProperty):
79+ variable_class = JSONVariable
80+
81
82 class List(SimpleProperty):
83 variable_class = ListVariable
84
85=== modified file 'storm/variables.py'
86--- storm/variables.py 2011-02-14 12:17:54 +0000
87+++ storm/variables.py 2011-09-07 14:15:40 +0000
88@@ -27,6 +27,7 @@
89 except ImportError:
90 uuid = None
91
92+from storm.compat import json
93 from storm.exceptions import NoneError
94 from storm import Undef, has_cextensions
95
96@@ -48,6 +49,7 @@
97 "EnumVariable",
98 "UUIDVariable",
99 "PickleVariable",
100+ "JSONVariable",
101 "ListVariable",
102 ]
103
104@@ -566,7 +568,7 @@
105 if (self._checkpoint_state is not Undef and
106 self.get_state() != self._checkpoint_state):
107 self.event.emit("changed", self, None, self._value, False)
108-
109+
110 def _detect_changes_and_stop(self, obj_info):
111 self._detect_changes(obj_info)
112 if self._event_system is not None:
113@@ -586,29 +588,55 @@
114 super(MutableValueVariable, self).set(value, from_db)
115
116
117-class PickleVariable(MutableValueVariable):
118+class EncodedValueVariable(MutableValueVariable):
119+
120 __slots__ = ()
121
122 def parse_set(self, value, from_db):
123 if from_db:
124 if isinstance(value, buffer):
125 value = str(value)
126- return pickle.loads(value)
127+ return self._loads(value)
128 else:
129 return value
130
131 def parse_get(self, value, to_db):
132 if to_db:
133- return pickle.dumps(value, -1)
134+ return self._dumps(value)
135 else:
136 return value
137
138 def get_state(self):
139- return (self._lazy_value, pickle.dumps(self._value, -1))
140+ return (self._lazy_value, self._dumps(self._value))
141
142 def set_state(self, state):
143 self._lazy_value = state[0]
144- self._value = pickle.loads(state[1])
145+ self._value = self._loads(state[1])
146+
147+
148+class PickleVariable(EncodedValueVariable):
149+
150+ def _loads(self, value):
151+ return pickle.loads(value)
152+
153+ def _dumps(self, value):
154+ return pickle.dumps(value, -1)
155+
156+
157+class JSONVariable(EncodedValueVariable):
158+
159+ __slots__ = ()
160+
161+ def __init__(self, *args, **kwargs):
162+ assert json is not None, (
163+ "Neither the json nor the simplejson module was found.")
164+ super(JSONVariable, self).__init__(*args, **kwargs)
165+
166+ def _loads(self, value):
167+ return json.loads(value)
168+
169+ def _dumps(self, value):
170+ return json.dumps(value)
171
172
173 class ListVariable(MutableValueVariable):
174
175=== modified file 'tests/properties.py'
176--- tests/properties.py 2009-02-04 06:33:24 +0000
177+++ tests/properties.py 2011-09-07 14:15:40 +0000
178@@ -26,6 +26,7 @@
179 except ImportError:
180 uuid = None
181
182+from storm.compat import json
183 from storm.exceptions import NoneError, PropertyPathError
184 from storm.properties import PropertyPublisherMeta
185 from storm.properties import *
186@@ -693,6 +694,67 @@
187 del self.obj
188 self.assertEquals(changes, [(self.variable1, None, ["a"], False)])
189
190+ def test_json(self):
191+ # Skip test if json support is not available.
192+ if json is None:
193+ return
194+
195+ self.setup(JSON, default_factory=dict, allow_none=False)
196+
197+ self.assertTrue(isinstance(self.column1, Column))
198+ self.assertTrue(isinstance(self.column2, Column))
199+ self.assertEquals(self.column1.name, "column1")
200+ self.assertEquals(self.column1.table, self.SubClass)
201+ self.assertEquals(self.column2.name, "prop2")
202+ self.assertEquals(self.column2.table, self.SubClass)
203+ self.assertTrue(isinstance(self.variable1, JSONVariable))
204+ self.assertTrue(isinstance(self.variable2, JSONVariable))
205+
206+ self.assertEquals(self.obj.prop1, {})
207+ self.assertRaises(NoneError, setattr, self.obj, "prop1", None)
208+ self.obj.prop2 = None
209+ self.assertEquals(self.obj.prop2, None)
210+
211+ self.obj.prop1 = []
212+ self.assertEquals(self.obj.prop1, [])
213+ self.obj.prop1.append("a")
214+ self.assertEquals(self.obj.prop1, ["a"])
215+
216+ def test_json_events(self):
217+ # Skip test if json support is not available.
218+ if json is None:
219+ return
220+
221+ self.setup(JSON, default_factory=list, allow_none=False)
222+
223+ changes = []
224+ def changed(owner, variable, old_value, new_value, fromdb):
225+ changes.append((variable, old_value, new_value, fromdb))
226+
227+ # Can't checkpoint Undef.
228+ self.obj.prop2 = []
229+
230+ self.obj_info.checkpoint()
231+ self.obj_info.event.emit("start-tracking-changes", self.obj_info.event)
232+ self.obj_info.event.hook("changed", changed)
233+
234+ self.assertEquals(self.obj.prop1, [])
235+ self.assertEquals(changes, [])
236+ self.obj.prop1.append("a")
237+ self.assertEquals(changes, [])
238+
239+ # Check "flush" event. Notice that the other variable wasn't
240+ # listed, since it wasn't changed.
241+ self.obj_info.event.emit("flush")
242+ self.assertEquals(changes, [(self.variable1, None, ["a"], False)])
243+
244+ del changes[:]
245+
246+ # Check "object-deleted" event. Notice that the other variable
247+ # wasn't listed again, since it wasn't changed.
248+ del self.obj
249+ self.assertEquals(changes, [(self.variable1, None, ["a"], False)])
250+
251 def test_list(self):
252 self.setup(List, default_factory=list, allow_none=False)
253
254
255=== modified file 'tests/variables.py'
256--- tests/variables.py 2011-01-19 00:32:48 +0000
257+++ tests/variables.py 2011-09-07 14:15:40 +0000
258@@ -28,6 +28,7 @@
259 except ImportError:
260 uuid = None
261
262+from storm.compat import json
263 from storm.exceptions import NoneError
264 from storm.variables import *
265 from storm.event import EventSystem
266@@ -825,13 +826,16 @@
267 self.assertEquals(variable.get(), value)
268
269
270-class PickleVariableTest(TestHelper):
271+class EncodedValueVariableTestMixin(object):
272+
273+ encoding = None
274+ variable_type = None
275
276 def test_get_set(self):
277 d = {"a": 1}
278- d_dump = pickle.dumps(d, -1)
279+ d_dump = self.encoding.dumps(d, -1)
280
281- variable = PickleVariable()
282+ variable = self.variable_type()
283
284 variable.set(d)
285 self.assertEquals(variable.get(), d)
286@@ -853,7 +857,7 @@
287 def test_pickle_events(self):
288 event = EventSystem(marker)
289
290- variable = PickleVariable(event=event, value_factory=list)
291+ variable = self.variable_type(event=event, value_factory=list)
292
293 changes = []
294 def changed(owner, variable, old_value, new_value, fromdb):
295@@ -887,6 +891,21 @@
296 self.assertEquals(changes, [(variable, None, ["a"], False)])
297
298
299+class PickleVariableTest(EncodedValueVariableTestMixin, TestHelper):
300+
301+ encoding = pickle
302+ variable_type = PickleVariable
303+
304+
305+class JSONVariableTest(EncodedValueVariableTestMixin, TestHelper):
306+
307+ encoding = json
308+ variable_type = JSONVariable
309+
310+ def is_supported(self):
311+ return json is not None
312+
313+
314 class ListVariableTest(TestHelper):
315
316 def test_get_set(self):

Subscribers

People subscribed via source and target branches

to status/vote changes: