comparison env/lib/python3.9/site-packages/boto/sdb/db/property.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # Copyright (c) 2006,2007,2008 Mitch Garnaat http://garnaat.org/
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a
4 # copy of this software and associated documentation files (the
5 # "Software"), to deal in the Software without restriction, including
6 # without limitation the rights to use, copy, modify, merge, publish, dis-
7 # tribute, sublicense, and/or sell copies of the Software, and to permit
8 # persons to whom the Software is furnished to do so, subject to the fol-
9 # lowing conditions:
10 #
11 # The above copyright notice and this permission notice shall be included
12 # in all copies or substantial portions of the Software.
13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 # IN THE SOFTWARE.
21
22 import datetime
23 from boto.sdb.db.key import Key
24 from boto.utils import Password
25 from boto.sdb.db.query import Query
26 import re
27 import boto
28 import boto.s3.key
29 from boto.sdb.db.blob import Blob
30 from boto.compat import six, long_type
31
32
33 class Property(object):
34
35 data_type = str
36 type_name = ''
37 name = ''
38 verbose_name = ''
39
40 def __init__(self, verbose_name=None, name=None, default=None,
41 required=False, validator=None, choices=None, unique=False):
42 self.verbose_name = verbose_name
43 self.name = name
44 self.default = default
45 self.required = required
46 self.validator = validator
47 self.choices = choices
48 if self.name:
49 self.slot_name = '_' + self.name
50 else:
51 self.slot_name = '_'
52 self.unique = unique
53
54 def __get__(self, obj, objtype):
55 if obj:
56 obj.load()
57 return getattr(obj, self.slot_name)
58 else:
59 return None
60
61 def __set__(self, obj, value):
62 self.validate(value)
63
64 # Fire off any on_set functions
65 try:
66 if obj._loaded and hasattr(obj, "on_set_%s" % self.name):
67 fnc = getattr(obj, "on_set_%s" % self.name)
68 value = fnc(value)
69 except Exception:
70 boto.log.exception("Exception running on_set_%s" % self.name)
71
72 setattr(obj, self.slot_name, value)
73
74 def __property_config__(self, model_class, property_name):
75 self.model_class = model_class
76 self.name = property_name
77 self.slot_name = '_' + self.name
78
79 def default_validator(self, value):
80 if isinstance(value, six.string_types) or value == self.default_value():
81 return
82 if not isinstance(value, self.data_type):
83 raise TypeError('Validation Error, %s.%s expecting %s, got %s' % (self.model_class.__name__, self.name, self.data_type, type(value)))
84
85 def default_value(self):
86 return self.default
87
88 def validate(self, value):
89 if self.required and value is None:
90 raise ValueError('%s is a required property' % self.name)
91 if self.choices and value and value not in self.choices:
92 raise ValueError('%s not a valid choice for %s.%s' % (value, self.model_class.__name__, self.name))
93 if self.validator:
94 self.validator(value)
95 else:
96 self.default_validator(value)
97 return value
98
99 def empty(self, value):
100 return not value
101
102 def get_value_for_datastore(self, model_instance):
103 return getattr(model_instance, self.name)
104
105 def make_value_from_datastore(self, value):
106 return value
107
108 def get_choices(self):
109 if callable(self.choices):
110 return self.choices()
111 return self.choices
112
113
114 def validate_string(value):
115 if value is None:
116 return
117 elif isinstance(value, six.string_types):
118 if len(value) > 1024:
119 raise ValueError('Length of value greater than maxlength')
120 else:
121 raise TypeError('Expecting String, got %s' % type(value))
122
123
124 class StringProperty(Property):
125
126 type_name = 'String'
127
128 def __init__(self, verbose_name=None, name=None, default='',
129 required=False, validator=validate_string,
130 choices=None, unique=False):
131 super(StringProperty, self).__init__(verbose_name, name, default, required,
132 validator, choices, unique)
133
134
135 class TextProperty(Property):
136
137 type_name = 'Text'
138
139 def __init__(self, verbose_name=None, name=None, default='',
140 required=False, validator=None, choices=None,
141 unique=False, max_length=None):
142 super(TextProperty, self).__init__(verbose_name, name, default, required,
143 validator, choices, unique)
144 self.max_length = max_length
145
146 def validate(self, value):
147 value = super(TextProperty, self).validate(value)
148 if not isinstance(value, six.string_types):
149 raise TypeError('Expecting Text, got %s' % type(value))
150 if self.max_length and len(value) > self.max_length:
151 raise ValueError('Length of value greater than maxlength %s' % self.max_length)
152
153
154 class PasswordProperty(StringProperty):
155 """
156
157 Hashed property whose original value can not be
158 retrieved, but still can be compared.
159
160 Works by storing a hash of the original value instead
161 of the original value. Once that's done all that
162 can be retrieved is the hash.
163
164 The comparison
165
166 obj.password == 'foo'
167
168 generates a hash of 'foo' and compares it to the
169 stored hash.
170
171 Underlying data type for hashing, storing, and comparing
172 is boto.utils.Password. The default hash function is
173 defined there ( currently sha512 in most cases, md5
174 where sha512 is not available )
175
176 It's unlikely you'll ever need to use a different hash
177 function, but if you do, you can control the behavior
178 in one of two ways:
179
180 1) Specifying hashfunc in PasswordProperty constructor
181
182 import hashlib
183
184 class MyModel(model):
185 password = PasswordProperty(hashfunc=hashlib.sha224)
186
187 2) Subclassing Password and PasswordProperty
188
189 class SHA224Password(Password):
190 hashfunc=hashlib.sha224
191
192 class SHA224PasswordProperty(PasswordProperty):
193 data_type=MyPassword
194 type_name="MyPassword"
195
196 class MyModel(Model):
197 password = SHA224PasswordProperty()
198
199 """
200 data_type = Password
201 type_name = 'Password'
202
203 def __init__(self, verbose_name=None, name=None, default='', required=False,
204 validator=None, choices=None, unique=False, hashfunc=None):
205
206 """
207 The hashfunc parameter overrides the default hashfunc in boto.utils.Password.
208
209 The remaining parameters are passed through to StringProperty.__init__"""
210
211 super(PasswordProperty, self).__init__(verbose_name, name, default, required,
212 validator, choices, unique)
213 self.hashfunc = hashfunc
214
215 def make_value_from_datastore(self, value):
216 p = self.data_type(value, hashfunc=self.hashfunc)
217 return p
218
219 def get_value_for_datastore(self, model_instance):
220 value = super(PasswordProperty, self).get_value_for_datastore(model_instance)
221 if value and len(value):
222 return str(value)
223 else:
224 return None
225
226 def __set__(self, obj, value):
227 if not isinstance(value, self.data_type):
228 p = self.data_type(hashfunc=self.hashfunc)
229 p.set(value)
230 value = p
231 super(PasswordProperty, self).__set__(obj, value)
232
233 def __get__(self, obj, objtype):
234 return self.data_type(super(PasswordProperty, self).__get__(obj, objtype), hashfunc=self.hashfunc)
235
236 def validate(self, value):
237 value = super(PasswordProperty, self).validate(value)
238 if isinstance(value, self.data_type):
239 if len(value) > 1024:
240 raise ValueError('Length of value greater than maxlength')
241 else:
242 raise TypeError('Expecting %s, got %s' % (type(self.data_type), type(value)))
243
244
245 class BlobProperty(Property):
246 data_type = Blob
247 type_name = "blob"
248
249 def __set__(self, obj, value):
250 if value != self.default_value():
251 if not isinstance(value, Blob):
252 oldb = self.__get__(obj, type(obj))
253 id = None
254 if oldb:
255 id = oldb.id
256 b = Blob(value=value, id=id)
257 value = b
258 super(BlobProperty, self).__set__(obj, value)
259
260
261 class S3KeyProperty(Property):
262
263 data_type = boto.s3.key.Key
264 type_name = 'S3Key'
265 validate_regex = "^s3:\/\/([^\/]*)\/(.*)$"
266
267 def __init__(self, verbose_name=None, name=None, default=None,
268 required=False, validator=None, choices=None, unique=False):
269 super(S3KeyProperty, self).__init__(verbose_name, name, default, required,
270 validator, choices, unique)
271
272 def validate(self, value):
273 value = super(S3KeyProperty, self).validate(value)
274 if value == self.default_value() or value == str(self.default_value()):
275 return self.default_value()
276 if isinstance(value, self.data_type):
277 return
278 match = re.match(self.validate_regex, value)
279 if match:
280 return
281 raise TypeError('Validation Error, expecting %s, got %s' % (self.data_type, type(value)))
282
283 def __get__(self, obj, objtype):
284 value = super(S3KeyProperty, self).__get__(obj, objtype)
285 if value:
286 if isinstance(value, self.data_type):
287 return value
288 match = re.match(self.validate_regex, value)
289 if match:
290 s3 = obj._manager.get_s3_connection()
291 bucket = s3.get_bucket(match.group(1), validate=False)
292 k = bucket.get_key(match.group(2))
293 if not k:
294 k = bucket.new_key(match.group(2))
295 k.set_contents_from_string("")
296 return k
297 else:
298 return value
299
300 def get_value_for_datastore(self, model_instance):
301 value = super(S3KeyProperty, self).get_value_for_datastore(model_instance)
302 if value:
303 return "s3://%s/%s" % (value.bucket.name, value.name)
304 else:
305 return None
306
307
308 class IntegerProperty(Property):
309
310 data_type = int
311 type_name = 'Integer'
312
313 def __init__(self, verbose_name=None, name=None, default=0, required=False,
314 validator=None, choices=None, unique=False, max=2147483647, min=-2147483648):
315 super(IntegerProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
316 self.max = max
317 self.min = min
318
319 def validate(self, value):
320 value = int(value)
321 value = super(IntegerProperty, self).validate(value)
322 if value > self.max:
323 raise ValueError('Maximum value is %d' % self.max)
324 if value < self.min:
325 raise ValueError('Minimum value is %d' % self.min)
326 return value
327
328 def empty(self, value):
329 return value is None
330
331 def __set__(self, obj, value):
332 if value == "" or value is None:
333 value = 0
334 return super(IntegerProperty, self).__set__(obj, value)
335
336
337 class LongProperty(Property):
338
339 data_type = long_type
340 type_name = 'Long'
341
342 def __init__(self, verbose_name=None, name=None, default=0, required=False,
343 validator=None, choices=None, unique=False):
344 super(LongProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
345
346 def validate(self, value):
347 value = long_type(value)
348 value = super(LongProperty, self).validate(value)
349 min = -9223372036854775808
350 max = 9223372036854775807
351 if value > max:
352 raise ValueError('Maximum value is %d' % max)
353 if value < min:
354 raise ValueError('Minimum value is %d' % min)
355 return value
356
357 def empty(self, value):
358 return value is None
359
360
361 class BooleanProperty(Property):
362
363 data_type = bool
364 type_name = 'Boolean'
365
366 def __init__(self, verbose_name=None, name=None, default=False, required=False,
367 validator=None, choices=None, unique=False):
368 super(BooleanProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
369
370 def empty(self, value):
371 return value is None
372
373
374 class FloatProperty(Property):
375
376 data_type = float
377 type_name = 'Float'
378
379 def __init__(self, verbose_name=None, name=None, default=0.0, required=False,
380 validator=None, choices=None, unique=False):
381 super(FloatProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
382
383 def validate(self, value):
384 value = float(value)
385 value = super(FloatProperty, self).validate(value)
386 return value
387
388 def empty(self, value):
389 return value is None
390
391
392 class DateTimeProperty(Property):
393 """This class handles both the datetime.datetime object
394 And the datetime.date objects. It can return either one,
395 depending on the value stored in the database"""
396
397 data_type = datetime.datetime
398 type_name = 'DateTime'
399
400 def __init__(self, verbose_name=None, auto_now=False, auto_now_add=False, name=None,
401 default=None, required=False, validator=None, choices=None, unique=False):
402 super(DateTimeProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
403 self.auto_now = auto_now
404 self.auto_now_add = auto_now_add
405
406 def default_value(self):
407 if self.auto_now or self.auto_now_add:
408 return self.now()
409 return super(DateTimeProperty, self).default_value()
410
411 def validate(self, value):
412 if value is None:
413 return
414 if isinstance(value, datetime.date):
415 return value
416 return super(DateTimeProperty, self).validate(value)
417
418 def get_value_for_datastore(self, model_instance):
419 if self.auto_now:
420 setattr(model_instance, self.name, self.now())
421 return super(DateTimeProperty, self).get_value_for_datastore(model_instance)
422
423 def now(self):
424 return datetime.datetime.utcnow()
425
426
427 class DateProperty(Property):
428
429 data_type = datetime.date
430 type_name = 'Date'
431
432 def __init__(self, verbose_name=None, auto_now=False, auto_now_add=False, name=None,
433 default=None, required=False, validator=None, choices=None, unique=False):
434 super(DateProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
435 self.auto_now = auto_now
436 self.auto_now_add = auto_now_add
437
438 def default_value(self):
439 if self.auto_now or self.auto_now_add:
440 return self.now()
441 return super(DateProperty, self).default_value()
442
443 def validate(self, value):
444 value = super(DateProperty, self).validate(value)
445 if value is None:
446 return
447 if not isinstance(value, self.data_type):
448 raise TypeError('Validation Error, expecting %s, got %s' % (self.data_type, type(value)))
449
450 def get_value_for_datastore(self, model_instance):
451 if self.auto_now:
452 setattr(model_instance, self.name, self.now())
453 val = super(DateProperty, self).get_value_for_datastore(model_instance)
454 if isinstance(val, datetime.datetime):
455 val = val.date()
456 return val
457
458 def now(self):
459 return datetime.date.today()
460
461
462 class TimeProperty(Property):
463 data_type = datetime.time
464 type_name = 'Time'
465
466 def __init__(self, verbose_name=None, name=None,
467 default=None, required=False, validator=None, choices=None, unique=False):
468 super(TimeProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
469
470 def validate(self, value):
471 value = super(TimeProperty, self).validate(value)
472 if value is None:
473 return
474 if not isinstance(value, self.data_type):
475 raise TypeError('Validation Error, expecting %s, got %s' % (self.data_type, type(value)))
476
477
478 class ReferenceProperty(Property):
479
480 data_type = Key
481 type_name = 'Reference'
482
483 def __init__(self, reference_class=None, collection_name=None,
484 verbose_name=None, name=None, default=None, required=False, validator=None, choices=None, unique=False):
485 super(ReferenceProperty, self).__init__(verbose_name, name, default, required, validator, choices, unique)
486 self.reference_class = reference_class
487 self.collection_name = collection_name
488
489 def __get__(self, obj, objtype):
490 if obj:
491 value = getattr(obj, self.slot_name)
492 if value == self.default_value():
493 return value
494 # If the value is still the UUID for the referenced object, we need to create
495 # the object now that is the attribute has actually been accessed. This lazy
496 # instantiation saves unnecessary roundtrips to SimpleDB
497 if isinstance(value, six.string_types):
498 value = self.reference_class(value)
499 setattr(obj, self.name, value)
500 return value
501
502 def __set__(self, obj, value):
503 """Don't allow this object to be associated to itself
504 This causes bad things to happen"""
505 if value is not None and (obj.id == value or (hasattr(value, "id") and obj.id == value.id)):
506 raise ValueError("Can not associate an object with itself!")
507 return super(ReferenceProperty, self).__set__(obj, value)
508
509 def __property_config__(self, model_class, property_name):
510 super(ReferenceProperty, self).__property_config__(model_class, property_name)
511 if self.collection_name is None:
512 self.collection_name = '%s_%s_set' % (model_class.__name__.lower(), self.name)
513 if hasattr(self.reference_class, self.collection_name):
514 raise ValueError('duplicate property: %s' % self.collection_name)
515 setattr(self.reference_class, self.collection_name,
516 _ReverseReferenceProperty(model_class, property_name, self.collection_name))
517
518 def check_uuid(self, value):
519 # This does a bit of hand waving to "type check" the string
520 t = value.split('-')
521 if len(t) != 5:
522 raise ValueError
523
524 def check_instance(self, value):
525 try:
526 obj_lineage = value.get_lineage()
527 cls_lineage = self.reference_class.get_lineage()
528 if obj_lineage.startswith(cls_lineage):
529 return
530 raise TypeError('%s not instance of %s' % (obj_lineage, cls_lineage))
531 except:
532 raise ValueError('%s is not a Model' % value)
533
534 def validate(self, value):
535 if self.validator:
536 self.validator(value)
537 if self.required and value is None:
538 raise ValueError('%s is a required property' % self.name)
539 if value == self.default_value():
540 return
541 if not isinstance(value, six.string_types):
542 self.check_instance(value)
543
544
545 class _ReverseReferenceProperty(Property):
546 data_type = Query
547 type_name = 'query'
548
549 def __init__(self, model, prop, name):
550 self.__model = model
551 self.__property = prop
552 self.collection_name = prop
553 self.name = name
554 self.item_type = model
555
556 def __get__(self, model_instance, model_class):
557 """Fetches collection of model instances of this collection property."""
558 if model_instance is not None:
559 query = Query(self.__model)
560 if isinstance(self.__property, list):
561 props = []
562 for prop in self.__property:
563 props.append("%s =" % prop)
564 return query.filter(props, model_instance)
565 else:
566 return query.filter(self.__property + ' =', model_instance)
567 else:
568 return self
569
570 def __set__(self, model_instance, value):
571 """Not possible to set a new collection."""
572 raise ValueError('Virtual property is read-only')
573
574
575 class CalculatedProperty(Property):
576
577 def __init__(self, verbose_name=None, name=None, default=None,
578 required=False, validator=None, choices=None,
579 calculated_type=int, unique=False, use_method=False):
580 super(CalculatedProperty, self).__init__(verbose_name, name, default, required,
581 validator, choices, unique)
582 self.calculated_type = calculated_type
583 self.use_method = use_method
584
585 def __get__(self, obj, objtype):
586 value = self.default_value()
587 if obj:
588 try:
589 value = getattr(obj, self.slot_name)
590 if self.use_method:
591 value = value()
592 except AttributeError:
593 pass
594 return value
595
596 def __set__(self, obj, value):
597 """Not possible to set a new AutoID."""
598 pass
599
600 def _set_direct(self, obj, value):
601 if not self.use_method:
602 setattr(obj, self.slot_name, value)
603
604 def get_value_for_datastore(self, model_instance):
605 if self.calculated_type in [str, int, bool]:
606 value = self.__get__(model_instance, model_instance.__class__)
607 return value
608 else:
609 return None
610
611
612 class ListProperty(Property):
613
614 data_type = list
615 type_name = 'List'
616
617 def __init__(self, item_type, verbose_name=None, name=None, default=None, **kwds):
618 if default is None:
619 default = []
620 self.item_type = item_type
621 super(ListProperty, self).__init__(verbose_name, name, default=default, required=True, **kwds)
622
623 def validate(self, value):
624 if self.validator:
625 self.validator(value)
626 if value is not None:
627 if not isinstance(value, list):
628 value = [value]
629
630 if self.item_type in six.integer_types:
631 item_type = six.integer_types
632 elif self.item_type in six.string_types:
633 item_type = six.string_types
634 else:
635 item_type = self.item_type
636
637 for item in value:
638 if not isinstance(item, item_type):
639 if item_type == six.integer_types:
640 raise ValueError('Items in the %s list must all be integers.' % self.name)
641 else:
642 raise ValueError('Items in the %s list must all be %s instances' %
643 (self.name, self.item_type.__name__))
644 return value
645
646 def empty(self, value):
647 return value is None
648
649 def default_value(self):
650 return list(super(ListProperty, self).default_value())
651
652 def __set__(self, obj, value):
653 """Override the set method to allow them to set the property to an instance of the item_type instead of requiring a list to be passed in"""
654 if self.item_type in six.integer_types:
655 item_type = six.integer_types
656 elif self.item_type in six.string_types:
657 item_type = six.string_types
658 else:
659 item_type = self.item_type
660 if isinstance(value, item_type):
661 value = [value]
662 elif value is None: # Override to allow them to set this to "None" to remove everything
663 value = []
664 return super(ListProperty, self).__set__(obj, value)
665
666
667 class MapProperty(Property):
668
669 data_type = dict
670 type_name = 'Map'
671
672 def __init__(self, item_type=str, verbose_name=None, name=None, default=None, **kwds):
673 if default is None:
674 default = {}
675 self.item_type = item_type
676 super(MapProperty, self).__init__(verbose_name, name, default=default, required=True, **kwds)
677
678 def validate(self, value):
679 value = super(MapProperty, self).validate(value)
680 if value is not None:
681 if not isinstance(value, dict):
682 raise ValueError('Value must of type dict')
683
684 if self.item_type in six.integer_types:
685 item_type = six.integer_types
686 elif self.item_type in six.string_types:
687 item_type = six.string_types
688 else:
689 item_type = self.item_type
690
691 for key in value:
692 if not isinstance(value[key], item_type):
693 if item_type == six.integer_types:
694 raise ValueError('Values in the %s Map must all be integers.' % self.name)
695 else:
696 raise ValueError('Values in the %s Map must all be %s instances' %
697 (self.name, self.item_type.__name__))
698 return value
699
700 def empty(self, value):
701 return value is None
702
703 def default_value(self):
704 return {}