##// END OF EJS Templates
improvements for extended json serializer
marcink -
r2160:3754ee8a beta
parent child Browse files
Show More
@@ -1,448 +1,476
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.compat
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Python backward compatibility functions and common libs
7 7
8 8
9 9 :created_on: Oct 7, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2010-2010 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import datetime
29 29 import functools
30 import decimal
30 31 from rhodecode import __platform__, PLATFORM_WIN
31 32
32 33 #==============================================================================
33 34 # json
34 35 #==============================================================================
35 36
36 37
38 def _is_aware(value):
39 """
40 Determines if a given datetime.time is aware.
41
42 The logic is described in Python's docs:
43 http://docs.python.org/library/datetime.html#datetime.tzinfo
44 """
45 return (value.tzinfo is not None
46 and value.tzinfo.utcoffset(value) is not None)
47
48
37 49 def _obj_dump(obj):
38 50 """
39 Custom function for dumping objects to JSON
51 Custom function for dumping objects to JSON, if obj has __json__ attribute
52 or method defined it will be used for serialization
40 53
41 54 :param obj:
42 55 """
43 DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
44 DATE_FORMAT = "%Y-%m-%d"
56
45 57 if isinstance(obj, complex):
46 58 return [obj.real, obj.imag]
59 # See "Date Time String Format" in the ECMA-262 specification.
60 # some code borrowed from django 1.4
47 61 elif isinstance(obj, datetime.datetime):
48 return obj.strftime(DATETIME_FORMAT)
62 r = obj.isoformat()
63 if obj.microsecond:
64 r = r[:23] + r[26:]
65 if r.endswith('+00:00'):
66 r = r[:-6] + 'Z'
67 return r
49 68 elif isinstance(obj, datetime.date):
50 return obj.strftime(DATE_FORMAT)
69 return obj.isoformat()
70 elif isinstance(obj, decimal.Decimal):
71 return str(obj)
72 elif isinstance(obj, datetime.time):
73 if _is_aware(obj):
74 raise ValueError("JSON can't represent timezone-aware times.")
75 r = obj.isoformat()
76 if obj.microsecond:
77 r = r[:12]
78 return r
51 79 elif isinstance(obj, set):
52 80 return list(obj)
53 81 elif isinstance(obj, OrderedDict):
54 82 return obj.as_dict()
55 83 elif hasattr(obj, '__json__'):
56 84 if callable(obj.__json__):
57 85 return obj.__json__()
58 86 else:
59 87 return obj.__json__
60 88 else:
61 89 raise NotImplementedError
62 90
63 91 try:
64 92 import json
65 93
66 94 # extended JSON encoder for json
67 95 class ExtendedEncoder(json.JSONEncoder):
68 96 def default(self, obj):
69 97 try:
70 98 return _obj_dump(obj)
71 99 except NotImplementedError:
72 100 pass
73 101 return json.JSONEncoder.default(self, obj)
74 102 # monkey-patch JSON encoder to use extended version
75 103 json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder)
76 104 except ImportError:
77 105 import simplejson as json
78 106
79 107 def extended_encode(obj):
80 108 try:
81 109 return _obj_dump(obj)
82 110 except NotImplementedError:
83 111 pass
84 112 raise TypeError("%r is not JSON serializable" % (obj,))
85 113 json.dumps = functools.partial(json.dumps, default=extended_encode)
86 114
87 115
88 116 #==============================================================================
89 117 # izip_longest
90 118 #==============================================================================
91 119 try:
92 120 from itertools import izip_longest
93 121 except ImportError:
94 122 import itertools
95 123
96 124 def izip_longest(*args, **kwds):
97 125 fillvalue = kwds.get("fillvalue")
98 126
99 127 def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
100 128 yield counter() # yields the fillvalue, or raises IndexError
101 129
102 130 fillers = itertools.repeat(fillvalue)
103 131 iters = [itertools.chain(it, sentinel(), fillers)
104 132 for it in args]
105 133 try:
106 134 for tup in itertools.izip(*iters):
107 135 yield tup
108 136 except IndexError:
109 137 pass
110 138
111 139
112 140 #==============================================================================
113 141 # OrderedDict
114 142 #==============================================================================
115 143
116 144 # Python Software Foundation License
117 145
118 146 # XXX: it feels like using the class with "is" and "is not" instead of "==" and
119 147 # "!=" should be faster.
120 148 class _Nil(object):
121 149
122 150 def __repr__(self):
123 151 return "nil"
124 152
125 153 def __eq__(self, other):
126 154 if (isinstance(other, _Nil)):
127 155 return True
128 156 else:
129 157 return NotImplemented
130 158
131 159 def __ne__(self, other):
132 160 if (isinstance(other, _Nil)):
133 161 return False
134 162 else:
135 163 return NotImplemented
136 164
137 165 _nil = _Nil()
138 166
139 167
140 168 class _odict(object):
141 169 """Ordered dict data structure, with O(1) complexity for dict operations
142 170 that modify one element.
143 171
144 172 Overwriting values doesn't change their original sequential order.
145 173 """
146 174
147 175 def _dict_impl(self):
148 176 return None
149 177
150 178 def __init__(self, data=(), **kwds):
151 179 """This doesn't accept keyword initialization as normal dicts to avoid
152 180 a trap - inside a function or method the keyword args are accessible
153 181 only as a dict, without a defined order, so their original order is
154 182 lost.
155 183 """
156 184 if kwds:
157 185 raise TypeError("__init__() of ordered dict takes no keyword "
158 186 "arguments to avoid an ordering trap.")
159 187 self._dict_impl().__init__(self)
160 188 # If you give a normal dict, then the order of elements is undefined
161 189 if hasattr(data, "iteritems"):
162 190 for key, val in data.iteritems():
163 191 self[key] = val
164 192 else:
165 193 for key, val in data:
166 194 self[key] = val
167 195
168 196 # Double-linked list header
169 197 def _get_lh(self):
170 198 dict_impl = self._dict_impl()
171 199 if not hasattr(self, '_lh'):
172 200 dict_impl.__setattr__(self, '_lh', _nil)
173 201 return dict_impl.__getattribute__(self, '_lh')
174 202
175 203 def _set_lh(self, val):
176 204 self._dict_impl().__setattr__(self, '_lh', val)
177 205
178 206 lh = property(_get_lh, _set_lh)
179 207
180 208 # Double-linked list tail
181 209 def _get_lt(self):
182 210 dict_impl = self._dict_impl()
183 211 if not hasattr(self, '_lt'):
184 212 dict_impl.__setattr__(self, '_lt', _nil)
185 213 return dict_impl.__getattribute__(self, '_lt')
186 214
187 215 def _set_lt(self, val):
188 216 self._dict_impl().__setattr__(self, '_lt', val)
189 217
190 218 lt = property(_get_lt, _set_lt)
191 219
192 220 def __getitem__(self, key):
193 221 return self._dict_impl().__getitem__(self, key)[1]
194 222
195 223 def __setitem__(self, key, val):
196 224 dict_impl = self._dict_impl()
197 225 try:
198 226 dict_impl.__getitem__(self, key)[1] = val
199 227 except KeyError:
200 228 new = [dict_impl.__getattribute__(self, 'lt'), val, _nil]
201 229 dict_impl.__setitem__(self, key, new)
202 230 if dict_impl.__getattribute__(self, 'lt') == _nil:
203 231 dict_impl.__setattr__(self, 'lh', key)
204 232 else:
205 233 dict_impl.__getitem__(
206 234 self, dict_impl.__getattribute__(self, 'lt'))[2] = key
207 235 dict_impl.__setattr__(self, 'lt', key)
208 236
209 237 def __delitem__(self, key):
210 238 dict_impl = self._dict_impl()
211 239 pred, _, succ = self._dict_impl().__getitem__(self, key)
212 240 if pred == _nil:
213 241 dict_impl.__setattr__(self, 'lh', succ)
214 242 else:
215 243 dict_impl.__getitem__(self, pred)[2] = succ
216 244 if succ == _nil:
217 245 dict_impl.__setattr__(self, 'lt', pred)
218 246 else:
219 247 dict_impl.__getitem__(self, succ)[0] = pred
220 248 dict_impl.__delitem__(self, key)
221 249
222 250 def __contains__(self, key):
223 251 return key in self.keys()
224 252
225 253 def __len__(self):
226 254 return len(self.keys())
227 255
228 256 def __str__(self):
229 257 pairs = ("%r: %r" % (k, v) for k, v in self.iteritems())
230 258 return "{%s}" % ", ".join(pairs)
231 259
232 260 def __repr__(self):
233 261 if self:
234 262 pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems())
235 263 return "odict([%s])" % ", ".join(pairs)
236 264 else:
237 265 return "odict()"
238 266
239 267 def get(self, k, x=None):
240 268 if k in self:
241 269 return self._dict_impl().__getitem__(self, k)[1]
242 270 else:
243 271 return x
244 272
245 273 def __iter__(self):
246 274 dict_impl = self._dict_impl()
247 275 curr_key = dict_impl.__getattribute__(self, 'lh')
248 276 while curr_key != _nil:
249 277 yield curr_key
250 278 curr_key = dict_impl.__getitem__(self, curr_key)[2]
251 279
252 280 iterkeys = __iter__
253 281
254 282 def keys(self):
255 283 return list(self.iterkeys())
256 284
257 285 def itervalues(self):
258 286 dict_impl = self._dict_impl()
259 287 curr_key = dict_impl.__getattribute__(self, 'lh')
260 288 while curr_key != _nil:
261 289 _, val, curr_key = dict_impl.__getitem__(self, curr_key)
262 290 yield val
263 291
264 292 def values(self):
265 293 return list(self.itervalues())
266 294
267 295 def iteritems(self):
268 296 dict_impl = self._dict_impl()
269 297 curr_key = dict_impl.__getattribute__(self, 'lh')
270 298 while curr_key != _nil:
271 299 _, val, next_key = dict_impl.__getitem__(self, curr_key)
272 300 yield curr_key, val
273 301 curr_key = next_key
274 302
275 303 def items(self):
276 304 return list(self.iteritems())
277 305
278 306 def sort(self, cmp=None, key=None, reverse=False):
279 307 items = [(k, v) for k, v in self.items()]
280 308 if cmp is not None:
281 309 items = sorted(items, cmp=cmp)
282 310 elif key is not None:
283 311 items = sorted(items, key=key)
284 312 else:
285 313 items = sorted(items, key=lambda x: x[1])
286 314 if reverse:
287 315 items.reverse()
288 316 self.clear()
289 317 self.__init__(items)
290 318
291 319 def clear(self):
292 320 dict_impl = self._dict_impl()
293 321 dict_impl.clear(self)
294 322 dict_impl.__setattr__(self, 'lh', _nil)
295 323 dict_impl.__setattr__(self, 'lt', _nil)
296 324
297 325 def copy(self):
298 326 return self.__class__(self)
299 327
300 328 def update(self, data=(), **kwds):
301 329 if kwds:
302 330 raise TypeError("update() of ordered dict takes no keyword "
303 331 "arguments to avoid an ordering trap.")
304 332 if hasattr(data, "iteritems"):
305 333 data = data.iteritems()
306 334 for key, val in data:
307 335 self[key] = val
308 336
309 337 def setdefault(self, k, x=None):
310 338 try:
311 339 return self[k]
312 340 except KeyError:
313 341 self[k] = x
314 342 return x
315 343
316 344 def pop(self, k, x=_nil):
317 345 try:
318 346 val = self[k]
319 347 del self[k]
320 348 return val
321 349 except KeyError:
322 350 if x == _nil:
323 351 raise
324 352 return x
325 353
326 354 def popitem(self):
327 355 try:
328 356 dict_impl = self._dict_impl()
329 357 key = dict_impl.__getattribute__(self, 'lt')
330 358 return key, self.pop(key)
331 359 except KeyError:
332 360 raise KeyError("'popitem(): ordered dictionary is empty'")
333 361
334 362 def riterkeys(self):
335 363 """To iterate on keys in reversed order.
336 364 """
337 365 dict_impl = self._dict_impl()
338 366 curr_key = dict_impl.__getattribute__(self, 'lt')
339 367 while curr_key != _nil:
340 368 yield curr_key
341 369 curr_key = dict_impl.__getitem__(self, curr_key)[0]
342 370
343 371 __reversed__ = riterkeys
344 372
345 373 def rkeys(self):
346 374 """List of the keys in reversed order.
347 375 """
348 376 return list(self.riterkeys())
349 377
350 378 def ritervalues(self):
351 379 """To iterate on values in reversed order.
352 380 """
353 381 dict_impl = self._dict_impl()
354 382 curr_key = dict_impl.__getattribute__(self, 'lt')
355 383 while curr_key != _nil:
356 384 curr_key, val, _ = dict_impl.__getitem__(self, curr_key)
357 385 yield val
358 386
359 387 def rvalues(self):
360 388 """List of the values in reversed order.
361 389 """
362 390 return list(self.ritervalues())
363 391
364 392 def riteritems(self):
365 393 """To iterate on (key, value) in reversed order.
366 394 """
367 395 dict_impl = self._dict_impl()
368 396 curr_key = dict_impl.__getattribute__(self, 'lt')
369 397 while curr_key != _nil:
370 398 pred_key, val, _ = dict_impl.__getitem__(self, curr_key)
371 399 yield curr_key, val
372 400 curr_key = pred_key
373 401
374 402 def ritems(self):
375 403 """List of the (key, value) in reversed order.
376 404 """
377 405 return list(self.riteritems())
378 406
379 407 def firstkey(self):
380 408 if self:
381 409 return self._dict_impl().__getattribute__(self, 'lh')
382 410 else:
383 411 raise KeyError("'firstkey(): ordered dictionary is empty'")
384 412
385 413 def lastkey(self):
386 414 if self:
387 415 return self._dict_impl().__getattribute__(self, 'lt')
388 416 else:
389 417 raise KeyError("'lastkey(): ordered dictionary is empty'")
390 418
391 419 def as_dict(self):
392 420 return self._dict_impl()(self.items())
393 421
394 422 def _repr(self):
395 423 """_repr(): low level repr of the whole data contained in the odict.
396 424 Useful for debugging.
397 425 """
398 426 dict_impl = self._dict_impl()
399 427 form = "odict low level repr lh,lt,data: %r, %r, %s"
400 428 return form % (dict_impl.__getattribute__(self, 'lh'),
401 429 dict_impl.__getattribute__(self, 'lt'),
402 430 dict_impl.__repr__(self))
403 431
404 432
405 433 class OrderedDict(_odict, dict):
406 434
407 435 def _dict_impl(self):
408 436 return dict
409 437
410 438
411 439 #==============================================================================
412 440 # OrderedSet
413 441 #==============================================================================
414 442 from sqlalchemy.util import OrderedSet
415 443
416 444
417 445 #==============================================================================
418 446 # kill FUNCTIONS
419 447 #==============================================================================
420 448 if __platform__ in PLATFORM_WIN:
421 449 import ctypes
422 450
423 451 def kill(pid, sig):
424 452 """kill function for Win32"""
425 453 kernel32 = ctypes.windll.kernel32
426 454 handle = kernel32.OpenProcess(1, 0, pid)
427 455 return (0 != kernel32.TerminateProcess(handle, 0))
428 456
429 457 else:
430 458 kill = os.kill
431 459
432 460
433 461 #==============================================================================
434 462 # itertools.product
435 463 #==============================================================================
436 464
437 465 try:
438 466 from itertools import product
439 467 except ImportError:
440 468 def product(*args, **kwds):
441 469 # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
442 470 # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
443 471 pools = map(tuple, args) * kwds.get('repeat', 1)
444 472 result = [[]]
445 473 for pool in pools:
446 474 result = [x + [y] for x in result for y in pool]
447 475 for prod in result:
448 476 yield tuple(prod)
General Comments 0
You need to be logged in to leave comments. Login now