thirdparty/google_appengine/google/appengine/api/datastore_types.py
changeset 2864 2e0b0af889be
parent 2413 d0b7dac5325c
child 3031 7678f72140e6
equal deleted inserted replaced
2862:27971a13089f 2864:2e0b0af889be
    45 import urlparse
    45 import urlparse
    46 from xml.sax import saxutils
    46 from xml.sax import saxutils
    47 from google.appengine.datastore import datastore_pb
    47 from google.appengine.datastore import datastore_pb
    48 from google.appengine.api import datastore_errors
    48 from google.appengine.api import datastore_errors
    49 from google.appengine.api import users
    49 from google.appengine.api import users
       
    50 from google.appengine.api import namespace_manager
    50 from google.net.proto import ProtocolBuffer
    51 from google.net.proto import ProtocolBuffer
    51 from google.appengine.datastore import entity_pb
    52 from google.appengine.datastore import entity_pb
    52 
    53 
    53 _MAX_STRING_LENGTH = 500
    54 _MAX_STRING_LENGTH = 500
    54 
    55 
    56 
    57 
    57 RESERVED_PROPERTY_NAME = re.compile('^__.*__$')
    58 RESERVED_PROPERTY_NAME = re.compile('^__.*__$')
    58 
    59 
    59 _KEY_SPECIAL_PROPERTY = '__key__'
    60 _KEY_SPECIAL_PROPERTY = '__key__'
    60 _SPECIAL_PROPERTIES = frozenset([_KEY_SPECIAL_PROPERTY])
    61 _SPECIAL_PROPERTIES = frozenset([_KEY_SPECIAL_PROPERTY])
       
    62 
       
    63 _NAMESPACE_SEPARATOR='!'
    61 
    64 
    62 class UtcTzinfo(datetime.tzinfo):
    65 class UtcTzinfo(datetime.tzinfo):
    63   def utcoffset(self, dt): return datetime.timedelta(0)
    66   def utcoffset(self, dt): return datetime.timedelta(0)
    64   def dst(self, dt): return datetime.timedelta(0)
    67   def dst(self, dt): return datetime.timedelta(0)
    65   def tzname(self, dt): return 'UTC'
    68   def tzname(self, dt): return 'UTC'
    78 
    81 
    79 
    82 
    80 def ValidateString(value,
    83 def ValidateString(value,
    81                    name='unused',
    84                    name='unused',
    82                    exception=datastore_errors.BadValueError,
    85                    exception=datastore_errors.BadValueError,
    83                    max_len=_MAX_STRING_LENGTH):
    86                    max_len=_MAX_STRING_LENGTH,
       
    87                    empty_ok=False):
    84   """Raises an exception if value is not a valid string or a subclass thereof.
    88   """Raises an exception if value is not a valid string or a subclass thereof.
    85 
    89 
    86   A string is valid if it's not empty, no more than _MAX_STRING_LENGTH bytes,
    90   A string is valid if it's not empty, no more than _MAX_STRING_LENGTH bytes,
    87   and not a Blob. The exception type can be specified with the exception
    91   and not a Blob. The exception type can be specified with the exception
    88   argument; it defaults to BadValueError.
    92   argument; it defaults to BadValueError.
    89 
    93 
    90   Args:
    94   Args:
    91     value: the value to validate.
    95     value: the value to validate.
    92     name: the name of this value; used in the exception message.
    96     name: the name of this value; used in the exception message.
    93     exception: the type of exception to raise.
    97     exception: the type of exception to raise.
    94     max_len: the maximum allowed length, in bytes
    98     max_len: the maximum allowed length, in bytes.
    95   """
    99     empty_ok: allow empty value.
       
   100   """
       
   101   if value is None and empty_ok:
       
   102     return
    96   if not isinstance(value, basestring) or isinstance(value, Blob):
   103   if not isinstance(value, basestring) or isinstance(value, Blob):
    97     raise exception('%s should be a string; received %s (a %s):' %
   104     raise exception('%s should be a string; received %s (a %s):' %
    98                     (name, value, typename(value)))
   105                     (name, value, typename(value)))
    99   if not value:
   106   if not value and not empty_ok:
   100     raise exception('%s must not be empty.' % name)
   107     raise exception('%s must not be empty.' % name)
   101 
   108 
   102   if len(value.encode('utf-8')) > max_len:
   109   if len(value.encode('utf-8')) > max_len:
   103     raise exception('%s must be under %d bytes.' % (name, max_len))
   110     raise exception('%s must be under %d bytes.' % (name, max_len))
   104 
   111 
       
   112 def ValidateInteger(value,
       
   113                    name='unused',
       
   114                    exception=datastore_errors.BadValueError,
       
   115                    empty_ok=False,
       
   116                    zero_ok=False,
       
   117                    negative_ok=False):
       
   118   """Raises an exception if value is not a valid integer.
       
   119 
       
   120   An integer is valid if it's not negative or empty and is an integer.
       
   121   The exception type can be specified with the exception argument;
       
   122   it defaults to BadValueError.
       
   123 
       
   124   Args:
       
   125     value: the value to validate.
       
   126     name: the name of this value; used in the exception message.
       
   127     exception: the type of exception to raise.
       
   128     empty_ok: allow None value.
       
   129     zero_ok: allow zero value.
       
   130     negative_ok: allow negative value.
       
   131   """
       
   132   if value is None and empty_ok:
       
   133     return
       
   134   if not isinstance(value, int):
       
   135     raise exception('%s should be an integer; received %s (a %s).' %
       
   136                     (name, value, typename(value)))
       
   137   if not value and not zero_ok:
       
   138     raise exception('%s must not be 0 (zero)' % name)
       
   139   if value < 0 and not negative_ok:
       
   140     raise exception('%s must not be negative.' % name)
   105 
   141 
   106 def ResolveAppId(app, name='_app'):
   142 def ResolveAppId(app, name='_app'):
   107   """Validate app id, providing a default.
   143   """Validate app id, providing a default.
   108 
   144 
   109   If the argument is None, $APPLICATION_ID is substituted.
   145   If the argument is None, $APPLICATION_ID is substituted.
   120   """
   156   """
   121   if app is None:
   157   if app is None:
   122     app = os.environ.get('APPLICATION_ID', '')
   158     app = os.environ.get('APPLICATION_ID', '')
   123   ValidateString(app, '_app', datastore_errors.BadArgumentError)
   159   ValidateString(app, '_app', datastore_errors.BadArgumentError)
   124   return app
   160   return app
       
   161 
       
   162 
       
   163 class AppIdNamespace(object):
       
   164   """Combined AppId and Namespace
       
   165 
       
   166   An identifier that combines the application identifier and the
       
   167   namespace.
       
   168   """
       
   169   __app_id = None
       
   170   __namespace = None
       
   171 
       
   172   def __init__(self, app_id, namespace):
       
   173     """Constructor. Creates a AppIdNamespace from two strings.
       
   174 
       
   175     Args:
       
   176       app_id: application identifier string
       
   177       namespace: namespace identifier string
       
   178     Raises:
       
   179       BadArgumentError if the values contain
       
   180       the _NAMESPACE_SEPARATOR character (!) or
       
   181       the app_id is empty.
       
   182     """
       
   183     self.__app_id = app_id
       
   184     if namespace:
       
   185       self.__namespace = namespace
       
   186     else:
       
   187       self.__namespace = None
       
   188     ValidateString(self.__app_id, 'app_id', datastore_errors.BadArgumentError)
       
   189     ValidateString(self.__namespace,
       
   190                    'namespace', datastore_errors.BadArgumentError,
       
   191                    empty_ok=True)
       
   192     if _NAMESPACE_SEPARATOR in self.__app_id:
       
   193       raise datastore_errors.BadArgumentError(
       
   194         'app_id must not contain a "%s"' % _NAMESPACE_SEPARATOR)
       
   195     if self.__namespace and _NAMESPACE_SEPARATOR in self.__namespace:
       
   196       raise datastore_errors.BadArgumentError(
       
   197         'namespace must not contain a "%s"' % _NAMESPACE_SEPARATOR)
       
   198 
       
   199   def __cmp__(self, other):
       
   200     """Returns negative, zero, or positive when comparing two AppIdNamespace.
       
   201 
       
   202     Args:
       
   203       other: AppIdNamespace to compare to.
       
   204 
       
   205     Returns:
       
   206       Negative if self is less than "other"
       
   207       Zero if "other" is equal to self
       
   208       Positive if self is greater than "other"
       
   209     """
       
   210     if not isinstance(other, AppIdNamespace):
       
   211       return cmp(id(self), id(other))
       
   212     return cmp((self.__app_id, self.__namespace),
       
   213                (other.__app_id, other.__namespace))
       
   214 
       
   215   def to_encoded(self):
       
   216     """Returns this AppIdNamespace's string equivalent
       
   217 
       
   218     i.e. "app!namespace"
       
   219     """
       
   220     if not self.__namespace:
       
   221       return self.__app_id
       
   222     else:
       
   223       return self.__app_id + _NAMESPACE_SEPARATOR + self.__namespace
       
   224 
       
   225   def app_id(self):
       
   226     """Returns this AppId portion of this AppIdNamespace.
       
   227     """
       
   228     return self.__app_id;
       
   229 
       
   230   def namespace(self):
       
   231     """Returns this namespace portion of this AppIdNamespace.
       
   232     """
       
   233     return self.__namespace;
       
   234 
       
   235 
       
   236 def PartitionString(value, separator):
       
   237   """Equivalent to python2.5 str.partition()
       
   238      TODO(gmariani) use str.partition() when python 2.5 is adopted.
       
   239 
       
   240   Args:
       
   241     value: String to be partitioned
       
   242     separator: Separator string
       
   243   """
       
   244   index = value.find(separator);
       
   245   if index == -1:
       
   246     return (value, '', value[0:0]);
       
   247   else:
       
   248     return (value[0:index], separator, value[index+len(separator):len(value)])
       
   249 
       
   250 
       
   251 def parse_app_id_namespace(app_id_namespace):
       
   252   """
       
   253   An app_id_namespace string is valid if it's not empty, and contains
       
   254   at most one namespace separator ('!').  Also, an app_id_namespace
       
   255   with an empty namespace must not contain a namespace separator.
       
   256 
       
   257   Args:
       
   258     app_id_namespace: an encoded app_id_namespace.
       
   259   Raises exception if format of app_id_namespace is invalid.
       
   260   """
       
   261   if not app_id_namespace:
       
   262     raise datastore_errors.BadArgumentError(
       
   263         'app_id_namespace must be non empty')
       
   264   parts = PartitionString(app_id_namespace, _NAMESPACE_SEPARATOR)
       
   265   if parts[1] == _NAMESPACE_SEPARATOR:
       
   266     if not parts[2]:
       
   267       raise datastore_errors.BadArgumentError(
       
   268         'app_id_namespace must not contain a "%s" if the namespace is empty' %
       
   269         _NAMESPACE_SEPARATOR)
       
   270   if parts[2]:
       
   271     return AppIdNamespace(parts[0], parts[2])
       
   272   return AppIdNamespace(parts[0], None)
       
   273 
       
   274 def ResolveAppIdNamespace(
       
   275     app_id=None, namespace=None, app_id_namespace=None):
       
   276   """Validate an app id/namespace and substitute default values.
       
   277 
       
   278   If the argument is None, $APPLICATION_ID!$NAMESPACE is substituted.
       
   279 
       
   280   Args:
       
   281     app_id: The app id argument value to be validated.
       
   282     namespace: The namespace argument value to be validated.
       
   283     app_id_namespace: An AppId/Namespace pair
       
   284 
       
   285   Returns:
       
   286     An AppIdNamespace object initialized with AppId and Namespace.
       
   287 
       
   288   Raises:
       
   289     BadArgumentError if the value is empty or not a string.
       
   290   """
       
   291   if app_id_namespace is None:
       
   292     if app_id is None:
       
   293       app_id = os.environ.get('APPLICATION_ID', '')
       
   294     if namespace is None:
       
   295       namespace = namespace_manager.get_request_namespace();
       
   296   else:
       
   297     if not app_id is None:
       
   298       raise datastore_errors.BadArgumentError(
       
   299           'app_id is overspecified.  Cannot define app_id_namespace and app_id')
       
   300     if not namespace is None:
       
   301       raise datastore_errors.BadArgumentError(
       
   302           'namespace is overspecified.  ' +
       
   303           'Cannot define app_id_namespace and namespace')
       
   304     return parse_app_id_namespace(app_id_namespace)
       
   305 
       
   306   return AppIdNamespace(app_id, namespace)
   125 
   307 
   126 
   308 
   127 class Key(object):
   309 class Key(object):
   128   """The primary key for a datastore entity.
   310   """The primary key for a datastore entity.
   129 
   311 
   170         else:
   352         else:
   171           raise
   353           raise
   172     else:
   354     else:
   173       self.__reference = entity_pb.Reference()
   355       self.__reference = entity_pb.Reference()
   174 
   356 
       
   357   def to_path(self):
       
   358     """Construct the "path" of this key as a list.
       
   359 
       
   360     Returns:
       
   361       A list [kind_1, id_or_name_1, ..., kind_n, id_or_name_n] of the key path.
       
   362 
       
   363     Raises:
       
   364       datastore_errors.BadKeyError if this key does not have a valid path.
       
   365     """
       
   366     path = []
       
   367     for path_element in self.__reference.path().element_list():
       
   368       path.append(path_element.type().decode('utf-8'))
       
   369       if path_element.has_name():
       
   370         path.append(path_element.name().decode('utf-8'))
       
   371       elif path_element.has_id():
       
   372         path.append(path_element.id())
       
   373       else:
       
   374         raise datastore_errors.BadKeyError('Incomplete key found in to_path')
       
   375     return path
       
   376 
   175   @staticmethod
   377   @staticmethod
   176   def from_path(*args, **kwds):
   378   def from_path(*args, **kwds):
   177     """Static method to construct a Key out of a "path" (kind, id or name, ...).
   379     """Static method to construct a Key out of a "path" (kind, id or name, ...).
   178 
   380 
   179     This is useful when an application wants to use just the id or name portion
   381     This is useful when an application wants to use just the id or name portion
   200     Raises:
   402     Raises:
   201       BadArgumentError for invalid arguments.
   403       BadArgumentError for invalid arguments.
   202       BadKeyError if the parent key is incomplete.
   404       BadKeyError if the parent key is incomplete.
   203     """
   405     """
   204     parent = kwds.pop('parent', None)
   406     parent = kwds.pop('parent', None)
   205     _app = ResolveAppId(kwds.pop('_app', None))
   407     _app_id_namespace_obj = ResolveAppIdNamespace(
       
   408         kwds.pop('_app', None),
       
   409         kwds.pop('_namespace', None),
       
   410         kwds.pop('_app_id_namespace', None))
   206 
   411 
   207     if kwds:
   412     if kwds:
   208       raise datastore_errors.BadArgumentError(
   413       raise datastore_errors.BadArgumentError(
   209           'Excess keyword arguments ' + repr(kwds))
   414           'Excess keyword arguments ' + repr(kwds))
   210 
   415 
   219             'Expected None or a Key as parent; received %r (a %s).' %
   424             'Expected None or a Key as parent; received %r (a %s).' %
   220             (parent, typename(parent)))
   425             (parent, typename(parent)))
   221       if not parent.has_id_or_name():
   426       if not parent.has_id_or_name():
   222         raise datastore_errors.BadKeyError(
   427         raise datastore_errors.BadKeyError(
   223             'The parent Key is incomplete.')
   428             'The parent Key is incomplete.')
   224       if _app != parent.app():
   429       if _app_id_namespace_obj != parent.app_id_namespace():
   225         raise datastore_errors.BadArgumentError(
   430         raise datastore_errors.BadArgumentError(
   226             'The _app argument (%r) should match parent.app() (%s)' %
   431             'The app_id/namespace arguments (%r) should match ' +
   227             (_app, parent.app()))
   432             'parent.app_id_namespace().to_encoded() (%s)' %
       
   433             (_app_id_namespace_obj, parent.app_id_namespace()))
   228 
   434 
   229     key = Key()
   435     key = Key()
   230     ref = key.__reference
   436     ref = key.__reference
   231     if parent is not None:
   437     if parent is not None:
   232       ref.CopyFrom(parent.__reference)
   438       ref.CopyFrom(parent.__reference)
   233     else:
   439     else:
   234       ref.set_app(_app)
   440       ref.set_app(_app_id_namespace_obj.to_encoded())
   235 
   441 
   236     path = ref.mutable_path()
   442     path = ref.mutable_path()
   237     for i in xrange(0, len(args), 2):
   443     for i in xrange(0, len(args), 2):
   238       kind, id_or_name = args[i:i+2]
   444       kind, id_or_name = args[i:i+2]
   239       if isinstance(kind, basestring):
   445       if isinstance(kind, basestring):
   246       elem.set_type(kind)
   452       elem.set_type(kind)
   247       if isinstance(id_or_name, (int, long)):
   453       if isinstance(id_or_name, (int, long)):
   248         elem.set_id(id_or_name)
   454         elem.set_id(id_or_name)
   249       elif isinstance(id_or_name, basestring):
   455       elif isinstance(id_or_name, basestring):
   250         ValidateString(id_or_name, 'name')
   456         ValidateString(id_or_name, 'name')
   251         if id_or_name and id_or_name[0] in string.digits:
       
   252           raise datastore_errors.BadArgumentError(
       
   253             'Names may not begin with a digit; received %s.' % id_or_name)
       
   254         elem.set_name(id_or_name.encode('utf-8'))
   457         elem.set_name(id_or_name.encode('utf-8'))
   255       else:
   458       else:
   256         raise datastore_errors.BadArgumentError(
   459         raise datastore_errors.BadArgumentError(
   257             'Expected an integer id or string name as argument %d; '
   460             'Expected an integer id or string name as argument %d; '
   258             'received %r (a %s).' % (i + 2, id_or_name, typename(id_or_name)))
   461             'received %r (a %s).' % (i + 2, id_or_name, typename(id_or_name)))
   261     return key
   464     return key
   262 
   465 
   263   def app(self):
   466   def app(self):
   264     """Returns this entity's app id, a string."""
   467     """Returns this entity's app id, a string."""
   265     if self.__reference.app():
   468     if self.__reference.app():
   266       return self.__reference.app().decode('utf-8')
   469       return self.app_id_namespace().app_id().decode('utf-8')
       
   470     else:
       
   471       return None
       
   472 
       
   473   def namespace(self):
       
   474     """Returns this entity's app id, a string."""
       
   475     if self.__reference.app():
       
   476       return self.app_id_namespace().namespace().decode('utf-8')
       
   477     else:
       
   478       return None
       
   479 
       
   480   def app_id_namespace(self):
       
   481     """Returns this entity's app id/namespace, an appIdNamespace object."""
       
   482     if self.__reference.app():
       
   483       return parse_app_id_namespace(self.__reference.app())
   267     else:
   484     else:
   268       return None
   485       return None
   269 
   486 
   270   def kind(self):
   487   def kind(self):
   271     """Returns this entity's kind, as a string."""
   488     """Returns this entity's kind, as a string."""
   337     """
   554     """
   338     if not self.has_id_or_name():
   555     if not self.has_id_or_name():
   339       raise datastore_errors.BadKeyError(
   556       raise datastore_errors.BadKeyError(
   340         'ToTagUri() called for an entity with an incomplete key.')
   557         'ToTagUri() called for an entity with an incomplete key.')
   341 
   558 
   342     return u'tag:%s.%s,%s:%s[%s]' % (saxutils.escape(self.app()),
   559     return u'tag:%s.%s,%s:%s[%s]' % (
   343                                      os.environ['AUTH_DOMAIN'],
   560         saxutils.escape(self.app_id_namespace().to_encoded()),
   344                                      datetime.date.today().isoformat(),
   561         os.environ['AUTH_DOMAIN'],
   345                                      saxutils.escape(self.kind()),
   562         datetime.date.today().isoformat(),
   346                                      saxutils.escape(str(self)))
   563         saxutils.escape(self.kind()),
       
   564         saxutils.escape(str(self)))
       
   565 
   347   ToXml = ToTagUri
   566   ToXml = ToTagUri
   348 
   567 
   349   def entity_group(self):
   568   def entity_group(self):
   350     """Returns this key's entity group as a Key.
   569     """Returns this key's entity group as a Key.
   351 
   570 
   434       if elem.has_name():
   653       if elem.has_name():
   435         args.append(repr(elem.name().decode('utf-8')))
   654         args.append(repr(elem.name().decode('utf-8')))
   436       else:
   655       else:
   437         args.append(repr(elem.id()))
   656         args.append(repr(elem.id()))
   438 
   657 
   439     args.append('_app=%r' % self.__reference.app().decode('utf-8'))
   658     args.append('_app_id_namespace=%r' % self.__reference.app().decode('utf-8'))
   440     return u'datastore_types.Key.from_path(%s)' % ', '.join(args)
   659     return u'datastore_types.Key.from_path(%s)' % ', '.join(args)
   441 
   660 
   442   def __cmp__(self, other):
   661   def __cmp__(self, other):
   443     """Returns negative, zero, or positive when comparing two keys.
   662     """Returns negative, zero, or positive when comparing two keys.
   444 
   663 
   457       return -2
   676       return -2
   458 
   677 
   459     self_args = []
   678     self_args = []
   460     other_args = []
   679     other_args = []
   461 
   680 
   462     self_args.append(self.__reference.app().decode('utf-8'))
   681     self_args.append(self.__reference.app())
   463     other_args.append(other.__reference.app().decode('utf-8'))
   682     other_args.append(other.__reference.app())
   464 
   683 
   465     for elem in self.__reference.path().element_list():
   684     for elem in self.__reference.path().element_list():
   466       self_args.append(repr(elem.type()))
   685       self_args.append(elem.type())
   467       if elem.has_name():
   686       if elem.has_name():
   468         self_args.append(repr(elem.name().decode('utf-8')))
   687         self_args.append(elem.name())
   469       else:
   688       else:
   470         self_args.append(elem.id())
   689         self_args.append(elem.id())
   471 
   690 
   472     for elem in other.__reference.path().element_list():
   691     for elem in other.__reference.path().element_list():
   473       other_args.append(repr(elem.type()))
   692       other_args.append(elem.type())
   474       if elem.has_name():
   693       if elem.has_name():
   475         other_args.append(repr(elem.name().decode('utf-8')))
   694         other_args.append(elem.name())
   476       else:
   695       else:
   477         other_args.append(elem.id())
   696         other_args.append(elem.id())
   478 
   697 
   479     result = cmp(self_args, other_args)
   698     for self_component, other_component in zip(self_args, other_args):
   480     return result
   699       comparison = cmp(self_component, other_component)
       
   700       if comparison != 0:
       
   701         return comparison
       
   702 
       
   703     return cmp(len(self_args), len(other_args))
   481 
   704 
   482   def __hash__(self):
   705   def __hash__(self):
   483     """Returns a 32-bit integer hash of this key.
   706     """Returns a 32-bit integer hash of this key.
   484 
   707 
   485     Implements Python's hash protocol so that Keys may be used in sets and as
   708     Implements Python's hash protocol so that Keys may be used in sets and as
   696       try:
   919       try:
   697         other = IM(other)
   920         other = IM(other)
   698       except datastore_errors.BadValueError:
   921       except datastore_errors.BadValueError:
   699         return NotImplemented
   922         return NotImplemented
   700 
   923 
       
   924 
   701     return cmp((self.address, self.protocol),
   925     return cmp((self.address, self.protocol),
   702                (other.address, other.protocol))
   926                (other.address, other.protocol))
   703 
   927 
   704   def __repr__(self):
   928   def __repr__(self):
   705     """Returns an eval()able string representation of this IM.
   929     """Returns an eval()able string representation of this IM.
   896     Returns:
  1120     Returns:
   897       Base64 encoded version of itself for safe insertion in to an XML document.
  1121       Base64 encoded version of itself for safe insertion in to an XML document.
   898     """
  1122     """
   899     encoded = base64.urlsafe_b64encode(self)
  1123     encoded = base64.urlsafe_b64encode(self)
   900     return saxutils.escape(encoded)
  1124     return saxutils.escape(encoded)
       
  1125 
       
  1126 
       
  1127 class BlobKey(object):
       
  1128   """Key used to identify a blob in Blobstore.
       
  1129 
       
  1130   This object wraps a string that gets used internally by the Blobstore API
       
  1131   to identify application blobs.  The BlobKey corresponds to the entity name
       
  1132   of the underlying BlobReference entity.  The structure of the key is:
       
  1133 
       
  1134     _<blob-key>
       
  1135 
       
  1136   This class is exposed in the API in both google.appengine.ext.db and
       
  1137   google.appengine.ext.blobstore.
       
  1138   """
       
  1139 
       
  1140   def __init__(self, blob_key):
       
  1141     """Constructor.
       
  1142 
       
  1143     Used to convert a string to a BlobKey.  Normally used internally by
       
  1144     Blobstore API.
       
  1145 
       
  1146     Args:
       
  1147       blob_key:  Key name of BlobReference that this key belongs to.
       
  1148     """
       
  1149     self.__blob_key = blob_key
       
  1150 
       
  1151   def __str__(self):
       
  1152     """Convert to string."""
       
  1153     return self.__blob_key
       
  1154 
       
  1155   def __repr__(self):
       
  1156     """Returns an eval()able string representation of this key.
       
  1157 
       
  1158     Returns a Python string of the form 'datastore_types.BlobKey(...)'
       
  1159     that can be used to recreate this key.
       
  1160 
       
  1161     Returns:
       
  1162       string
       
  1163     """
       
  1164     s = type(self).__module__
       
  1165     return '%s.%s(%r)' % (type(self).__module__,
       
  1166                        type(self).__name__,
       
  1167                        self.__blob_key)
       
  1168 
       
  1169   def __cmp__(self, other):
       
  1170     if type(other) is type(self):
       
  1171       return cmp(str(self), str(other))
       
  1172     elif isinstance(other, basestring):
       
  1173       return cmp(self.__blob_key, other)
       
  1174     else:
       
  1175       return NotImplemented
       
  1176 
       
  1177   def __hash__(self):
       
  1178     return hash(self.__blob_key)
       
  1179 
       
  1180   def ToXml(self):
       
  1181     return str(self)
   901 
  1182 
   902 
  1183 
   903 _PROPERTY_MEANINGS = {
  1184 _PROPERTY_MEANINGS = {
   904 
  1185 
   905 
  1186 
   914   GeoPt:             entity_pb.Property.GEORSS_POINT,
  1195   GeoPt:             entity_pb.Property.GEORSS_POINT,
   915   IM:                entity_pb.Property.GD_IM,
  1196   IM:                entity_pb.Property.GD_IM,
   916   PhoneNumber:       entity_pb.Property.GD_PHONENUMBER,
  1197   PhoneNumber:       entity_pb.Property.GD_PHONENUMBER,
   917   PostalAddress:     entity_pb.Property.GD_POSTALADDRESS,
  1198   PostalAddress:     entity_pb.Property.GD_POSTALADDRESS,
   918   Rating:            entity_pb.Property.GD_RATING,
  1199   Rating:            entity_pb.Property.GD_RATING,
       
  1200   BlobKey:           entity_pb.Property.BLOBKEY,
   919 }
  1201 }
   920 
  1202 
   921 _PROPERTY_TYPES = frozenset([
  1203 _PROPERTY_TYPES = frozenset([
   922   Blob,
  1204   Blob,
   923   ByteString,
  1205   ByteString,
   938   str,
  1220   str,
   939   Text,
  1221   Text,
   940   type(None),
  1222   type(None),
   941   unicode,
  1223   unicode,
   942   users.User,
  1224   users.User,
       
  1225   BlobKey,
   943 ])
  1226 ])
   944 
  1227 
   945 _RAW_PROPERTY_TYPES = (Blob, Text)
  1228 _RAW_PROPERTY_TYPES = (Blob, Text)
   946 
  1229 
   947 def ValidatePropertyInteger(name, value):
  1230 def ValidatePropertyInteger(name, value):
  1041   str: ValidatePropertyString,
  1324   str: ValidatePropertyString,
  1042   Text: ValidatePropertyNothing,
  1325   Text: ValidatePropertyNothing,
  1043   type(None): ValidatePropertyNothing,
  1326   type(None): ValidatePropertyNothing,
  1044   unicode: ValidatePropertyString,
  1327   unicode: ValidatePropertyString,
  1045   users.User: ValidatePropertyNothing,
  1328   users.User: ValidatePropertyNothing,
       
  1329   BlobKey: ValidatePropertyString,
  1046 }
  1330 }
  1047 
  1331 
  1048 assert set(_VALIDATE_PROPERTY_VALUES.iterkeys()) == _PROPERTY_TYPES
  1332 assert set(_VALIDATE_PROPERTY_VALUES.iterkeys()) == _PROPERTY_TYPES
  1049 
  1333 
  1050 
  1334 
  1219     name: The name of the property as a string.
  1503     name: The name of the property as a string.
  1220     value: A float instance.
  1504     value: A float instance.
  1221     pbvalue: The entity_pb.PropertyValue to pack this value into.
  1505     pbvalue: The entity_pb.PropertyValue to pack this value into.
  1222   """
  1506   """
  1223   pbvalue.set_doublevalue(value)
  1507   pbvalue.set_doublevalue(value)
       
  1508 
  1224 
  1509 
  1225 _PACK_PROPERTY_VALUES = {
  1510 _PACK_PROPERTY_VALUES = {
  1226   Blob: PackBlob,
  1511   Blob: PackBlob,
  1227   ByteString: PackBlob,
  1512   ByteString: PackBlob,
  1228   bool: PackBool,
  1513   bool: PackBool,
  1242   str: PackString,
  1527   str: PackString,
  1243   Text: PackString,
  1528   Text: PackString,
  1244   type(None): lambda name, value, pbvalue: None,
  1529   type(None): lambda name, value, pbvalue: None,
  1245   unicode: PackString,
  1530   unicode: PackString,
  1246   users.User: PackUser,
  1531   users.User: PackUser,
       
  1532   BlobKey: PackString,
  1247 }
  1533 }
  1248 
  1534 
  1249 assert set(_PACK_PROPERTY_VALUES.iterkeys()) == _PROPERTY_TYPES
  1535 assert set(_PACK_PROPERTY_VALUES.iterkeys()) == _PROPERTY_TYPES
  1250 
  1536 
  1251 
  1537 
  1329 
  1615 
  1330     lambda val: _EPOCH + datetime.timedelta(microseconds=val),
  1616     lambda val: _EPOCH + datetime.timedelta(microseconds=val),
  1331   entity_pb.Property.ATOM_CATEGORY:     Category,
  1617   entity_pb.Property.ATOM_CATEGORY:     Category,
  1332   entity_pb.Property.ATOM_LINK:         Link,
  1618   entity_pb.Property.ATOM_LINK:         Link,
  1333   entity_pb.Property.GD_EMAIL:          Email,
  1619   entity_pb.Property.GD_EMAIL:          Email,
  1334   entity_pb.Property.GEORSS_POINT:      lambda coords: GeoPt(*coords),
       
  1335   entity_pb.Property.GD_IM:             IM,
  1620   entity_pb.Property.GD_IM:             IM,
  1336   entity_pb.Property.GD_PHONENUMBER:    PhoneNumber,
  1621   entity_pb.Property.GD_PHONENUMBER:    PhoneNumber,
  1337   entity_pb.Property.GD_POSTALADDRESS:  PostalAddress,
  1622   entity_pb.Property.GD_POSTALADDRESS:  PostalAddress,
  1338   entity_pb.Property.GD_RATING:         Rating,
  1623   entity_pb.Property.GD_RATING:         Rating,
  1339   entity_pb.Property.BLOB:              Blob,
  1624   entity_pb.Property.BLOB:              Blob,
  1340   entity_pb.Property.BYTESTRING:        ByteString,
  1625   entity_pb.Property.BYTESTRING:        ByteString,
  1341   entity_pb.Property.TEXT:              Text,
  1626   entity_pb.Property.TEXT:              Text,
       
  1627   entity_pb.Property.BLOBKEY:           BlobKey,
  1342 }
  1628 }
  1343 
  1629 
  1344 
  1630 
  1345 def FromPropertyPb(pb):
  1631 def FromPropertyPb(pb):
  1346   """Converts a property PB to a python value.
  1632   """Converts a property PB to a python value.
  1366   elif pbval.has_doublevalue():
  1652   elif pbval.has_doublevalue():
  1367     value = pbval.doublevalue()
  1653     value = pbval.doublevalue()
  1368   elif pbval.has_referencevalue():
  1654   elif pbval.has_referencevalue():
  1369     value = FromReferenceProperty(pbval)
  1655     value = FromReferenceProperty(pbval)
  1370   elif pbval.has_pointvalue():
  1656   elif pbval.has_pointvalue():
  1371     value = (pbval.pointvalue().x(), pbval.pointvalue().y())
  1657     value = GeoPt(pbval.pointvalue().x(), pbval.pointvalue().y())
  1372   elif pbval.has_uservalue():
  1658   elif pbval.has_uservalue():
  1373     email = unicode(pbval.uservalue().email().decode('utf-8'))
  1659     email = unicode(pbval.uservalue().email().decode('utf-8'))
  1374     auth_domain = unicode(pbval.uservalue().auth_domain().decode('utf-8'))
  1660     auth_domain = unicode(pbval.uservalue().auth_domain().decode('utf-8'))
  1375     obfuscated_gaiaid = pbval.uservalue().obfuscated_gaiaid().decode('utf-8')
  1661     obfuscated_gaiaid = pbval.uservalue().obfuscated_gaiaid().decode('utf-8')
  1376     obfuscated_gaiaid = unicode(obfuscated_gaiaid)
  1662     obfuscated_gaiaid = unicode(obfuscated_gaiaid)
  1379                        _user_id=obfuscated_gaiaid)
  1665                        _user_id=obfuscated_gaiaid)
  1380   else:
  1666   else:
  1381     value = None
  1667     value = None
  1382 
  1668 
  1383   try:
  1669   try:
  1384     if pb.has_meaning():
  1670     if pb.has_meaning() and pb.meaning() in _PROPERTY_CONVERSIONS:
  1385       conversion = _PROPERTY_CONVERSIONS[meaning]
  1671       conversion = _PROPERTY_CONVERSIONS[meaning]
  1386       value = conversion(value)
  1672       value = conversion(value)
  1387   except (KeyError, ValueError, IndexError, TypeError, AttributeError), msg:
  1673   except (KeyError, ValueError, IndexError, TypeError, AttributeError), msg:
  1388     raise datastore_errors.BadValueError(
  1674     raise datastore_errors.BadValueError(
  1389       'Error converting pb: %s\nException was: %s' % (pb, msg))
  1675       'Error converting pb: %s\nException was: %s' % (pb, msg))
  1435     'georss:point':     GeoPt,
  1721     'georss:point':     GeoPt,
  1436     'gd:im':            IM,
  1722     'gd:im':            IM,
  1437     'gd:phonenumber':   PhoneNumber,
  1723     'gd:phonenumber':   PhoneNumber,
  1438     'gd:postaladdress': PostalAddress,
  1724     'gd:postaladdress': PostalAddress,
  1439     'gd:rating':        Rating,
  1725     'gd:rating':        Rating,
       
  1726     'blobkey':          BlobKey,
  1440     }
  1727     }
  1441 
  1728 
  1442 
  1729 
  1443 def FromPropertyTypeName(type_name):
  1730 def FromPropertyTypeName(type_name):
  1444   """Returns the python type given a type name.
  1731   """Returns the python type given a type name.