Index: third_party/gsutil/boto/boto/sdb/db/property.py |
diff --git a/third_party/gsutil/boto/boto/sdb/db/property.py b/third_party/gsutil/boto/boto/sdb/db/property.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b8610cfeffa64cd1dcd64d71e9279bdce82c6826 |
--- /dev/null |
+++ b/third_party/gsutil/boto/boto/sdb/db/property.py |
@@ -0,0 +1,703 @@ |
+# Copyright (c) 2006,2007,2008 Mitch Garnaat http://garnaat.org/ |
+# |
+# Permission is hereby granted, free of charge, to any person obtaining a |
+# copy of this software and associated documentation files (the |
+# "Software"), to deal in the Software without restriction, including |
+# without limitation the rights to use, copy, modify, merge, publish, dis- |
+# tribute, sublicense, and/or sell copies of the Software, and to permit |
+# persons to whom the Software is furnished to do so, subject to the fol- |
+# lowing conditions: |
+# |
+# The above copyright notice and this permission notice shall be included |
+# in all copies or substantial portions of the Software. |
+# |
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
+# IN THE SOFTWARE. |
+ |
+import datetime |
+from key import Key |
+from boto.utils import Password |
+from boto.sdb.db.query import Query |
+import re |
+import boto |
+import boto.s3.key |
+from boto.sdb.db.blob import Blob |
+ |
+ |
+class Property(object): |
+ |
+ data_type = str |
+ type_name = '' |
+ name = '' |
+ verbose_name = '' |
+ |
+ def __init__(self, verbose_name=None, name=None, default=None, |
+ required=False, validator=None, choices=None, unique=False): |
+ self.verbose_name = verbose_name |
+ self.name = name |
+ self.default = default |
+ self.required = required |
+ self.validator = validator |
+ self.choices = choices |
+ if self.name: |
+ self.slot_name = '_' + self.name |
+ else: |
+ self.slot_name = '_' |
+ self.unique = unique |
+ |
+ def __get__(self, obj, objtype): |
+ if obj: |
+ obj.load() |
+ return getattr(obj, self.slot_name) |
+ else: |
+ return None |
+ |
+ def __set__(self, obj, value): |
+ self.validate(value) |
+ |
+ # Fire off any on_set functions |
+ try: |
+ if obj._loaded and hasattr(obj, "on_set_%s" % self.name): |
+ fnc = getattr(obj, "on_set_%s" % self.name) |
+ value = fnc(value) |
+ except Exception: |
+ boto.log.exception("Exception running on_set_%s" % self.name) |
+ |
+ setattr(obj, self.slot_name, value) |
+ |
+ def __property_config__(self, model_class, property_name): |
+ self.model_class = model_class |
+ self.name = property_name |
+ self.slot_name = '_' + self.name |
+ |
+ def default_validator(self, value): |
+ if isinstance(value, basestring) or value == self.default_value(): |
+ return |
+ if not isinstance(value, self.data_type): |
+ raise TypeError('Validation Error, %s.%s expecting %s, got %s' % (self.model_class.__name__, self.name, self.data_type, type(value))) |
+ |
+ def default_value(self): |
+ return self.default |
+ |
+ def validate(self, value): |
+ if self.required and value == None: |
+ raise ValueError('%s is a required property' % self.name) |
+ if self.choices and value and not value in self.choices: |
+ raise ValueError('%s not a valid choice for %s.%s' % (value, self.model_class.__name__, self.name)) |
+ if self.validator: |
+ self.validator(value) |
+ else: |
+ self.default_validator(value) |
+ return value |
+ |
+ def empty(self, value): |
+ return not value |
+ |
+ def get_value_for_datastore(self, model_instance): |
+ return getattr(model_instance, self.name) |
+ |
+ def make_value_from_datastore(self, value): |
+ return value |
+ |
+ def get_choices(self): |
+ if callable(self.choices): |
+ return self.choices() |
+ return self.choices |
+ |
+ |
+def validate_string(value): |
+ if value == None: |
+ return |
+ elif isinstance(value, str) or isinstance(value, unicode): |
+ if len(value) > 1024: |
+ raise ValueError('Length of value greater than maxlength') |
+ else: |
+ raise TypeError('Expecting String, got %s' % type(value)) |
+ |
+ |
+class StringProperty(Property): |
+ |
+ type_name = 'String' |
+ |
+ def __init__(self, verbose_name=None, name=None, default='', |
+ required=False, validator=validate_string, |
+ choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, |
+ validator, choices, unique) |
+ |
+ |
+class TextProperty(Property): |
+ |
+ type_name = 'Text' |
+ |
+ def __init__(self, verbose_name=None, name=None, default='', |
+ required=False, validator=None, choices=None, |
+ unique=False, max_length=None): |
+ Property.__init__(self, verbose_name, name, default, required, |
+ validator, choices, unique) |
+ self.max_length = max_length |
+ |
+ def validate(self, value): |
+ value = super(TextProperty, self).validate(value) |
+ if not isinstance(value, str) and not isinstance(value, unicode): |
+ raise TypeError('Expecting Text, got %s' % type(value)) |
+ if self.max_length and len(value) > self.max_length: |
+ raise ValueError('Length of value greater than maxlength %s' % self.max_length) |
+ |
+ |
+class PasswordProperty(StringProperty): |
+ """ |
+ |
+ Hashed property whose original value can not be |
+ retrieved, but still can be compared. |
+ |
+ Works by storing a hash of the original value instead |
+ of the original value. Once that's done all that |
+ can be retrieved is the hash. |
+ |
+ The comparison |
+ |
+ obj.password == 'foo' |
+ |
+ generates a hash of 'foo' and compares it to the |
+ stored hash. |
+ |
+ Underlying data type for hashing, storing, and comparing |
+ is boto.utils.Password. The default hash function is |
+ defined there ( currently sha512 in most cases, md5 |
+ where sha512 is not available ) |
+ |
+ It's unlikely you'll ever need to use a different hash |
+ function, but if you do, you can control the behavior |
+ in one of two ways: |
+ |
+ 1) Specifying hashfunc in PasswordProperty constructor |
+ |
+ import hashlib |
+ |
+ class MyModel(model): |
+ password = PasswordProperty(hashfunc=hashlib.sha224) |
+ |
+ 2) Subclassing Password and PasswordProperty |
+ |
+ class SHA224Password(Password): |
+ hashfunc=hashlib.sha224 |
+ |
+ class SHA224PasswordProperty(PasswordProperty): |
+ data_type=MyPassword |
+ type_name="MyPassword" |
+ |
+ class MyModel(Model): |
+ password = SHA224PasswordProperty() |
+ |
+ """ |
+ data_type = Password |
+ type_name = 'Password' |
+ |
+ def __init__(self, verbose_name=None, name=None, default='', required=False, |
+ validator=None, choices=None, unique=False, hashfunc=None): |
+ |
+ """ |
+ The hashfunc parameter overrides the default hashfunc in boto.utils.Password. |
+ |
+ The remaining parameters are passed through to StringProperty.__init__""" |
+ |
+ StringProperty.__init__(self, verbose_name, name, default, required, |
+ validator, choices, unique) |
+ self.hashfunc = hashfunc |
+ |
+ def make_value_from_datastore(self, value): |
+ p = self.data_type(value, hashfunc=self.hashfunc) |
+ return p |
+ |
+ def get_value_for_datastore(self, model_instance): |
+ value = StringProperty.get_value_for_datastore(self, model_instance) |
+ if value and len(value): |
+ return str(value) |
+ else: |
+ return None |
+ |
+ def __set__(self, obj, value): |
+ if not isinstance(value, self.data_type): |
+ p = self.data_type(hashfunc=self.hashfunc) |
+ p.set(value) |
+ value = p |
+ Property.__set__(self, obj, value) |
+ |
+ def __get__(self, obj, objtype): |
+ return self.data_type(StringProperty.__get__(self, obj, objtype), hashfunc=self.hashfunc) |
+ |
+ def validate(self, value): |
+ value = Property.validate(self, value) |
+ if isinstance(value, self.data_type): |
+ if len(value) > 1024: |
+ raise ValueError('Length of value greater than maxlength') |
+ else: |
+ raise TypeError('Expecting %s, got %s' % (type(self.data_type), type(value))) |
+ |
+ |
+class BlobProperty(Property): |
+ data_type = Blob |
+ type_name = "blob" |
+ |
+ def __set__(self, obj, value): |
+ if value != self.default_value(): |
+ if not isinstance(value, Blob): |
+ oldb = self.__get__(obj, type(obj)) |
+ id = None |
+ if oldb: |
+ id = oldb.id |
+ b = Blob(value=value, id=id) |
+ value = b |
+ Property.__set__(self, obj, value) |
+ |
+ |
+class S3KeyProperty(Property): |
+ |
+ data_type = boto.s3.key.Key |
+ type_name = 'S3Key' |
+ validate_regex = "^s3:\/\/([^\/]*)\/(.*)$" |
+ |
+ def __init__(self, verbose_name=None, name=None, default=None, |
+ required=False, validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, |
+ validator, choices, unique) |
+ |
+ def validate(self, value): |
+ value = super(S3KeyProperty, self).validate(value) |
+ if value == self.default_value() or value == str(self.default_value()): |
+ return self.default_value() |
+ if isinstance(value, self.data_type): |
+ return |
+ match = re.match(self.validate_regex, value) |
+ if match: |
+ return |
+ raise TypeError('Validation Error, expecting %s, got %s' % (self.data_type, type(value))) |
+ |
+ def __get__(self, obj, objtype): |
+ value = Property.__get__(self, obj, objtype) |
+ if value: |
+ if isinstance(value, self.data_type): |
+ return value |
+ match = re.match(self.validate_regex, value) |
+ if match: |
+ s3 = obj._manager.get_s3_connection() |
+ bucket = s3.get_bucket(match.group(1), validate=False) |
+ k = bucket.get_key(match.group(2)) |
+ if not k: |
+ k = bucket.new_key(match.group(2)) |
+ k.set_contents_from_string("") |
+ return k |
+ else: |
+ return value |
+ |
+ def get_value_for_datastore(self, model_instance): |
+ value = Property.get_value_for_datastore(self, model_instance) |
+ if value: |
+ return "s3://%s/%s" % (value.bucket.name, value.name) |
+ else: |
+ return None |
+ |
+ |
+class IntegerProperty(Property): |
+ |
+ data_type = int |
+ type_name = 'Integer' |
+ |
+ def __init__(self, verbose_name=None, name=None, default=0, required=False, |
+ validator=None, choices=None, unique=False, max=2147483647, min=-2147483648): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ self.max = max |
+ self.min = min |
+ |
+ def validate(self, value): |
+ value = int(value) |
+ value = Property.validate(self, value) |
+ if value > self.max: |
+ raise ValueError('Maximum value is %d' % self.max) |
+ if value < self.min: |
+ raise ValueError('Minimum value is %d' % self.min) |
+ return value |
+ |
+ def empty(self, value): |
+ return value is None |
+ |
+ def __set__(self, obj, value): |
+ if value == "" or value == None: |
+ value = 0 |
+ return Property.__set__(self, obj, value) |
+ |
+ |
+class LongProperty(Property): |
+ |
+ data_type = long |
+ type_name = 'Long' |
+ |
+ def __init__(self, verbose_name=None, name=None, default=0, required=False, |
+ validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ |
+ def validate(self, value): |
+ value = long(value) |
+ value = Property.validate(self, value) |
+ min = -9223372036854775808 |
+ max = 9223372036854775807 |
+ if value > max: |
+ raise ValueError('Maximum value is %d' % max) |
+ if value < min: |
+ raise ValueError('Minimum value is %d' % min) |
+ return value |
+ |
+ def empty(self, value): |
+ return value is None |
+ |
+ |
+class BooleanProperty(Property): |
+ |
+ data_type = bool |
+ type_name = 'Boolean' |
+ |
+ def __init__(self, verbose_name=None, name=None, default=False, required=False, |
+ validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ |
+ def empty(self, value): |
+ return value is None |
+ |
+ |
+class FloatProperty(Property): |
+ |
+ data_type = float |
+ type_name = 'Float' |
+ |
+ def __init__(self, verbose_name=None, name=None, default=0.0, required=False, |
+ validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ |
+ def validate(self, value): |
+ value = float(value) |
+ value = Property.validate(self, value) |
+ return value |
+ |
+ def empty(self, value): |
+ return value is None |
+ |
+ |
+class DateTimeProperty(Property): |
+ """This class handles both the datetime.datetime object |
+ And the datetime.date objects. It can return either one, |
+ depending on the value stored in the database""" |
+ |
+ data_type = datetime.datetime |
+ type_name = 'DateTime' |
+ |
+ def __init__(self, verbose_name=None, auto_now=False, auto_now_add=False, name=None, |
+ default=None, required=False, validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ self.auto_now = auto_now |
+ self.auto_now_add = auto_now_add |
+ |
+ def default_value(self): |
+ if self.auto_now or self.auto_now_add: |
+ return self.now() |
+ return Property.default_value(self) |
+ |
+ def validate(self, value): |
+ if value == None: |
+ return |
+ if isinstance(value, datetime.date): |
+ return value |
+ return super(DateTimeProperty, self).validate(value) |
+ |
+ def get_value_for_datastore(self, model_instance): |
+ if self.auto_now: |
+ setattr(model_instance, self.name, self.now()) |
+ return Property.get_value_for_datastore(self, model_instance) |
+ |
+ def now(self): |
+ return datetime.datetime.utcnow() |
+ |
+ |
+class DateProperty(Property): |
+ |
+ data_type = datetime.date |
+ type_name = 'Date' |
+ |
+ def __init__(self, verbose_name=None, auto_now=False, auto_now_add=False, name=None, |
+ default=None, required=False, validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ self.auto_now = auto_now |
+ self.auto_now_add = auto_now_add |
+ |
+ def default_value(self): |
+ if self.auto_now or self.auto_now_add: |
+ return self.now() |
+ return Property.default_value(self) |
+ |
+ def validate(self, value): |
+ value = super(DateProperty, self).validate(value) |
+ if value == None: |
+ return |
+ if not isinstance(value, self.data_type): |
+ raise TypeError('Validation Error, expecting %s, got %s' % (self.data_type, type(value))) |
+ |
+ def get_value_for_datastore(self, model_instance): |
+ if self.auto_now: |
+ setattr(model_instance, self.name, self.now()) |
+ val = Property.get_value_for_datastore(self, model_instance) |
+ if isinstance(val, datetime.datetime): |
+ val = val.date() |
+ return val |
+ |
+ def now(self): |
+ return datetime.date.today() |
+ |
+ |
+class TimeProperty(Property): |
+ data_type = datetime.time |
+ type_name = 'Time' |
+ |
+ def __init__(self, verbose_name=None, name=None, |
+ default=None, required=False, validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ |
+ def validate(self, value): |
+ value = super(TimeProperty, self).validate(value) |
+ if value is None: |
+ return |
+ if not isinstance(value, self.data_type): |
+ raise TypeError('Validation Error, expecting %s, got %s' % (self.data_type, type(value))) |
+ |
+ |
+class ReferenceProperty(Property): |
+ |
+ data_type = Key |
+ type_name = 'Reference' |
+ |
+ def __init__(self, reference_class=None, collection_name=None, |
+ verbose_name=None, name=None, default=None, required=False, validator=None, choices=None, unique=False): |
+ Property.__init__(self, verbose_name, name, default, required, validator, choices, unique) |
+ self.reference_class = reference_class |
+ self.collection_name = collection_name |
+ |
+ def __get__(self, obj, objtype): |
+ if obj: |
+ value = getattr(obj, self.slot_name) |
+ if value == self.default_value(): |
+ return value |
+ # If the value is still the UUID for the referenced object, we need to create |
+ # the object now that is the attribute has actually been accessed. This lazy |
+ # instantiation saves unnecessary roundtrips to SimpleDB |
+ if isinstance(value, str) or isinstance(value, unicode): |
+ value = self.reference_class(value) |
+ setattr(obj, self.name, value) |
+ return value |
+ |
+ def __set__(self, obj, value): |
+ """Don't allow this object to be associated to itself |
+ This causes bad things to happen""" |
+ if value != None and (obj.id == value or (hasattr(value, "id") and obj.id == value.id)): |
+ raise ValueError("Can not associate an object with itself!") |
+ return super(ReferenceProperty, self).__set__(obj, value) |
+ |
+ def __property_config__(self, model_class, property_name): |
+ Property.__property_config__(self, model_class, property_name) |
+ if self.collection_name is None: |
+ self.collection_name = '%s_%s_set' % (model_class.__name__.lower(), self.name) |
+ if hasattr(self.reference_class, self.collection_name): |
+ raise ValueError('duplicate property: %s' % self.collection_name) |
+ setattr(self.reference_class, self.collection_name, |
+ _ReverseReferenceProperty(model_class, property_name, self.collection_name)) |
+ |
+ def check_uuid(self, value): |
+ # This does a bit of hand waving to "type check" the string |
+ t = value.split('-') |
+ if len(t) != 5: |
+ raise ValueError |
+ |
+ def check_instance(self, value): |
+ try: |
+ obj_lineage = value.get_lineage() |
+ cls_lineage = self.reference_class.get_lineage() |
+ if obj_lineage.startswith(cls_lineage): |
+ return |
+ raise TypeError('%s not instance of %s' % (obj_lineage, cls_lineage)) |
+ except: |
+ raise ValueError('%s is not a Model' % value) |
+ |
+ def validate(self, value): |
+ if self.validator: |
+ self.validator(value) |
+ if self.required and value == None: |
+ raise ValueError('%s is a required property' % self.name) |
+ if value == self.default_value(): |
+ return |
+ if not isinstance(value, str) and not isinstance(value, unicode): |
+ self.check_instance(value) |
+ |
+ |
+class _ReverseReferenceProperty(Property): |
+ data_type = Query |
+ type_name = 'query' |
+ |
+ def __init__(self, model, prop, name): |
+ self.__model = model |
+ self.__property = prop |
+ self.collection_name = prop |
+ self.name = name |
+ self.item_type = model |
+ |
+ def __get__(self, model_instance, model_class): |
+ """Fetches collection of model instances of this collection property.""" |
+ if model_instance is not None: |
+ query = Query(self.__model) |
+ if isinstance(self.__property, list): |
+ props = [] |
+ for prop in self.__property: |
+ props.append("%s =" % prop) |
+ return query.filter(props, model_instance) |
+ else: |
+ return query.filter(self.__property + ' =', model_instance) |
+ else: |
+ return self |
+ |
+ def __set__(self, model_instance, value): |
+ """Not possible to set a new collection.""" |
+ raise ValueError('Virtual property is read-only') |
+ |
+ |
+class CalculatedProperty(Property): |
+ |
+ def __init__(self, verbose_name=None, name=None, default=None, |
+ required=False, validator=None, choices=None, |
+ calculated_type=int, unique=False, use_method=False): |
+ Property.__init__(self, verbose_name, name, default, required, |
+ validator, choices, unique) |
+ self.calculated_type = calculated_type |
+ self.use_method = use_method |
+ |
+ def __get__(self, obj, objtype): |
+ value = self.default_value() |
+ if obj: |
+ try: |
+ value = getattr(obj, self.slot_name) |
+ if self.use_method: |
+ value = value() |
+ except AttributeError: |
+ pass |
+ return value |
+ |
+ def __set__(self, obj, value): |
+ """Not possible to set a new AutoID.""" |
+ pass |
+ |
+ def _set_direct(self, obj, value): |
+ if not self.use_method: |
+ setattr(obj, self.slot_name, value) |
+ |
+ def get_value_for_datastore(self, model_instance): |
+ if self.calculated_type in [str, int, bool]: |
+ value = self.__get__(model_instance, model_instance.__class__) |
+ return value |
+ else: |
+ return None |
+ |
+ |
+class ListProperty(Property): |
+ |
+ data_type = list |
+ type_name = 'List' |
+ |
+ def __init__(self, item_type, verbose_name=None, name=None, default=None, **kwds): |
+ if default is None: |
+ default = [] |
+ self.item_type = item_type |
+ Property.__init__(self, verbose_name, name, default=default, required=True, **kwds) |
+ |
+ def validate(self, value): |
+ if self.validator: |
+ self.validator(value) |
+ if value is not None: |
+ if not isinstance(value, list): |
+ value = [value] |
+ |
+ if self.item_type in (int, long): |
+ item_type = (int, long) |
+ elif self.item_type in (str, unicode): |
+ item_type = (str, unicode) |
+ else: |
+ item_type = self.item_type |
+ |
+ for item in value: |
+ if not isinstance(item, item_type): |
+ if item_type == (int, long): |
+ raise ValueError('Items in the %s list must all be integers.' % self.name) |
+ else: |
+ raise ValueError('Items in the %s list must all be %s instances' % |
+ (self.name, self.item_type.__name__)) |
+ return value |
+ |
+ def empty(self, value): |
+ return value is None |
+ |
+ def default_value(self): |
+ return list(super(ListProperty, self).default_value()) |
+ |
+ def __set__(self, obj, value): |
+ """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""" |
+ if self.item_type in (int, long): |
+ item_type = (int, long) |
+ elif self.item_type in (str, unicode): |
+ item_type = (str, unicode) |
+ else: |
+ item_type = self.item_type |
+ if isinstance(value, item_type): |
+ value = [value] |
+ elif value == None: # Override to allow them to set this to "None" to remove everything |
+ value = [] |
+ return super(ListProperty, self).__set__(obj, value) |
+ |
+ |
+class MapProperty(Property): |
+ |
+ data_type = dict |
+ type_name = 'Map' |
+ |
+ def __init__(self, item_type=str, verbose_name=None, name=None, default=None, **kwds): |
+ if default is None: |
+ default = {} |
+ self.item_type = item_type |
+ Property.__init__(self, verbose_name, name, default=default, required=True, **kwds) |
+ |
+ def validate(self, value): |
+ value = super(MapProperty, self).validate(value) |
+ if value is not None: |
+ if not isinstance(value, dict): |
+ raise ValueError('Value must of type dict') |
+ |
+ if self.item_type in (int, long): |
+ item_type = (int, long) |
+ elif self.item_type in (str, unicode): |
+ item_type = (str, unicode) |
+ else: |
+ item_type = self.item_type |
+ |
+ for key in value: |
+ if not isinstance(value[key], item_type): |
+ if item_type == (int, long): |
+ raise ValueError('Values in the %s Map must all be integers.' % self.name) |
+ else: |
+ raise ValueError('Values in the %s Map must all be %s instances' % |
+ (self.name, self.item_type.__name__)) |
+ return value |
+ |
+ def empty(self, value): |
+ return value is None |
+ |
+ def default_value(self): |
+ return {} |