Package couchdbkit :: Package schema :: Module properties
[hide private]
[frames] | no frames]

Source Code for Module couchdbkit.schema.properties

   1  # -*- coding: utf-8 - 
   2  # 
   3  # This file is part of couchdbkit released under the MIT license. 
   4  # See the NOTICE for more information. 
   5   
   6  """ properties used by Document object """ 
   7   
   8  import decimal 
   9  import datetime 
  10  import re 
  11  import time 
  12   
  13  try: 
  14      from collections import MutableSet, Iterable 
15 16 - def is_iterable(c):
17 return isinstance(c, Iterable)
18 19 support_setproperty = True 20 except ImportError: 21 support_setproperty = False 22 23 from couchdbkit.exceptions import BadValueError 24 25 __all__ = ['ALLOWED_PROPERTY_TYPES', 'Property', 'StringProperty', 26 'IntegerProperty', 'DecimalProperty', 'BooleanProperty', 27 'FloatProperty', 'DateTimeProperty', 'DateProperty', 28 'TimeProperty', 'DictProperty', 'ListProperty', 29 'StringListProperty', 30 'dict_to_json', 'list_to_json', 31 'value_to_json', 'MAP_TYPES_PROPERTIES', 'value_to_python', 32 'dict_to_python', 'list_to_python', 'convert_property', 33 'value_to_property', 'LazyDict', 'LazyList'] 34 35 if support_setproperty: 36 __all__ += ['SetProperty', 'LasySet'] 37 38 ALLOWED_PROPERTY_TYPES = set([ 39 basestring, 40 str, 41 unicode, 42 bool, 43 int, 44 long, 45 float, 46 datetime.datetime, 47 datetime.date, 48 datetime.time, 49 decimal.Decimal, 50 dict, 51 list, 52 set, 53 type(None) 54 ]) 55 56 re_date = re.compile('^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$') 57 re_time = re.compile('^([01]\d|2[0-3])\D?([0-5]\d)\D?([0-5]\d)?\D?(\d{3})?$') 58 re_datetime = re.compile('^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])(\D?([01]\d|2[0-3])\D?([0-5]\d)\D?([0-5]\d)?\D?(\d{3})?([zZ]|([\+-])([01]\d|2[0-3])\D?([0-5]\d)?)?)?$') 59 re_decimal = re.compile('^(\d+)\.(\d+)$')
60 61 -class Property(object):
62 """ Property base which all other properties 63 inherit.""" 64 creation_counter = 0 65
66 - def __init__(self, verbose_name=None, name=None, 67 default=None, required=False, validators=None, 68 choices=None):
69 """ Default constructor for a property. 70 71 :param verbose_name: str, verbose name of field, could 72 be use for description 73 :param name: str, name of field 74 :param default: default value 75 :param required: True if field is required, default is False 76 :param validators: list of callable or callable, field validators 77 function that are executed when document is saved. 78 """ 79 self.verbose_name = verbose_name 80 self.name = name 81 self.default = default 82 self.required = required 83 self.validators = validators 84 self.choices = choices 85 self.creation_counter = Property.creation_counter 86 Property.creation_counter += 1
87
88 - def __property_config__(self, document_class, property_name):
89 self.document_class = document_class 90 if self.name is None: 91 self.name = property_name
92
93 - def __property_init__(self, document_instance, value):
94 """ method used to set value of the property when 95 we create the document. Don't check required. """ 96 if value is not None: 97 value = self.to_json(self.validate(value, required=False)) 98 document_instance._doc[self.name] = value
99
100 - def __get__(self, document_instance, document_class):
101 if document_instance is None: 102 return self 103 104 value = document_instance._doc.get(self.name) 105 if value is not None: 106 value = self._to_python(value) 107 108 return value
109
110 - def __set__(self, document_instance, value):
111 value = self.validate(value, required=False) 112 document_instance._doc[self.name] = self._to_json(value)
113
114 - def __delete__(self, document_instance):
115 pass
116
117 - def default_value(self):
118 """ return default value """ 119 120 default = self.default 121 if callable(default): 122 default = default() 123 return default
124
125 - def validate(self, value, required=True):
126 """ validate value """ 127 if required and self.empty(value): 128 if self.required: 129 raise BadValueError("Property %s is required." % self.name) 130 else: 131 if self.choices and value is not None: 132 if isinstance(self.choices, list): choice_list = self.choices 133 if isinstance(self.choices, dict): choice_list = self.choices.keys() 134 if isinstance(self.choices, tuple): choice_list = [key for (key, name) in self.choices] 135 136 if value not in choice_list: 137 raise BadValueError('Property %s is %r; must be one of %r' % ( 138 self.name, value, choice_list)) 139 if self.validators: 140 if isinstance(self.validators, (list, tuple,)): 141 for validator in self.validators: 142 if callable(validator): 143 validator(value) 144 elif callable(self.validators): 145 self.validators(value) 146 return value
147
148 - def empty(self, value):
149 """ test if value is empty """ 150 return not value or value is None
151
152 - def _to_python(self, value):
153 if value == None: 154 return value 155 return self.to_python(value)
156
157 - def _to_json(self, value):
158 if value == None: 159 return value 160 return self.to_json(value)
161
162 - def to_python(self, value):
163 """ convert to python type """ 164 return unicode(value)
165
166 - def to_json(self, value):
167 """ convert to json, Converted value is saved in couchdb. """ 168 return self.to_python(value)
169 170 data_type = None
171
172 -class StringProperty(Property):
173 """ string property str or unicode property 174 175 *Value type*: unicode 176 """ 177 178 to_python = unicode 179
180 - def validate(self, value, required=True):
181 value = super(StringProperty, self).validate(value, 182 required=required) 183 184 if value is None: 185 return value 186 187 if not isinstance(value, basestring): 188 raise BadValueError( 189 'Property %s must be unicode or str instance, not a %s' % (self.name, type(value).__name__)) 190 return value
191 192 data_type = unicode
193
194 -class IntegerProperty(Property):
195 """ Integer property. map to int 196 197 *Value type*: int 198 """ 199 to_python = int 200
201 - def empty(self, value):
202 return value is None
203
204 - def validate(self, value, required=True):
205 value = super(IntegerProperty, self).validate(value, 206 required=required) 207 208 if value is None: 209 return value 210 211 if value is not None and not isinstance(value, (int, long,)): 212 raise BadValueError( 213 'Property %s must be %s or long instance, not a %s' 214 % (self.name, type(self.data_type).__name__, 215 type(value).__name__)) 216 217 return value
218 219 data_type = int
220 LongProperty = IntegerProperty
221 222 -class FloatProperty(Property):
223 """ Float property, map to python float 224 225 *Value type*: float 226 """ 227 to_python = float 228 data_type = float 229
230 - def validate(self, value, required=True):
231 value = super(FloatProperty, self).validate(value, 232 required=required) 233 234 if value is None: 235 return value 236 237 if not isinstance(value, float): 238 raise BadValueError( 239 'Property %s must be float instance, not a %s' 240 % (self.name, type(value).__name__)) 241 242 return value
243 Number = FloatProperty
244 245 -class BooleanProperty(Property):
246 """ Boolean property, map to python bool 247 248 *ValueType*: bool 249 """ 250 to_python = bool 251 data_type = bool 252
253 - def validate(self, value, required=True):
254 value = super(BooleanProperty, self).validate(value, 255 required=required) 256 257 if value is None: 258 return value 259 260 if value is not None and not isinstance(value, bool): 261 raise BadValueError( 262 'Property %s must be bool instance, not a %s' 263 % (self.name, type(value).__name__)) 264 265 return value
266
267 - def empty(self, value):
268 """test if boolean is empty""" 269 return value is None
270
271 -class DecimalProperty(Property):
272 """ Decimal property, map to Decimal python object 273 274 *ValueType*: decimal.Decimal 275 """ 276 data_type = decimal.Decimal 277
278 - def to_python(self, value):
279 return decimal.Decimal(value)
280
281 - def to_json(self, value):
282 return unicode(value)
283
284 -class DateTimeProperty(Property):
285 """DateTime property. It convert iso3339 string 286 to python and vice-versa. Map to datetime.datetime 287 object. 288 289 *ValueType*: datetime.datetime 290 """ 291
292 - def __init__(self, verbose_name=None, auto_now=False, auto_now_add=False, 293 **kwds):
294 super(DateTimeProperty, self).__init__(verbose_name, **kwds) 295 self.auto_now = auto_now 296 self.auto_now_add = auto_now_add
297
298 - def validate(self, value, required=True):
299 value = super(DateTimeProperty, self).validate(value, required=required) 300 301 if value is None: 302 return value 303 304 if value and not isinstance(value, self.data_type): 305 raise BadValueError('Property %s must be a %s, current is %s' % 306 (self.name, self.data_type.__name__, type(value).__name__)) 307 return value
308
309 - def default_value(self):
310 if self.auto_now or self.auto_now_add: 311 return self.now() 312 return Property.default_value(self)
313
314 - def to_python(self, value):
315 if isinstance(value, basestring): 316 try: 317 value = value.split('.', 1)[0] # strip out microseconds 318 value = value[0:19] # remove timezone 319 value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S') 320 except ValueError, e: 321 raise ValueError('Invalid ISO date/time %r [%s]' % 322 (value, str(e))) 323 return value
324
325 - def to_json(self, value):
326 if self.auto_now: 327 value = self.now() 328 329 if value is None: 330 return value 331 return value.replace(microsecond=0).isoformat() + 'Z'
332 333 data_type = datetime.datetime 334 335 @staticmethod
336 - def now():
337 return datetime.datetime.utcnow()
338
339 -class DateProperty(DateTimeProperty):
340 """ Date property, like DateTime property but only 341 for Date. Map to datetime.date object 342 343 *ValueType*: datetime.date 344 """ 345 data_type = datetime.date 346 347 @staticmethod
348 - def now():
349 return datetime.datetime.now().date()
350
351 - def to_python(self, value):
352 if isinstance(value, basestring): 353 try: 354 value = datetime.date(*time.strptime(value, '%Y-%m-%d')[:3]) 355 except ValueError, e: 356 raise ValueError('Invalid ISO date %r [%s]' % (value, 357 str(e))) 358 return value
359
360 - def to_json(self, value):
361 if value is None: 362 return value 363 return value.isoformat()
364
365 -class TimeProperty(DateTimeProperty):
366 """ Date property, like DateTime property but only 367 for time. Map to datetime.time object 368 369 *ValueType*: datetime.time 370 """ 371 372 data_type = datetime.time 373 374 @staticmethod
375 - def now(self):
376 return datetime.datetime.now().time()
377
378 - def to_python(self, value):
379 if isinstance(value, basestring): 380 try: 381 value = value.split('.', 1)[0] # strip out microseconds 382 value = datetime.time(*time.strptime(value, '%H:%M:%S')[3:6]) 383 except ValueError, e: 384 raise ValueError('Invalid ISO time %r [%s]' % (value, 385 str(e))) 386 return value
387
388 - def to_json(self, value):
389 if value is None: 390 return value 391 return value.replace(microsecond=0).isoformat()
392
393 394 -class DictProperty(Property):
395 """ A property that stores a dict of things""" 396
397 - def __init__(self, verbose_name=None, default=None, 398 required=False, **kwds):
399 """ 400 :args verbose_name: Optional verbose name. 401 :args default: Optional default value; if omitted, an empty list is used. 402 :args**kwds: Optional additional keyword arguments, passed to base class. 403 404 Note that the only permissible value for 'required' is True. 405 """ 406 407 if default is None: 408 default = {} 409 410 Property.__init__(self, verbose_name, default=default, 411 required=required, **kwds)
412 413 data_type = dict 414
415 - def validate(self, value, required=True):
416 value = super(DictProperty, self).validate(value, required=required) 417 if value and value is not None: 418 if not isinstance(value, dict): 419 raise BadValueError('Property %s must be a dict' % self.name) 420 value = self.validate_dict_contents(value) 421 return value
422
423 - def validate_dict_contents(self, value):
424 try: 425 value = validate_dict_content(value) 426 except BadValueError: 427 raise BadValueError( 428 'Items of %s dict must all be in %s' % 429 (self.name, ALLOWED_PROPERTY_TYPES)) 430 return value
431
432 - def default_value(self):
433 """Default value for list. 434 435 Because the property supplied to 'default' is a static value, 436 that value must be shallow copied to prevent all fields with 437 default values from sharing the same instance. 438 439 Returns: 440 Copy of the default value. 441 """ 442 value = super(DictProperty, self).default_value() 443 if value is None: 444 value = {} 445 return dict(value)
446
447 - def to_python(self, value):
448 return LazyDict(value)
449
450 - def to_json(self, value):
451 return value_to_json(value)
452
453 454 455 -class ListProperty(Property):
456 """A property that stores a list of things. 457 458 """
459 - def __init__(self, verbose_name=None, default=None, 460 required=False, item_type=None, **kwds):
461 """Construct ListProperty. 462 463 464 :args verbose_name: Optional verbose name. 465 :args default: Optional default value; if omitted, an empty list is used. 466 :args**kwds: Optional additional keyword arguments, passed to base class. 467 468 469 """ 470 if default is None: 471 default = [] 472 473 if item_type is not None and item_type not in ALLOWED_PROPERTY_TYPES: 474 raise ValueError('item_type %s not in %s' % (item_type, ALLOWED_PROPERTY_TYPES)) 475 self.item_type = item_type 476 477 Property.__init__(self, verbose_name, default=default, 478 required=required, **kwds)
479 480 data_type = list 481
482 - def validate(self, value, required=True):
483 value = super(ListProperty, self).validate(value, required=required) 484 if value and value is not None: 485 if not isinstance(value, list): 486 raise BadValueError('Property %s must be a list' % self.name) 487 value = self.validate_list_contents(value) 488 return value
489
490 - def validate_list_contents(self, value):
491 value = validate_list_content(value, item_type=self.item_type) 492 try: 493 value = validate_list_content(value, item_type=self.item_type) 494 except BadValueError: 495 raise BadValueError( 496 'Items of %s list must all be in %s' % 497 (self.name, ALLOWED_PROPERTY_TYPES)) 498 return value
499
500 - def default_value(self):
501 """Default value for list. 502 503 Because the property supplied to 'default' is a static value, 504 that value must be shallow copied to prevent all fields with 505 default values from sharing the same instance. 506 507 Returns: 508 Copy of the default value. 509 """ 510 value = super(ListProperty, self).default_value() 511 if value is None: 512 value = [] 513 return list(value)
514
515 - def to_python(self, value):
516 return LazyList(value, item_type=self.item_type)
517
518 - def to_json(self, value):
519 return value_to_json(value, item_type=self.item_type)
520
521 522 -class StringListProperty(ListProperty):
523 """ shorthand for list that should containe only unicode""" 524
525 - def __init__(self, verbose_name=None, default=None, 526 required=False, **kwds):
527 super(StringListProperty, self).__init__(verbose_name=verbose_name, 528 default=default, required=required, item_type=basestring, **kwds)
529
530 531 532 533 534 # dict proxy 535 536 -class LazyDict(dict):
537 """ object to make sure we keep updated of dict 538 in _doc. We just override a dict and maintain change in 539 doc reference (doc[keyt] obviously). 540 541 if init_vals is specified, doc is overwritten 542 with the dict given. Otherwise, the values already in 543 doc are used. 544 """ 545
546 - def __init__(self, doc, item_type=None, init_vals=None):
547 dict.__init__(self) 548 self.item_type = item_type 549 550 self.doc = doc 551 if init_vals is None: 552 self._wrap() 553 else: 554 for key, value in init_vals.items(): 555 self[key] = value
556
557 - def _wrap(self):
558 for key, json_value in self.doc.items(): 559 if isinstance(json_value, dict): 560 value = LazyDict(json_value, item_type=self.item_type) 561 elif isinstance(json_value, list): 562 value = LazyList(json_value, item_type=self.item_type) 563 else: 564 value = value_to_python(json_value, self.item_type) 565 dict.__setitem__(self, key, value)
566
567 - def __setitem__(self, key, value):
568 if isinstance(value, dict): 569 self.doc[key] = {} 570 value = LazyDict(self.doc[key], item_type=self.item_type, init_vals=value) 571 elif isinstance(value, list): 572 self.doc[key] = [] 573 value = LazyList(self.doc[key], item_type=self.item_type, init_vals=value) 574 else: 575 self.doc.update({key: value_to_json(value, item_type=self.item_type) }) 576 super(LazyDict, self).__setitem__(key, value)
577
578 - def __delitem__(self, key):
579 del self.doc[key] 580 super(LazyDict, self).__delitem__(key)
581
582 - def pop(self, key, *args):
583 default = len(args) == 1 584 if default: 585 self.doc.pop(key, args[-1]) 586 return super(LazyDict, self).pop(key, args[-1]) 587 self.doc.pop(key) 588 return super(LazyDict, self).pop(key)
589
590 - def setdefault(self, key, default):
591 if key in self: 592 return self[key] 593 self.doc.setdefault(key, value_to_json(default, item_type=self.item_type)) 594 super(LazyDict, self).setdefault(key, default) 595 return default
596
597 - def update(self, value):
598 for k, v in value.items(): 599 self[k] = v
600
601 - def popitem(self, value):
602 new_value = super(LazyDict, self).popitem(value) 603 self.doc.popitem(value_to_json(value, item_type=self.item_type)) 604 return new_value
605
606 - def clear(self):
607 self.doc.clear() 608 super(LazyDict, self).clear()
609
610 611 -class LazyList(list):
612 """ object to make sure we keep update of list 613 in _doc. We just override a list and maintain change in 614 doc reference (doc[index] obviously). 615 616 if init_vals is specified, doc is overwritten 617 with the list given. Otherwise, the values already in 618 doc are used. 619 """ 620
621 - def __init__(self, doc, item_type=None, init_vals=None):
622 list.__init__(self) 623 624 self.item_type = item_type 625 self.doc = doc 626 if init_vals is None: 627 # just wrap the current values 628 self._wrap() 629 else: 630 # initialize this list and the underlying list 631 # with the values given. 632 del self.doc[:] 633 for item in init_vals: 634 self.append(item)
635
636 - def _wrap(self):
637 for json_value in self.doc: 638 if isinstance(json_value, dict): 639 value = LazyDict(json_value, item_type=self.item_type) 640 elif isinstance(json_value, list): 641 value = LazyList(json_value, item_type=self.item_type) 642 else: 643 value = value_to_python(json_value, self.item_type) 644 list.append(self, value)
645
646 - def __delitem__(self, index):
647 del self.doc[index] 648 list.__delitem__(self, index)
649
650 - def __setitem__(self, index, value):
651 if isinstance(value, dict): 652 self.doc[index] = {} 653 value = LazyDict(self.doc[index], item_type=self.item_type, init_vals=value) 654 elif isinstance(value, list): 655 self.doc[index] = [] 656 value = LazyList(self.doc[index], item_type=self.item_type, init_vals=value) 657 else: 658 self.doc[index] = value_to_json(value, item_type=self.item_type) 659 list.__setitem__(self, index, value)
660 661
662 - def __delslice__(self, i, j):
663 del self.doc[i:j] 664 list.__delslice__(self, i, j)
665
666 - def __getslice__(self, i, j):
667 return LazyList(self.doc[i:j], self.item_type)
668
669 - def __setslice__(self, i, j, seq):
670 self.doc[i:j] = (value_to_json(v, item_type=self.item_type) for v in seq) 671 list.__setslice__(self, i, j, seq)
672
673 - def __contains__(self, value):
674 jvalue = value_to_json(value) 675 for m in self.doc: 676 if m == jvalue: return True 677 return False
678
679 - def append(self, *args, **kwargs):
680 if args: 681 assert len(args) == 1 682 value = args[0] 683 else: 684 value = kwargs 685 686 index = len(self) 687 if isinstance(value, dict): 688 self.doc.append({}) 689 value = LazyDict(self.doc[index], item_type=self.item_type, init_vals=value) 690 elif isinstance(value, list): 691 self.doc.append([]) 692 value = LazyList(self.doc[index], item_type=self.item_type, init_vals=value) 693 else: 694 self.doc.append(value_to_json(value, item_type=self.item_type)) 695 super(LazyList, self).append(value)
696
697 - def extend(self, x):
698 self.doc.extend( 699 [value_to_json(v, item_type=self.item_type) for v in x]) 700 super(LazyList, self).extend(x)
701
702 - def index(self, x, *args):
703 x = value_to_json(x, item_type=self.item_type) 704 return self.doc.index(x)
705
706 - def insert(self, i, x):
707 self.__setslice__(i, i, [x])
708
709 - def pop(self, i=-1):
710 del self.doc[i] 711 v = super(LazyList, self).pop(i) 712 return value_to_python(v, item_type=self.item_type)
713
714 - def remove(self, x):
715 del self[self.index(x)]
716
717 - def sort(self, cmp=None, key=None, reverse=False):
718 self.doc.sort(cmp, key, reverse) 719 list.sort(self, cmp, key, reverse)
720
721 - def reverse(self):
722 self.doc.reverse() 723 list.reverse(self)
724 725 if support_setproperty:
726 - class SetProperty(Property):
727 """A property that stores a Python set as a list of unique 728 elements. 729 730 Note that Python set operations like union that return a set 731 object do not alter list that will be stored with the next save, 732 while operations like update that change a set object in-place do 733 keep the list in sync. 734 """
735 - def __init__(self, verbose_name=None, default=None, required=None, 736 item_type=None, **kwds):
737 """Construct SetProperty. 738 739 :args verbose_name: Optional verbose name. 740 741 :args default: Optional default value; if omitted, an empty 742 set is used. 743 744 :args required: True if field is required, default is False. 745 746 :args item_type: Optional data type of items that set 747 contains. Used to assist with JSON 748 serialization/deserialization when data is 749 stored/retireved. 750 751 :args **kwds: Optional additional keyword arguments, passed to 752 base class. 753 """ 754 if default is None: 755 default = set() 756 if item_type is not None and item_type not in ALLOWED_PROPERTY_TYPES: 757 raise ValueError('item_type %s not in %s' 758 % (item_type, ALLOWED_PROPERTY_TYPES)) 759 self.item_type = item_type 760 super(SetProperty, self).__init__( 761 verbose_name=verbose_name, default=default, required=required, 762 **kwds)
763 764 data_type = set 765
766 - def validate(self, value, required=True):
767 value = super(SetProperty, self).validate(value, required=required) 768 if value and value is not None: 769 if not isinstance(value, MutableSet): 770 raise BadValueError('Property %s must be a set' % self.name) 771 value = self.validate_set_contents(value) 772 return value
773
774 - def validate_set_contents(self, value):
775 try: 776 value = validate_set_content(value, item_type=self.item_type) 777 except BadValueError: 778 raise BadValueError( 779 'Items of %s set must all be in %s' % 780 (self.name, ALLOWED_PROPERTY_TYPES)) 781 return value
782
783 - def default_value(self):
784 """Return default value for set. 785 786 Because the property supplied to 'default' is a static value, 787 that value must be shallow copied to prevent all fields with 788 default values from sharing the same instance. 789 790 Returns: 791 Copy of the default value. 792 """ 793 value = super(SetProperty, self).default_value() 794 if value is None: 795 return set() 796 return value.copy()
797
798 - def to_python(self, value):
799 return LazySet(value, item_type=self.item_type)
800
801 - def to_json(self, value):
802 return value_to_json(value, item_type=self.item_type)
803
804 805 - class LazySet(MutableSet):
806 """Object to make sure that we keep set and _doc synchronized. 807 808 We sub-class MutableSet and maintain changes in doc. 809 810 Note that methods like union that return a set object do not 811 alter _doc, while methods like update that change a set object 812 in-place do keep _doc in sync. 813 """
814 - def _map_named_operation(opname):
815 fn = getattr(MutableSet, opname) 816 if hasattr(fn, 'im_func'): 817 fn = fn.im_func 818 def method(self, other, fn=fn): 819 if not isinstance(other, MutableSet): 820 other = self._from_iterable(other) 821 return fn(self, other)
822 return method
823 824 issubset = _map_named_operation('__le__') 825 issuperset = _map_named_operation('__ge__') 826 symmetric_difference = _map_named_operation('__xor__') 827
828 - def __init__(self, doc, item_type=None):
829 self.item_type = item_type 830 self.doc = doc 831 self.elements = set(value_to_python(value, self.item_type) 832 for value in self.doc)
833
834 - def __repr__(self):
835 return '%s(%r)' % (type(self).__name__, list(self))
836 837 @classmethod
838 - def _from_iterable(cls, it):
839 return cls(it)
840
841 - def __iand__(self, iterator):
842 for value in (self.elements - iterator): 843 self.elements.discard(value) 844 return self
845
846 - def __iter__(self):
847 return iter(element for element in self.elements)
848
849 - def __len__(self):
850 return len(self.elements)
851
852 - def __contains__(self, item):
853 return item in self.elements
854
855 - def __xor__(self, other):
856 if not isinstance(other, MutableSet): 857 if not is_iterable(other): 858 return NotImplemented 859 other = self._from_iterable(other) 860 return (self.elements - other) | (other - self.elements)
861
862 - def __gt__(self, other):
863 if not isinstance(other, MutableSet): 864 return NotImplemented 865 return other < self.elements
866
867 - def __ge__(self, other):
868 if not isinstance(other, MutableSet): 869 return NotImplemented 870 return other <= self.elements
871
872 - def __ne__(self, other):
873 return not (self.elements == other)
874
875 - def add(self, value):
876 self.elements.add(value) 877 if value not in self.doc: 878 self.doc.append(value_to_json(value, item_type=self.item_type))
879
880 - def copy(self):
881 return self.elements.copy()
882
883 - def difference(self, other, *args):
884 return self.elements.difference(other, *args)
885
886 - def difference_update(self, other, *args):
887 for value in other: 888 self.discard(value) 889 for arg in args: 890 self.difference_update(arg)
891
892 - def discard(self, value):
893 self.elements.discard(value) 894 try: 895 self.doc.remove(value) 896 except ValueError: 897 pass
898
899 - def intersection(self, other, *args):
900 return self.elements.intersection(other, *args)
901
902 - def intersection_update(self, other, *args):
903 if not isinstance(other, MutableSet): 904 other = set(other) 905 for value in self.elements - other: 906 self.discard(value) 907 for arg in args: 908 self.intersection_update(arg)
909
910 - def symmetric_difference_update(self, other):
911 if not isinstance(other, MutableSet): 912 other = set(other) 913 for value in other: 914 if value in self.elements: 915 self.discard(value) 916 else: 917 self.add(value)
918
919 - def union(self, other, *args):
920 return self.elements.union(other, *args)
921
922 - def update(self, other, *args):
923 self.elements.update(other, *args) 924 for element in self.elements: 925 if element not in self.doc: 926 self.doc.append( 927 value_to_json(element, item_type=self.item_type))
928 929 # some mapping 930 931 MAP_TYPES_PROPERTIES = { 932 decimal.Decimal: DecimalProperty, 933 datetime.datetime: DateTimeProperty, 934 datetime.date: DateProperty, 935 datetime.time: TimeProperty, 936 str: StringProperty, 937 unicode: StringProperty, 938 bool: BooleanProperty, 939 int: IntegerProperty, 940 long: LongProperty, 941 float: FloatProperty, 942 list: ListProperty, 943 dict: DictProperty 944 } 945 946 if support_setproperty: 947 MAP_TYPES_PROPERTIES[set] = SetProperty
948 949 -def convert_property(value):
950 """ convert a value to json from Property._to_json """ 951 if type(value) in MAP_TYPES_PROPERTIES: 952 prop = MAP_TYPES_PROPERTIES[type(value)]() 953 value = prop.to_json(value) 954 return value
955
956 957 -def value_to_property(value):
958 """ Convert value in a Property object """ 959 if type(value) in MAP_TYPES_PROPERTIES: 960 prop = MAP_TYPES_PROPERTIES[type(value)]() 961 return prop 962 else: 963 return value
964
965 # utilities functions 966 967 -def validate_list_content(value, item_type=None):
968 """ validate type of values in a list """ 969 return [validate_content(item, item_type=item_type) for item in value]
970
971 -def validate_dict_content(value, item_type=None):
972 """ validate type of values in a dict """ 973 return dict([(k, validate_content(v, 974 item_type=item_type)) for k, v in value.iteritems()])
975
976 -def validate_set_content(value, item_type=None):
977 """ validate type of values in a set """ 978 return set(validate_content(item, item_type=item_type) for item in value)
979
980 -def validate_content(value, item_type=None):
981 """ validate a value. test if value is in supported types """ 982 if isinstance(value, list): 983 value = validate_list_content(value, item_type=item_type) 984 elif isinstance(value, dict): 985 value = validate_dict_content(value, item_type=item_type) 986 elif item_type is not None and not isinstance(value, item_type): 987 raise BadValueError( 988 'Items must all be in %s' % item_type) 989 elif type(value) not in ALLOWED_PROPERTY_TYPES: 990 raise BadValueError( 991 'Items must all be in %s' % 992 (ALLOWED_PROPERTY_TYPES)) 993 return value
994
995 -def dict_to_json(value, item_type=None):
996 """ convert a dict to json """ 997 return dict([(k, value_to_json(v, item_type=item_type)) for k, v in value.iteritems()])
998
999 -def list_to_json(value, item_type=None):
1000 """ convert a list to json """ 1001 return [value_to_json(item, item_type=item_type) for item in value]
1002
1003 -def value_to_json(value, item_type=None):
1004 """ convert a value to json using appropriate regexp. 1005 For Dates we use ISO 8601. Decimal are converted to string. 1006 1007 """ 1008 if isinstance(value, datetime.datetime) and is_type_ok(item_type, datetime.datetime): 1009 value = value.replace(microsecond=0).isoformat() + 'Z' 1010 elif isinstance(value, datetime.date) and is_type_ok(item_type, datetime.date): 1011 value = value.isoformat() 1012 elif isinstance(value, datetime.time) and is_type_ok(item_type, datetime.time): 1013 value = value.replace(microsecond=0).isoformat() 1014 elif isinstance(value, decimal.Decimal) and is_type_ok(item_type, decimal.Decimal): 1015 value = unicode(value) 1016 elif isinstance(value, (list, MutableSet)): 1017 value = list_to_json(value, item_type) 1018 elif isinstance(value, dict): 1019 value = dict_to_json(value, item_type) 1020 return value
1021
1022 -def is_type_ok(item_type, value_type):
1023 return item_type is None or item_type == value_type
1024
1025 1026 -def value_to_python(value, item_type=None):
1027 """ convert a json value to python type using regexp. values converted 1028 have been put in json via `value_to_json` . 1029 """ 1030 data_type = None 1031 if isinstance(value, basestring): 1032 if re_date.match(value) and is_type_ok(item_type, datetime.date): 1033 data_type = datetime.date 1034 elif re_time.match(value) and is_type_ok(item_type, datetime.time): 1035 data_type = datetime.time 1036 elif re_datetime.match(value) and is_type_ok(item_type, datetime.datetime): 1037 data_type = datetime.datetime 1038 elif re_decimal.match(value) and is_type_ok(item_type, decimal.Decimal): 1039 data_type = decimal.Decimal 1040 if data_type is not None: 1041 prop = MAP_TYPES_PROPERTIES[data_type]() 1042 try: 1043 #sometimes regex fail so return value 1044 value = prop.to_python(value) 1045 except: 1046 pass 1047 elif isinstance(value, (list, MutableSet)): 1048 value = list_to_python(value, item_type=item_type) 1049 elif isinstance(value, dict): 1050 value = dict_to_python(value, item_type=item_type) 1051 return value
1052
1053 -def list_to_python(value, item_type=None):
1054 """ convert a list of json values to python list """ 1055 return [value_to_python(item, item_type=item_type) for item in value]
1056
1057 -def dict_to_python(value, item_type=None):
1058 """ convert a json object values to python dict """ 1059 return dict([(k, value_to_python(v, item_type=item_type)) for k, v in value.iteritems()])
1060