Source code for goblin.properties.properties

import copy
import datetime
from calendar import timegm
from decimal import Decimal as _D
import re
import time
import warnings
from uuid import uuid1, uuid4
from uuid import UUID as _UUID

from goblin._compat import (
    text_type, string_types, float_types, integer_types, long_, PY3)

from goblin.properties.base import GraphProperty
from goblin.properties.validators import *


[docs]class String(GraphProperty): """ String/CharField property """ data_type = "String" validator = string_validator def __init__(self, *args, **kwargs): required = kwargs.get('required', False) self.min_length = kwargs.pop('min_length', 1 if required else None) self.max_length = kwargs.pop('max_length', None) self.encoding = kwargs.pop('encoding', 'utf-8') if 'default' in kwargs and isinstance(kwargs['default'], string_types): if not PY3: kwargs['default'] = kwargs['default'].encode(self.encoding) super(String, self).__init__(*args, **kwargs)
[docs] def to_python(self, value): if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode(self.encoding) else: return value
[docs] def validate(self, value): # Make sure it gets encoded properly if isinstance(value, text_type) and not PY3: value = value.encode(self.encoding) value = super(String, self).validate(value) # this should never happen unless the Validator is changed if value is None: return None if self.max_length: if len(value) > self.max_length: raise ValidationError('{} is longer than {} characters'.format( self.property_name, self.max_length)) if self.min_length: if len(value) < self.min_length: raise ValidationError( '{} is shorter than {} characters'.format( self.property_name, self.min_length)) return value
Text = String
[docs]class Short(GraphProperty): """ Short Data property type """ data_type = "Short" validator = integer_validator
[docs] def to_python(self, value): if value is not None: return int(value)
[docs] def to_database(self, value): value = super(Short, self).to_database(value) if value is not None: return int(value)
[docs]class Integer(GraphProperty): """ Integer Data property type """ data_type = "Integer" validator = long_validator
[docs] def to_python(self, value): if value is not None: try: return int(value) except: return long_(value)
[docs] def to_database(self, value): value = super(Integer, self).to_database(value) if value is not None: return long_(value)
[docs]class PositiveInteger(Integer): """ Positive Integer Data property type """ data_type = "Integer" validator = positive_integer_validator
[docs] def to_python(self, value): if value is not None: return long_(value)
[docs] def to_database(self, value): value = super(Integer, self).to_database(value) if value is not None: return long_(value)
[docs]class Long(GraphProperty): """ Long Data property type """ data_type = "Long" validator = long_validator
[docs] def to_python(self, value): if value is not None: return long_(value)
[docs] def to_database(self, value): value = super(Long, self).to_database(value) if value is not None: return long_(value)
[docs]class PositiveLong(Long): """ Positive Long Data property type """ data_type = "Long" validator = positive_integer_validator
[docs] def to_python(self, value): if value is not None: return long_(value)
[docs] def to_database(self, value): value = super(Long, self).to_database(value) if value is not None: return long_(value)
[docs]class DateTimeNaive(GraphProperty): """ DateTime Data property type """ data_type = "Double" validator = datetime_validator def __init__(self, strict=True, **kwargs): """ Initialize date-time column with the given settings. :param strict: Whether or not to attempt to automatically coerce types :type strict: boolean """ self.strict = strict super(DateTimeNaive, self).__init__(**kwargs)
[docs] def to_python(self, value): if isinstance(value, datetime.datetime): return value return datetime.datetime.fromtimestamp(float(value))
[docs] def to_database(self, value): value = super(DateTimeNaive, self).to_database(value) if value is None: return if not isinstance(value, datetime.datetime): if not self.strict and isinstance(value, string_types + integer_types + float_types): value = datetime.datetime.fromtimestamp(float(value)) else: raise ValidationError( "'{}' is not a datetime object".format(value)) tmp = time.mktime(value.timetuple()) # gives us a float with .0 # microtime is a 6 digit int, so we bring it down to .xxx and add it to # the float TS tmp += float(value.microsecond) / 1000000.0 return tmp
[docs]class DateTime(GraphProperty): """ UTC DateTime Data property type """ data_type = "Double" validator = datetime_utc_validator def __init__(self, strict=True, **kwargs): """ Initialize date-time column with the given settings. :param strict: Whether or not to attempt to automatically coerce types :type strict: boolean """ self.strict = strict super(DateTime, self).__init__(**kwargs)
[docs] def to_python(self, value): try: if isinstance(value, datetime.datetime): if value.tzinfo == utc: return self.validator(value) # .astimezone(tz=utc) else: return self.validator(value).astimezone(tz=utc) except: # pragma: no cover # this shouldn't happen unless the validator has changed pass return datetime.datetime.utcfromtimestamp( float(value)).replace(tzinfo=utc)
[docs] def to_database(self, value): value = super(DateTime, self).to_database(value) if value is None: return if not isinstance(value, datetime.datetime): if isinstance(value, string_types + integer_types + float_types): value = datetime.datetime.utcfromtimestamp( float(value)).replace(tzinfo=utc) else: raise ValidationError( "'{}' is not a datetime object".format(value)) tmp = timegm(value.utctimetuple()) # gives us an integer of epoch seconds without microseconds # microtime is a 6 digit int, so we bring it down to .xxx and add it # to the float TS tmp += float(value.microsecond) / 1000000.0 return tmp
[docs]class UUID(GraphProperty): """Universally Unique Identifier (UUID) type - UUID4 by default""" data_type = "String" validator = validate_uuid4 def __init__(self, default=lambda: str(uuid4()), **kwargs): self.uuid_version = kwargs.pop('version', 'uuid4').lower() if self.uuid_version == 'uuid4': self.validator = validate_uuid4 elif self.uuid_version == 'uuid1': self.validator = validate_uuid1 super(UUID, self).__init__(default=default, **kwargs)
[docs] def to_python(self, value): val = super(UUID, self).to_python(value) if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode('utf-8') else: return value
[docs] def to_database(self, value): val = super(UUID, self).to_database(value) if val is None: return return str(val)
[docs]class Boolean(GraphProperty): """ Boolean Data property type """ data_type = "Boolean" validator = bool_validator
[docs] def to_python(self, value): return bool(value)
[docs] def to_database(self, value): val = super(Boolean, self).to_database(value) return bool(val)
[docs]class Double(GraphProperty): """ Double Data property type """ data_type = "Double" validator = float_validator def __init__(self, **kwargs): self.db_type = 'double' super(Double, self).__init__(**kwargs)
[docs] def to_python(self, value): if value is not None: return float(value)
[docs] def to_database(self, value): value = super(Double, self).to_database(value) if value is not None: return float(value)
[docs]class Float(Double): """Float class for backwards compatability / if you really want to""" def __init__(self, **kwargs): warnings.warn("Float type is deprecated. Please use Double.", category=DeprecationWarning) super(Float, self).__init__(**kwargs)
[docs]class Decimal(GraphProperty): """ Decimal Data property type """ validator = decimal_validator
[docs] def to_python(self, value): val = super(Decimal, self).to_python(value) if val is not None: return _D(val)
[docs] def to_database(self, value): val = super(Decimal, self).to_database(value) if val is not None: return str(val)
[docs]class Dictionary(GraphProperty): """ Dictionary Data property type """ data_type = "HashMap" validator = dict_validator
[docs]class List(GraphProperty): """ List Data property type """ data_type = "ArrayList" validator = list_validator
[docs]class URL(GraphProperty): """ URL Data property type """ data_type = "String" validator = validate_url def __init__(self, *args, **kwargs): required = kwargs.get('required', False) self.min_length = kwargs.pop('min_length', 1 if required else None) self.max_length = kwargs.pop('max_length', None) self.encoding = kwargs.pop('encoding', 'utf-8') if 'default' in kwargs and isinstance(kwargs['default'], string_types): if not PY3: kwargs['default'] = kwargs['default'].encode(self.encoding) super(URL, self).__init__(*args, **kwargs)
[docs] def validate(self, value): # Make sure it gets encoded correctly if isinstance(value, text_type) and not PY3: value = value.encode(self.encoding) value = super(URL, self).validate(value) # this should never happen unless the validator is customized if value in (None, [], (), {}): # pragma: no cover return None if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode(self.encoding) if self.max_length: if len(value) > self.max_length: raise ValidationError('{} is longer than {} characters'.format( self.property_name, self.max_length)) if self.min_length: if len(value) < self.min_length: raise ValidationError( '{} is shorter than {} characters'.format( self.property_name, self.min_length)) self.validator(value) return value
[docs]class Email(GraphProperty): """ Email Data property type """ data_type = "String" validator = validate_email def __init__(self, *args, **kwargs): self.encoding = kwargs.pop('encoding', 'utf-8') if 'default' in kwargs and isinstance(kwargs['default'], string_types): if not PY3: kwargs['default'] = kwargs['default'].encode(self.encoding) super(Email, self).__init__(*args, **kwargs)
[docs] def validate(self, value): # Make sure it gets encoded correctly if isinstance(value, text_type) and not PY3: value = value.encode(self.encoding) value = super(Email, self).validate(value) # This should never happen unless the validator is changed if value in (None, [], (), {}): # pragma: no cover return None if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode(self.encoding) # This should never happen unless the validator is changed if not isinstance(value, string_types): # pragma: no cover raise ValidationError('%s is not a string' % type(value)) self.validator(value) return value
[docs]class IPV4(GraphProperty): """ IPv4 Data property type """ data_type = "String" validator = validate_ipv4_address def __init__(self, *args, **kwargs): self.encoding = kwargs.pop('encoding', 'utf-8') if 'default' in kwargs and isinstance(kwargs['default'], string_types): if not PY3: kwargs['default'] = kwargs['default'].encode(self.encoding) super(IPV4, self).__init__(*args, **kwargs)
[docs] def validate(self, value): # Make sure it gets encoded correctly if isinstance(value, text_type) and not PY3: value = value.encode(self.encoding) value = super(IPV4, self).validate(value) # This should never happen unless the validator is changed if value in (None, [], (), {}): # pragma: no cover return None if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode(self.encoding) # This should never happen unless the validator is changed if not isinstance(value, string_types): # pragma: no cover raise ValidationError('%s is not a string' % type(value)) self.validator(value) return value
[docs]class IPV6(GraphProperty): """ IPv6 Data property type """ data_type = "String" validator = validate_ipv6_address def __init__(self, *args, **kwargs): self.encoding = kwargs.pop('encoding', 'utf-8') if 'default' in kwargs and isinstance(kwargs['default'], string_types): if not PY3: kwargs['default'] = kwargs['default'].encode(self.encoding) super(IPV6, self).__init__(*args, **kwargs)
[docs] def validate(self, value): # Make sure it gets encoded correctly if isinstance(value, text_type) and not PY3: value = value.encode(self.encoding) value = super(IPV6, self).validate(value) if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode(self.encoding) # This shouldn't happend unless the validator is changed if value in (None, [], (), {}): # pragma: no cover return None # This shouldn't happend unless the validator is changed if not isinstance(value, string_types): # pragma: no cover raise ValidationError('%s is not a string' % type(value)) self.validator(value) return value
[docs]class IPV6WithV4(GraphProperty): """ IPv6 with Mapped/Translated/Embedded IPv4 Data property type """ data_type = "String" validator = validate_ipv6_ipv4_address def __init__(self, *args, **kwargs): self.encoding = kwargs.pop('encoding', 'utf-8') if 'default' in kwargs and isinstance(kwargs['default'], string_types): if not PY3: kwargs['default'] = kwargs['default'].encode(self.encoding) super(IPV6WithV4, self).__init__(*args, **kwargs)
[docs] def validate(self, value): # Make sure it gets encoded correctly if isinstance(value, text_type) and not PY3: value = value.encode(self.encoding) value = super(IPV6WithV4, self).validate(value) # This shouldn't happend unless the validator is changed if value in (None, [], (), {}): # pragma: no cover return None if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode(self.encoding) # This shouldn't happend unless the validator is changed if not isinstance(value, string_types): # pragma: no cover raise ValidationError('%s is not a string' % type(value)) self.validator(value) return value
[docs]class Slug(GraphProperty): """ Slug Data property type """ data_type = "String" validator = validate_slug def __init__(self, *args, **kwargs): self.encoding = kwargs.pop('encoding', 'utf-8') if 'default' in kwargs and isinstance(kwargs['default'], string_types): if not PY3: kwargs['default'] = kwargs['default'].encode(self.encoding) super(Slug, self).__init__(*args, **kwargs)
[docs] def validate(self, value): # Make sure it gets encoded correctly if isinstance(value, text_type) and not PY3: value = value.encode(self.encoding) value = super(Slug, self).validate(value) if value in (None, [], (), {}): return None if value is not None: if isinstance(value, (bytes, bytearray)) and not isinstance(value, str): return value.decode(self.encoding) if not isinstance(value, string_types): raise ValidationError('%s is not a string' % type(value)) self.validator(value) return value