3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
22 """Higher-level, semantic data types for the datastore. These types
23 are expected to be set as attributes of Entities. See "Supported Data Types"
26 Most of these types are based on XML elements from Atom and GData elements
27 from the atom and gd namespaces. For more information, see:
29 http://www.atomenabled.org/developers/syndication/
30 http://code.google.com/apis/gdata/common-elements.html
32 The namespace schemas are:
34 http://www.w3.org/2005/Atom
35 http://schemas.google.com/g/2005
54 from xml
.sax
import saxutils
56 from google
.appengine
.datastore
import entity_pb
58 from google
.appengine
.api
import datastore_errors
59 from google
.appengine
.api
import namespace_manager
60 from google
.appengine
.api
import users
61 from google
.appengine
.datastore
import datastore_pb
62 from google
.appengine
.datastore
import datastore_pbs
63 from google
.appengine
.datastore
import entity_v4_pb
64 from google
.appengine
.datastore
import sortable_pb_encoder
71 _MAX_STRING_LENGTH
= 500
82 _MAX_LINK_PROPERTY_LENGTH
= 2083
88 _MAX_RAW_PROPERTY_BYTES
= 1000 * 1000
96 RESERVED_PROPERTY_NAME
= re
.compile('^__.*__$')
104 KEY_SPECIAL_PROPERTY
= '__key__'
105 _KEY_SPECIAL_PROPERTY
= KEY_SPECIAL_PROPERTY
106 _UNAPPLIED_LOG_TIMESTAMP_SPECIAL_PROPERTY
= '__unapplied_log_timestamp_us__'
107 SCATTER_SPECIAL_PROPERTY
= '__scatter__'
108 _SPECIAL_PROPERTIES
= frozenset(
109 [KEY_SPECIAL_PROPERTY
,
110 _UNAPPLIED_LOG_TIMESTAMP_SPECIAL_PROPERTY
,
111 SCATTER_SPECIAL_PROPERTY
])
119 _NAMESPACE_SEPARATOR
= '!'
125 _EMPTY_NAMESPACE_ID
= 1
128 _EPOCH
= datetime
.datetime
.utcfromtimestamp(0)
134 class UtcTzinfo(datetime
.tzinfo
):
135 def utcoffset(self
, dt
): return datetime
.timedelta(0)
136 def dst(self
, dt
): return datetime
.timedelta(0)
137 def tzname(self
, dt
): return 'UTC'
138 def __repr__(self
): return 'datastore_types.UTC'
144 """Returns the type of obj as a string. More descriptive and specific than
145 type(obj), and safe for any object, unlike __class__."""
146 if hasattr(obj
, '__class__'):
147 return getattr(obj
, '__class__').__name
__
149 return type(obj
).__name
__
152 def ValidateString(value
,
154 exception
=datastore_errors
.BadValueError
,
155 max_len
=_MAX_STRING_LENGTH
,
157 """Raises an exception if value is not a valid string or a subclass thereof.
159 A string is valid if it's not empty, no more than _MAX_STRING_LENGTH bytes,
160 and not a Blob. The exception type can be specified with the exception
161 argument; it defaults to BadValueError.
164 value: the value to validate.
165 name: the name of this value; used in the exception message.
166 exception: the type of exception to raise.
167 max_len: the maximum allowed length, in bytes.
168 empty_ok: allow empty value.
170 if value
is None and empty_ok
:
172 if not isinstance(value
, basestring
) or isinstance(value
, Blob
):
173 raise exception('%s should be a string; received %s (a %s):' %
174 (name
, value
, typename(value
)))
175 if not value
and not empty_ok
:
176 raise exception('%s must not be empty.' % name
)
178 if len(value
.encode('utf-8')) > max_len
:
179 raise exception('%s must be under %d bytes.' % (name
, max_len
))
182 def ValidateInteger(value
,
184 exception
=datastore_errors
.BadValueError
,
188 """Raises an exception if value is not a valid integer.
190 An integer is valid if it's not negative or empty and is an integer
191 (either int or long). The exception type raised can be specified
192 with the exception argument; it defaults to BadValueError.
195 value: the value to validate.
196 name: the name of this value; used in the exception message.
197 exception: the type of exception to raise.
198 empty_ok: allow None value.
199 zero_ok: allow zero value.
200 negative_ok: allow negative value.
202 if value
is None and empty_ok
:
204 if not isinstance(value
, (int, long)):
205 raise exception('%s should be an integer; received %s (a %s).' %
206 (name
, value
, typename(value
)))
207 if not value
and not zero_ok
:
208 raise exception('%s must not be 0 (zero)' % name
)
209 if value
< 0 and not negative_ok
:
210 raise exception('%s must not be negative.' % name
)
212 def ResolveAppId(app
):
213 """Validate app id, providing a default.
215 If the argument is None, $APPLICATION_ID is substituted.
218 app: The app id argument value to be validated.
221 The value of app, or the substituted default. Always a non-empty string.
224 BadArgumentError if the value is empty or not a string.
227 app
= os
.environ
.get('APPLICATION_ID', '')
228 ValidateString(app
, 'app', datastore_errors
.BadArgumentError
)
232 def ResolveNamespace(namespace
):
233 """Validate app namespace, providing a default.
235 If the argument is None, namespace_manager.get_namespace() is substituted.
238 namespace: The namespace argument value to be validated.
241 The value of namespace, or the substituted default. The empty string is used
242 to denote the empty namespace.
245 BadArgumentError if the value is not a string.
247 if namespace
is None:
248 namespace
= namespace_manager
.get_namespace()
250 namespace_manager
.validate_namespace(
251 namespace
, datastore_errors
.BadArgumentError
)
255 def EncodeAppIdNamespace(app_id
, namespace
):
256 """Concatenates app id and namespace into a single string.
258 This method is needed for xml and datastore_file_stub.
261 app_id: The application id to encode
262 namespace: The namespace to encode
265 The string encoding for the app_id, namespace pair.
270 return app_id
+ _NAMESPACE_SEPARATOR
+ namespace
273 def DecodeAppIdNamespace(app_namespace_str
):
274 """Decodes app_namespace_str into an (app_id, namespace) pair.
276 This method is the reverse of EncodeAppIdNamespace and is needed for
280 app_namespace_str: An encoded app_id, namespace pair created by
284 (app_id, namespace) pair encoded in app_namespace_str
286 sep
= app_namespace_str
.find(_NAMESPACE_SEPARATOR
)
288 return (app_namespace_str
, '')
290 return (app_namespace_str
[0:sep
], app_namespace_str
[sep
+ 1:])
293 def SetNamespace(proto
, namespace
):
294 """Sets the namespace for a protocol buffer or clears the field.
297 proto: the protocol buffer to update
298 namespace: the new namespace (None or an empty string will clear out the
302 proto
.clear_name_space()
304 proto
.set_name_space(namespace
)
307 def PartitionString(value
, separator
):
308 """Equivalent to python2.5 str.partition()
309 TODO use str.partition() when python 2.5 is adopted.
312 value: String to be partitioned
313 separator: Separator string
315 index
= value
.find(separator
)
317 return (value
, '', value
[0:0])
319 return (value
[0:index
], separator
, value
[index
+len(separator
):len(value
)])
324 """The primary key for a datastore entity.
326 A datastore GUID. A Key instance uniquely identifies an entity across all
327 apps, and includes all information necessary to fetch the entity from the
328 datastore with Get().
330 Key implements __hash__, and key instances are immutable, so Keys may be
331 used in sets and as dictionary keys.
335 def __init__(self
, encoded
=None):
336 """Constructor. Creates a Key from a string.
339 # a base64-encoded primary key, generated by Key.__str__
343 if encoded
is not None:
344 if not isinstance(encoded
, basestring
):
346 repr_encoded
= repr(encoded
)
348 repr_encoded
= "<couldn't encode>"
349 raise datastore_errors
.BadArgumentError(
350 'Key() expects a string; received %s (a %s).' %
351 (repr_encoded
, typename(encoded
)))
354 modulo
= len(encoded
) % 4
356 encoded
+= ('=' * (4 - modulo
))
364 self
._str
= str(encoded
)
365 encoded_pb
= base64
.urlsafe_b64decode(self
._str
)
366 self
.__reference
= entity_pb
.Reference(encoded_pb
)
367 assert self
.__reference
.IsInitialized()
370 self
._str
= self
._str
.rstrip('=')
372 except (AssertionError, TypeError), e
:
373 raise datastore_errors
.BadKeyError(
374 'Invalid string key %s. Details: %s' % (encoded
, e
))
381 if e
.__class
__.__name
__ == 'ProtocolBufferDecodeError':
382 raise datastore_errors
.BadKeyError('Invalid string key %s.' % encoded
)
387 self
.__reference
= entity_pb
.Reference()
389 def to_path(self
, _default_id
=None, _decode
=True, _fail
=True):
390 """Construct the "path" of this key as a list.
393 A list [kind_1, id_or_name_1, ..., kind_n, id_or_name_n] of the key path.
396 datastore_errors.BadKeyError if this key does not have a valid path.
407 return s
.decode('utf-8')
408 except UnicodeDecodeError:
414 for path_element
in self
.__reference
.path().element_list():
415 path
.append(Decode(path_element
.type()))
416 if path_element
.has_name():
417 path
.append(Decode(path_element
.name()))
418 elif path_element
.has_id():
419 path
.append(path_element
.id())
420 elif _default_id
is not None:
421 path
.append(_default_id
)
423 raise datastore_errors
.BadKeyError('Incomplete key found in to_path')
427 def from_path(*args
, **kwds
):
428 """Static method to construct a Key out of a "path" (kind, id or name, ...).
430 This is useful when an application wants to use just the id or name portion
431 of a key in e.g. a URL, where the rest of the URL provides enough context to
432 fill in the rest, i.e. the app id (always implicit), the entity kind, and
433 possibly an ancestor key. Since ids and names are usually small, they're
434 more attractive for use in end-user-visible URLs than the full string
435 representation of a key.
438 kind: the entity kind (a str or unicode instance)
439 id_or_name: the id (an int or long) or name (a str or unicode instance)
440 parent: optional parent Key; default None.
441 namespace: optional namespace to use otherwise namespace_manager's
442 default namespace is used.
445 A new Key instance whose .kind() and .id() or .name() methods return
446 the *last* kind and id or name positional arguments passed.
449 BadArgumentError for invalid arguments.
450 BadKeyError if the parent key is incomplete.
453 parent
= kwds
.pop('parent', None)
455 app_id
= ResolveAppId(kwds
.pop('_app', None))
459 namespace
= kwds
.pop('namespace', None)
463 raise datastore_errors
.BadArgumentError(
464 'Excess keyword arguments ' + repr(kwds
))
467 if not args
or len(args
) % 2:
468 raise datastore_errors
.BadArgumentError(
469 'A non-zero even number of positional arguments is required '
470 '(kind, id or name, kind, id or name, ...); received %s' % repr(args
))
473 if parent
is not None:
474 if not isinstance(parent
, Key
):
475 raise datastore_errors
.BadArgumentError(
476 'Expected None or a Key as parent; received %r (a %s).' %
477 (parent
, typename(parent
)))
478 if namespace
is None:
479 namespace
= parent
.namespace()
480 if not parent
.has_id_or_name():
481 raise datastore_errors
.BadKeyError(
482 'The parent Key is incomplete.')
483 if app_id
!= parent
.app() or namespace
!= parent
.namespace():
484 raise datastore_errors
.BadArgumentError(
485 'The app/namespace arguments (%s/%s) should match '
486 'parent.app/namespace() (%s/%s)' %
487 (app_id
, namespace
, parent
.app(), parent
.namespace()))
490 namespace
= ResolveNamespace(namespace
)
494 ref
= key
.__reference
495 if parent
is not None:
496 ref
.CopyFrom(parent
.__reference
)
499 SetNamespace(ref
, namespace
)
503 path
= ref
.mutable_path()
504 for i
in xrange(0, len(args
), 2):
505 kind
, id_or_name
= args
[i
:i
+2]
506 if isinstance(kind
, basestring
):
507 kind
= kind
.encode('utf-8')
509 raise datastore_errors
.BadArgumentError(
510 'Expected a string kind as argument %d; received %r (a %s).' %
511 (i
+ 1, kind
, typename(kind
)))
512 elem
= path
.add_element()
514 if isinstance(id_or_name
, (int, long)):
515 elem
.set_id(id_or_name
)
516 elif isinstance(id_or_name
, basestring
):
517 ValidateString(id_or_name
, 'name')
518 elem
.set_name(id_or_name
.encode('utf-8'))
520 raise datastore_errors
.BadArgumentError(
521 'Expected an integer id or string name as argument %d; '
522 'received %r (a %s).' % (i
+ 2, id_or_name
, typename(id_or_name
)))
525 assert ref
.IsInitialized()
529 """Returns this entity's app id, a string."""
530 if self
.__reference
.app():
531 return self
.__reference
.app().decode('utf-8')
536 """Returns this entity's namespace, a string."""
537 if self
.__reference
.has_name_space():
538 return self
.__reference
.name_space().decode('utf-8')
545 """Returns this entity's kind, as a string."""
546 if self
.__reference
.path().element_size() > 0:
547 encoded
= self
.__reference
.path().element_list()[-1].type()
548 return unicode(encoded
.decode('utf-8'))
553 """Returns this entity's id, or None if it doesn't have one."""
554 elems
= self
.__reference
.path().element_list()
555 if elems
and elems
[-1].has_id() and elems
[-1].id():
556 return elems
[-1].id()
561 """Returns this entity's name, or None if it doesn't have one."""
562 elems
= self
.__reference
.path().element_list()
563 if elems
and elems
[-1].has_name() and elems
[-1].name():
564 return elems
[-1].name().decode('utf-8')
568 def id_or_name(self
):
569 """Returns this entity's id or name, whichever it has, or None."""
570 if self
.id() is not None:
576 def has_id_or_name(self
):
577 """Returns True if this entity has an id or name, False otherwise.
579 elems
= self
.__reference
.path().element_list()
582 return bool(e
.name() or e
.id())
587 """Returns this entity's parent, as a Key. If this entity has no parent,
589 if self
.__reference
.path().element_size() > 1:
591 parent
.__reference
.CopyFrom(self
.__reference
)
592 del parent
.__reference
.path().element_list()[-1]
598 """Returns a tag: URI for this entity for use in XML output.
600 Foreign keys for entities may be represented in XML output as tag URIs.
601 RFC 4151 describes the tag URI scheme. From http://taguri.org/:
603 The tag algorithm lets people mint - create - identifiers that no one
604 else using the same algorithm could ever mint. It is simple enough to do
605 in your head, and the resulting identifiers can be easy to read, write,
606 and remember. The identifiers conform to the URI (URL) Syntax.
608 Tag URIs for entities use the app's auth domain and the date that the URI
609 is generated. The namespace-specific part is <kind>[<key>].
611 For example, here is the tag URI for a Kitten with the key "Fluffy" in the
614 tag:catsinsinks.googleapps.com,2006-08-29:Kitten[Fluffy]
616 Raises a BadKeyError if this entity's key is incomplete.
618 if not self
.has_id_or_name():
619 raise datastore_errors
.BadKeyError(
620 'ToTagUri() called for an entity with an incomplete key.')
622 return u
'tag:%s.%s,%s:%s[%s]' % (
624 saxutils
.escape(EncodeAppIdNamespace(self
.app(), self
.namespace())),
625 os
.environ
['AUTH_DOMAIN'],
626 datetime
.date
.today().isoformat(),
627 saxutils
.escape(self
.kind()),
628 saxutils
.escape(str(self
)))
632 def entity_group(self
):
633 """Returns this key's entity group as a Key.
635 Note that the returned Key will be incomplete if this Key is for a root
636 entity and it is incomplete.
638 group
= Key
._FromPb
(self
.__reference
)
639 del group
.__reference
.path().element_list()[1:]
644 """Static factory method. Creates a Key from an entity_pb.Reference.
646 Not intended to be used by application developers. Enforced by hiding the
650 pb: entity_pb.Reference
652 if not isinstance(pb
, entity_pb
.Reference
):
653 raise datastore_errors
.BadArgumentError(
654 'Key constructor takes an entity_pb.Reference; received %s (a %s).' %
658 key
.__reference
= entity_pb
.Reference()
659 key
.__reference
.CopyFrom(pb
)
663 """Converts this Key to its protocol buffer representation.
665 Not intended to be used by application developers. Enforced by hiding the
669 # the Reference PB representation of this Key
672 pb
= entity_pb
.Reference()
673 pb
.CopyFrom(self
.__reference
)
674 if not self
.has_id_or_name():
675 pb
.mutable_path().element_list()[-1].set_id(0)
679 pb
.app().decode('utf-8')
680 for pathelem
in pb
.path().element_list():
681 pathelem
.type().decode('utf-8')
686 """Encodes this Key as an opaque string.
688 Returns a string representation of this key, suitable for use in HTML,
689 URLs, and other similar use cases. If the entity's key is incomplete,
690 raises a BadKeyError.
692 Unfortunately, this string encoding isn't particularly compact, and its
693 length varies with the length of the path. If you want a shorter identifier
694 and you know the kind and parent (if any) ahead of time, consider using just
695 the entity's id or name.
704 if self
._str
is not None:
706 except AttributeError:
708 if (self
.has_id_or_name()):
709 encoded
= base64
.urlsafe_b64encode(self
.__reference
.Encode())
710 self
._str
= encoded
.replace('=', '')
712 raise datastore_errors
.BadKeyError(
713 'Cannot string encode an incomplete key!\n%s' % self
.__reference
)
718 """Returns an eval()able string representation of this key.
720 Returns a Python string of the form 'datastore_types.Key.from_path(...)'
721 that can be used to recreate this key.
727 for elem
in self
.__reference
.path().element_list():
728 args
.append(repr(elem
.type().decode('utf-8')))
730 args
.append(repr(elem
.name().decode('utf-8')))
732 args
.append(repr(elem
.id()))
734 args
.append('_app=%r' % self
.__reference
.app().decode('utf-8'))
735 if self
.__reference
.has_name_space():
736 args
.append('namespace=%r' %
737 self
.__reference
.name_space().decode('utf-8'))
738 return u
'datastore_types.Key.from_path(%s)' % ', '.join(args
)
740 def __cmp__(self
, other
):
741 """Returns negative, zero, or positive when comparing two keys.
743 TODO: for API v2, we should change this to make incomplete keys, ie
744 keys without an id or name, not equal to any other keys.
747 other: Key to compare to.
750 Negative if self is less than "other"
751 Zero if "other" is equal to self
752 Positive if self is greater than "other"
754 if not isinstance(other
, Key
):
757 self_args
= [self
.__reference
.app(), self
.__reference
.name_space()]
758 self_args
+= self
.to_path(_default_id
=0, _decode
=False)
760 other_args
= [other
.__reference
.app(), other
.__reference
.name_space()]
761 other_args
+= other
.to_path(_default_id
=0, _decode
=False)
763 for self_component
, other_component
in zip(self_args
, other_args
):
764 comparison
= cmp(self_component
, other_component
)
768 return cmp(len(self_args
), len(other_args
))
771 """Returns an integer hash of this key.
773 Implements Python's hash protocol so that Keys may be used in sets and as
779 args
= self
.to_path(_default_id
=0, _fail
=False)
780 args
.append(self
.__reference
.app())
781 return hash(type(args
)) ^
hash(tuple(args
))
784 class _OverflowDateTime(long):
785 """Container for GD_WHEN values that don't fit into a datetime.datetime.
787 This class only exists to safely round-trip GD_WHEN values that are too large
788 to fit in a datetime.datetime instance e.g. that were created by Java
789 applications. It should not be created directly.
795 """Coverts a GD_WHEN value to the appropriate type."""
797 return _EPOCH
+ datetime
.timedelta(microseconds
=val
)
798 except OverflowError:
799 return _OverflowDateTime(val
)
810 class Category(unicode):
811 """A tag, ie a descriptive word or phrase. Entities may be tagged by users,
812 and later returned by a queries for that tag. Tags can also be used for
813 ranking results (frequency), photo captions, clustering, activity, etc.
815 Here's a more in-depth description: http://www.zeldman.com/daily/0405d.shtml
817 This is the Atom "category" element. In XML output, the tag is provided as
818 the term attribute. See:
819 http://www.atomenabled.org/developers/syndication/#category
821 Raises BadValueError if tag is not a string or subtype.
825 def __init__(self
, tag
):
826 super(Category
, self
).__init
__()
827 ValidateString(tag
, 'tag')
830 return u
'<category term="%s" label=%s />' % (Category
.TERM
,
831 saxutils
.quoteattr(self
))
835 """A fully qualified URL. Usually http: scheme, but may also be file:, ftp:,
838 If you have email (mailto:) or instant messaging (aim:, xmpp:) links,
839 consider using the Email or IM classes instead.
841 This is the Atom "link" element. In XML output, the link is provided as the
843 http://www.atomenabled.org/developers/syndication/#link
845 Raises BadValueError if link is not a fully qualified, well-formed URL.
847 def __init__(self
, link
):
848 super(Link
, self
).__init
__()
849 ValidateString(link
, 'link', max_len
=_MAX_LINK_PROPERTY_LENGTH
)
851 scheme
, domain
, path
, params
, query
, fragment
= urlparse
.urlparse(link
)
852 if (not scheme
or (scheme
!= 'file' and not domain
) or
853 (scheme
== 'file' and not path
)):
854 raise datastore_errors
.BadValueError('Invalid URL: %s' % link
)
857 return u
'<link href=%s />' % saxutils
.quoteattr(self
)
860 class Email(unicode):
861 """An RFC2822 email address. Makes no attempt at validation; apart from
862 checking MX records, email address validation is a rathole.
864 This is the gd:email element. In XML output, the email address is provided as
865 the address attribute. See:
866 http://code.google.com/apis/gdata/common-elements.html#gdEmail
868 Raises BadValueError if email is not a valid email address.
870 def __init__(self
, email
):
871 super(Email
, self
).__init
__()
872 ValidateString(email
, 'email')
875 return u
'<gd:email address=%s />' % saxutils
.quoteattr(self
)
879 """A geographical point, specified by floating-point latitude and longitude
880 coordinates. Often used to integrate with mapping sites like Google Maps.
881 May also be used as ICBM coordinates.
883 This is the georss:point element. In XML output, the coordinates are
884 provided as the lat and lon attributes. See: http://georss.org/
886 Serializes to '<lat>,<lon>'. Raises BadValueError if it's passed an invalid
887 serialized string, or if lat and lon are not valid floating points in the
888 ranges [-90, 90] and [-180, 180], respectively.
893 def __init__(self
, lat
, lon
=None):
897 split
= lat
.split(',')
899 except (AttributeError, ValueError):
900 raise datastore_errors
.BadValueError(
901 'Expected a "lat,long" formatted string; received %s (a %s).' %
902 (lat
, typename(lat
)))
908 raise datastore_errors
.BadValueError(
909 'Latitude must be between -90 and 90; received %f' % lat
)
911 raise datastore_errors
.BadValueError(
912 'Longitude must be between -180 and 180; received %f' % lon
)
913 except (TypeError, ValueError):
915 raise datastore_errors
.BadValueError(
916 'Expected floats for lat and long; received %s (a %s) and %s (a %s).' %
917 (lat
, typename(lat
), lon
, typename(lon
)))
922 def __cmp__(self
, other
):
923 if not isinstance(other
, GeoPt
):
926 except datastore_errors
.BadValueError
:
927 return NotImplemented
930 lat_cmp
= cmp(self
.lat
, other
.lat
)
934 return cmp(self
.lon
, other
.lon
)
937 """Returns an integer hash of this point.
939 Implements Python's hash protocol so that GeoPts may be used in sets and
945 return hash((self
.lat
, self
.lon
))
948 """Returns an eval()able string representation of this GeoPt.
950 The returned string is of the form 'datastore_types.GeoPt([lat], [lon])'.
955 return 'datastore_types.GeoPt(%r, %r)' % (self
.lat
, self
.lon
)
957 def __unicode__(self
):
958 return u
'%s,%s' % (unicode(self
.lat
), unicode(self
.lon
))
960 __str__
= __unicode__
963 return u
'<georss:point>%s %s</georss:point>' % (unicode(self
.lat
),
968 """An instant messaging handle. Includes both an address and its protocol.
969 The protocol value is either a standard IM scheme or a URL identifying the
970 IM network for the protocol. Possible values include:
974 unknown Unknown or unspecified
978 http://talk.google.com/ Google Talk
979 http://messenger.msn.com/ MSN Messenger
980 http://messenger.yahoo.com/ Yahoo Messenger
981 http://sametime.com/ Lotus Sametime
982 http://gadu-gadu.pl/ Gadu-Gadu
984 This is the gd:im element. In XML output, the address and protocol are
985 provided as the address and protocol attributes, respectively. See:
986 http://code.google.com/apis/gdata/common-elements.html#gdIm
988 Serializes to '<protocol> <address>'. Raises BadValueError if tag is not a
989 standard IM scheme or a URL.
991 PROTOCOLS
= [ 'sip', 'unknown', 'xmpp' ]
996 def __init__(self
, protocol
, address
=None):
1000 split
= protocol
.split(' ', 1)
1001 protocol
, address
= split
1002 except (AttributeError, ValueError):
1003 raise datastore_errors
.BadValueError(
1004 'Expected string of format "protocol address"; received %s' %
1007 ValidateString(address
, 'address')
1008 if protocol
not in self
.PROTOCOLS
:
1012 self
.address
= address
1013 self
.protocol
= protocol
1015 def __cmp__(self
, other
):
1016 if not isinstance(other
, IM
):
1019 except datastore_errors
.BadValueError
:
1020 return NotImplemented
1032 return cmp((self
.address
, self
.protocol
),
1033 (other
.address
, other
.protocol
))
1036 """Returns an eval()able string representation of this IM.
1038 The returned string is of the form:
1040 datastore_types.IM('address', 'protocol')
1045 return 'datastore_types.IM(%r, %r)' % (self
.protocol
, self
.address
)
1047 def __unicode__(self
):
1048 return u
'%s %s' % (self
.protocol
, self
.address
)
1050 __str__
= __unicode__
1053 return (u
'<gd:im protocol=%s address=%s />' %
1054 (saxutils
.quoteattr(self
.protocol
),
1055 saxutils
.quoteattr(self
.address
)))
1058 return len(unicode(self
))
1061 class PhoneNumber(unicode):
1062 """A human-readable phone number or address.
1064 No validation is performed. Phone numbers have many different formats -
1065 local, long distance, domestic, international, internal extension, TTY,
1066 VOIP, SMS, and alternative networks like Skype, XFire and Roger Wilco. They
1067 all have their own numbering and addressing formats.
1069 This is the gd:phoneNumber element. In XML output, the phone number is
1070 provided as the text of the element. See:
1071 http://code.google.com/apis/gdata/common-elements.html#gdPhoneNumber
1073 Raises BadValueError if phone is not a string or subtype.
1075 def __init__(self
, phone
):
1076 super(PhoneNumber
, self
).__init
__()
1077 ValidateString(phone
, 'phone')
1080 return u
'<gd:phoneNumber>%s</gd:phoneNumber>' % saxutils
.escape(self
)
1083 class PostalAddress(unicode):
1084 """A human-readable mailing address. Again, mailing address formats vary
1085 widely, so no validation is performed.
1087 This is the gd:postalAddress element. In XML output, the address is provided
1088 as the text of the element. See:
1089 http://code.google.com/apis/gdata/common-elements.html#gdPostalAddress
1091 Raises BadValueError if address is not a string or subtype.
1093 def __init__(self
, address
):
1094 super(PostalAddress
, self
).__init
__()
1095 ValidateString(address
, 'address')
1098 return u
'<gd:postalAddress>%s</gd:postalAddress>' % saxutils
.escape(self
)
1102 """A user-provided integer rating for a piece of content. Normalized to a
1105 This is the gd:rating element. In XML output, the address is provided
1106 as the text of the element. See:
1107 http://code.google.com/apis/gdata/common-elements.html#gdRating
1109 Serializes to the decimal string representation of the rating. Raises
1110 BadValueError if the rating is not an integer in the range [0, 100].
1115 def __init__(self
, rating
):
1116 super(Rating
, self
).__init
__()
1117 if isinstance(rating
, float) or isinstance(rating
, complex):
1119 raise datastore_errors
.BadValueError(
1120 'Expected int or long; received %s (a %s).' %
1121 (rating
, typename(rating
)))
1124 if long(rating
) < Rating
.MIN
or long(rating
) > Rating
.MAX
:
1125 raise datastore_errors
.BadValueError()
1128 raise datastore_errors
.BadValueError(
1129 'Expected int or long; received %s (a %s).' %
1130 (rating
, typename(rating
)))
1133 return (u
'<gd:rating value="%d" min="%d" max="%d" />' %
1134 (self
, Rating
.MIN
, Rating
.MAX
))
1137 class Text(unicode):
1138 """A long string type.
1140 Strings of any length can be stored in the datastore using this
1141 type. It behaves identically to the Python unicode type, except for
1142 the constructor, which only accepts str and unicode arguments.
1145 def __new__(cls
, arg
=None, encoding
=None):
1148 We only accept unicode and str instances, the latter with encoding.
1151 arg: optional unicode or str instance; default u''
1152 encoding: optional encoding; disallowed when isinstance(arg, unicode),
1153 defaults to 'ascii' when isinstance(arg, str);
1157 if isinstance(arg
, unicode):
1158 if encoding
is not None:
1159 raise TypeError('Text() with a unicode argument '
1160 'should not specify an encoding')
1161 return super(Text
, cls
).__new
__(cls
, arg
)
1163 if isinstance(arg
, str):
1164 if encoding
is None:
1166 return super(Text
, cls
).__new
__(cls
, arg
, encoding
)
1168 raise TypeError('Text() argument should be str or unicode, not %s' %
1171 class _BaseByteType(str):
1172 """A base class for datastore types that are encoded as bytes.
1174 This behaves identically to the Python str type, except for the
1175 constructor, which only accepts str arguments.
1178 def __new__(cls
, arg
=None):
1181 We only accept str instances.
1184 arg: optional str instance (default '')
1188 if isinstance(arg
, str):
1189 return super(_BaseByteType
, cls
).__new
__(cls
, arg
)
1191 raise TypeError('%s() argument should be str instance, not %s' %
1192 (cls
.__name
__, type(arg
).__name
__))
1195 """Output bytes as XML.
1198 Base64 encoded version of itself for safe insertion in to an XML document.
1200 encoded
= base64
.urlsafe_b64encode(self
)
1201 return saxutils
.escape(encoded
)
1204 class Blob(_BaseByteType
):
1205 """A blob type, appropriate for storing binary data of any length.
1207 This behaves identically to the Python str type, except for the
1208 constructor, which only accepts str arguments.
1213 class EmbeddedEntity(_BaseByteType
):
1214 """A proto encoded EntityProto.
1216 This behaves identically to Blob, except for the
1217 constructor, which accepts a str or EntityProto argument.
1219 Can be decoded using datastore.Entity.FromProto(), db.model_from_protobuf() or
1220 ndb.LocalStructuredProperty.
1223 def __new__(cls
, arg
=None):
1227 arg: optional str or EntityProto instance (default '')
1229 if isinstance(arg
, entity_pb
.EntityProto
):
1230 arg
= arg
.SerializePartialToString()
1231 return super(EmbeddedEntity
, cls
).__new
__(cls
, arg
)
1234 class ByteString(_BaseByteType
):
1235 """A byte-string type, appropriate for storing short amounts of indexed data.
1237 This behaves identically to Blob, except it's used only for short, indexed
1243 class BlobKey(object):
1244 """Key used to identify a blob in Blobstore.
1246 This object wraps a string that gets used internally by the Blobstore API
1247 to identify application blobs. The BlobKey corresponds to the entity name
1248 of the underlying BlobReference entity.
1250 This class is exposed in the API in both google.appengine.ext.db and
1251 google.appengine.ext.blobstore.
1254 def __init__(self
, blob_key
):
1257 Used to convert a string to a BlobKey. Normally used internally by
1261 blob_key: Key name of BlobReference that this key belongs to.
1263 ValidateString(blob_key
, 'blob-key')
1264 self
.__blob
_key
= blob_key
1267 """Convert to string."""
1268 return self
.__blob
_key
1271 """Returns an eval()able string representation of this key.
1273 Returns a Python string of the form 'datastore_types.BlobKey(...)'
1274 that can be used to recreate this key.
1279 return 'datastore_types.%s(%r)' % (type(self
).__name
__, self
.__blob
_key
)
1281 def __cmp__(self
, other
):
1284 if type(other
) is type(self
):
1285 return cmp(str(self
), str(other
))
1286 elif isinstance(other
, basestring
):
1287 return cmp(self
.__blob
_key
, other
)
1289 return NotImplemented
1292 return hash(self
.__blob
_key
)
1299 _PROPERTY_MEANINGS
= {
1303 Blob
: entity_pb
.Property
.BLOB
,
1304 EmbeddedEntity
: entity_pb
.Property
.ENTITY_PROTO
,
1305 ByteString
: entity_pb
.Property
.BYTESTRING
,
1306 Text
: entity_pb
.Property
.TEXT
,
1307 datetime
.datetime
: entity_pb
.Property
.GD_WHEN
,
1308 datetime
.date
: entity_pb
.Property
.GD_WHEN
,
1309 datetime
.time
: entity_pb
.Property
.GD_WHEN
,
1310 _OverflowDateTime
: entity_pb
.Property
.GD_WHEN
,
1311 Category
: entity_pb
.Property
.ATOM_CATEGORY
,
1312 Link
: entity_pb
.Property
.ATOM_LINK
,
1313 Email
: entity_pb
.Property
.GD_EMAIL
,
1314 GeoPt
: entity_pb
.Property
.GEORSS_POINT
,
1315 IM
: entity_pb
.Property
.GD_IM
,
1316 PhoneNumber
: entity_pb
.Property
.GD_PHONENUMBER
,
1317 PostalAddress
: entity_pb
.Property
.GD_POSTALADDRESS
,
1318 Rating
: entity_pb
.Property
.GD_RATING
,
1319 BlobKey
: entity_pb
.Property
.BLOBKEY
,
1323 _PROPERTY_TYPES
= frozenset([
1353 _RAW_PROPERTY_TYPES
= (Blob
, Text
, EmbeddedEntity
)
1354 _RAW_PROPERTY_MEANINGS
= (entity_pb
.Property
.BLOB
, entity_pb
.Property
.TEXT
,
1355 entity_pb
.Property
.ENTITY_PROTO
)
1358 def ValidatePropertyInteger(name
, value
):
1359 """Raises an exception if the supplied integer is invalid.
1362 name: Name of the property this is for.
1363 value: Integer value.
1366 OverflowError if the value does not fit within a signed int64.
1368 if not (-0x8000000000000000 <= value
<= 0x7fffffffffffffff):
1369 raise OverflowError('%d is out of bounds for int64' % value
)
1372 def ValidateStringLength(name
, value
, max_len
):
1373 """Raises an exception if the supplied string is too long.
1376 name: Name of the property this is for.
1377 value: String value.
1378 max_len: Maximum length the string may be.
1381 OverflowError if the value is larger than the maximum length.
1383 if len(value
) > max_len
:
1384 raise datastore_errors
.BadValueError(
1385 'Property %s is %d bytes long; it must be %d or less. '
1386 'Consider Text instead, which can store strings of any length.' %
1387 (name
, len(value
), max_len
))
1390 def ValidatePropertyString(name
, value
):
1391 """Validates the length of an indexed string property.
1394 name: Name of the property this is for.
1395 value: String value.
1397 ValidateStringLength(name
, value
, max_len
=_MAX_STRING_LENGTH
)
1400 def ValidatePropertyLink(name
, value
):
1401 """Validates the length of an indexed Link property.
1404 name: Name of the property this is for.
1405 value: String value.
1407 ValidateStringLength(name
, value
, max_len
=_MAX_LINK_PROPERTY_LENGTH
)
1410 def ValidatePropertyNothing(name
, value
):
1411 """No-op validation function.
1414 name: Name of the property this is for.
1420 def ValidatePropertyKey(name
, value
):
1421 """Raises an exception if the supplied datastore.Key instance is invalid.
1424 name: Name of the property this is for.
1425 value: A datastore.Key instance.
1428 datastore_errors.BadValueError if the value is invalid.
1430 if not value
.has_id_or_name():
1431 raise datastore_errors
.BadValueError(
1432 'Incomplete key found for reference property %s.' % name
)
1440 _VALIDATE_PROPERTY_VALUES
= {
1441 Blob
: ValidatePropertyNothing
,
1442 EmbeddedEntity
: ValidatePropertyNothing
,
1443 ByteString
: ValidatePropertyNothing
,
1444 bool: ValidatePropertyNothing
,
1445 Category
: ValidatePropertyNothing
,
1446 datetime
.datetime
: ValidatePropertyNothing
,
1447 _OverflowDateTime
: ValidatePropertyInteger
,
1448 Email
: ValidatePropertyNothing
,
1449 float: ValidatePropertyNothing
,
1450 GeoPt
: ValidatePropertyNothing
,
1451 IM
: ValidatePropertyNothing
,
1452 int: ValidatePropertyInteger
,
1453 Key
: ValidatePropertyKey
,
1454 Link
: ValidatePropertyNothing
,
1455 long: ValidatePropertyInteger
,
1456 PhoneNumber
: ValidatePropertyNothing
,
1457 PostalAddress
: ValidatePropertyNothing
,
1458 Rating
: ValidatePropertyInteger
,
1459 str: ValidatePropertyNothing
,
1460 Text
: ValidatePropertyNothing
,
1461 type(None): ValidatePropertyNothing
,
1462 unicode: ValidatePropertyNothing
,
1463 users
.User
: ValidatePropertyNothing
,
1464 BlobKey
: ValidatePropertyNothing
,
1467 _PROPERTY_TYPE_TO_INDEX_VALUE_TYPE
= {
1470 EmbeddedEntity
: str,
1474 datetime
.datetime
: long,
1475 datetime
.date
: long,
1476 datetime
.time
: long,
1477 _OverflowDateTime
: long,
1491 type(None): type(None),
1493 users
.User
: users
.User
,
1498 assert set(_VALIDATE_PROPERTY_VALUES
.iterkeys()) == _PROPERTY_TYPES
1501 def ValidateProperty(name
, values
, read_only
=False):
1502 """Helper function for validating property values.
1505 name: Name of the property this is for.
1506 value: Value for the property as a Python native type.
1507 read_only: deprecated
1510 BadPropertyError if the property name is invalid. BadValueError if the
1511 property did not validate correctly or the value was an empty list. Other
1512 exception types (like OverflowError) if the property value does not meet
1513 type-specific criteria.
1515 ValidateString(name
, 'property name', datastore_errors
.BadPropertyError
)
1517 values_type
= type(values
)
1520 if values_type
is tuple:
1521 raise datastore_errors
.BadValueError(
1522 'May not use tuple property value; property %s is %s.' %
1523 (name
, repr(values
)))
1526 if values_type
is not list:
1531 raise datastore_errors
.BadValueError(
1532 'May not use the empty list as a property value; property %s is %s.' %
1533 (name
, repr(values
)))
1539 prop_validator
= _VALIDATE_PROPERTY_VALUES
.get(v
.__class
__)
1540 if prop_validator
is None:
1541 raise datastore_errors
.BadValueError(
1542 'Unsupported type for property %s: %s' % (name
, v
.__class
__))
1543 prop_validator(name
, v
)
1545 except (KeyError, ValueError, TypeError, IndexError, AttributeError), msg
:
1546 raise datastore_errors
.BadValueError(
1547 'Error type checking values for property %s: %s' % (name
, msg
))
1553 ValidateReadProperty
= ValidateProperty
1557 def PackBlob(name
, value
, pbvalue
):
1558 """Packs a Blob property into a entity_pb.PropertyValue.
1561 name: The name of the property as a string.
1562 value: A Blob instance.
1563 pbvalue: The entity_pb.PropertyValue to pack this value into.
1565 pbvalue
.set_stringvalue(value
)
1568 def PackString(name
, value
, pbvalue
):
1569 """Packs a string-typed property into a entity_pb.PropertyValue.
1572 name: The name of the property as a string.
1573 value: A string, unicode, or string-like value instance.
1574 pbvalue: The entity_pb.PropertyValue to pack this value into.
1576 pbvalue
.set_stringvalue(unicode(value
).encode('utf-8'))
1579 def PackDatetime(name
, value
, pbvalue
):
1580 """Packs a datetime-typed property into a entity_pb.PropertyValue.
1583 name: The name of the property as a string.
1584 value: A datetime.datetime instance.
1585 pbvalue: The entity_pb.PropertyValue to pack this value into.
1587 pbvalue
.set_int64value(DatetimeToTimestamp(value
))
1590 def DatetimeToTimestamp(value
):
1591 """Converts a datetime.datetime to microseconds since the epoch, as a float.
1593 value: datetime.datetime
1595 Returns: value as a long
1599 value
= value
.astimezone(UTC
)
1600 return long(calendar
.timegm(value
.timetuple()) * 1000000L) + value
.microsecond
1603 def PackGeoPt(name
, value
, pbvalue
):
1604 """Packs a GeoPt property into a entity_pb.PropertyValue.
1607 name: The name of the property as a string.
1608 value: A GeoPt instance.
1609 pbvalue: The entity_pb.PropertyValue to pack this value into.
1611 pbvalue
.mutable_pointvalue().set_x(value
.lat
)
1612 pbvalue
.mutable_pointvalue().set_y(value
.lon
)
1615 def PackUser(name
, value
, pbvalue
):
1616 """Packs a User property into a entity_pb.PropertyValue.
1619 name: The name of the property as a string.
1620 value: A users.User instance.
1621 pbvalue: The entity_pb.PropertyValue to pack this value into.
1623 pbvalue
.mutable_uservalue().set_email(value
.email().encode('utf-8'))
1624 pbvalue
.mutable_uservalue().set_auth_domain(
1625 value
.auth_domain().encode('utf-8'))
1626 pbvalue
.mutable_uservalue().set_gaiaid(0)
1631 if value
.user_id() is not None:
1632 pbvalue
.mutable_uservalue().set_obfuscated_gaiaid(
1633 value
.user_id().encode('utf-8'))
1635 if value
.federated_identity() is not None:
1636 pbvalue
.mutable_uservalue().set_federated_identity(
1637 value
.federated_identity().encode('utf-8'))
1639 if value
.federated_provider() is not None:
1640 pbvalue
.mutable_uservalue().set_federated_provider(
1641 value
.federated_provider().encode('utf-8'))
1644 def PackKey(name
, value
, pbvalue
):
1645 """Packs a reference property into a entity_pb.PropertyValue.
1648 name: The name of the property as a string.
1649 value: A Key instance.
1650 pbvalue: The entity_pb.PropertyValue to pack this value into.
1652 ref
= value
._Key
__reference
1653 pbvalue
.mutable_referencevalue().set_app(ref
.app())
1654 SetNamespace(pbvalue
.mutable_referencevalue(), ref
.name_space())
1655 for elem
in ref
.path().element_list():
1656 pbvalue
.mutable_referencevalue().add_pathelement().CopyFrom(elem
)
1659 def PackBool(name
, value
, pbvalue
):
1660 """Packs a boolean property into a entity_pb.PropertyValue.
1663 name: The name of the property as a string.
1664 value: A boolean instance.
1665 pbvalue: The entity_pb.PropertyValue to pack this value into.
1667 pbvalue
.set_booleanvalue(value
)
1670 def PackInteger(name
, value
, pbvalue
):
1671 """Packs an integer property into a entity_pb.PropertyValue.
1674 name: The name of the property as a string.
1675 value: An int or long instance.
1676 pbvalue: The entity_pb.PropertyValue to pack this value into.
1678 pbvalue
.set_int64value(value
)
1681 def PackFloat(name
, value
, pbvalue
):
1682 """Packs a float property into a entity_pb.PropertyValue.
1685 name: The name of the property as a string.
1686 value: A float instance.
1687 pbvalue: The entity_pb.PropertyValue to pack this value into.
1689 pbvalue
.set_doublevalue(value
)
1697 _PACK_PROPERTY_VALUES
= {
1699 EmbeddedEntity
: PackBlob
,
1700 ByteString
: PackBlob
,
1702 Category
: PackString
,
1703 datetime
.datetime
: PackDatetime
,
1704 _OverflowDateTime
: PackInteger
,
1713 PhoneNumber
: PackString
,
1714 PostalAddress
: PackString
,
1715 Rating
: PackInteger
,
1718 type(None): lambda name
, value
, pbvalue
: None,
1719 unicode: PackString
,
1720 users
.User
: PackUser
,
1721 BlobKey
: PackString
,
1725 assert set(_PACK_PROPERTY_VALUES
.iterkeys()) == _PROPERTY_TYPES
1728 def ToPropertyPb(name
, values
):
1729 """Creates type-specific entity_pb.PropertyValues.
1731 Determines the type and meaning of the PropertyValue based on the Python
1732 type of the input value(s).
1734 NOTE: This function does not validate anything!
1737 name: string or unicode; the property name
1738 values: The values for this property, either a single one or a list of them.
1739 All values must be a supported type. Lists of values must all be of the
1743 A list of entity_pb.Property instances.
1745 encoded_name
= name
.encode('utf-8')
1747 values_type
= type(values
)
1748 if values_type
is list:
1756 pb
= entity_pb
.Property()
1757 pb
.set_name(encoded_name
)
1758 pb
.set_multiple(multiple
)
1760 meaning
= _PROPERTY_MEANINGS
.get(v
.__class
__)
1761 if meaning
is not None:
1762 pb
.set_meaning(meaning
)
1764 pack_prop
= _PACK_PROPERTY_VALUES
[v
.__class
__]
1765 pbvalue
= pack_prop(name
, v
, pb
.mutable_value())
1774 def FromReferenceProperty(value
):
1775 """Converts a reference PropertyValue to a Key.
1778 value: entity_pb.PropertyValue
1784 BadValueError if the value is not a PropertyValue.
1786 assert isinstance(value
, entity_pb
.PropertyValue
)
1787 assert value
.has_referencevalue()
1788 ref
= value
.referencevalue()
1791 key_ref
= key
._Key
__reference
1792 key_ref
.set_app(ref
.app())
1793 SetNamespace(key_ref
, ref
.name_space())
1795 for pathelem
in ref
.pathelement_list():
1796 key_ref
.mutable_path().add_element().CopyFrom(pathelem
)
1808 _PROPERTY_CONVERSIONS
= {
1809 entity_pb
.Property
.GD_WHEN
: _When
,
1810 entity_pb
.Property
.ATOM_CATEGORY
: Category
,
1811 entity_pb
.Property
.ATOM_LINK
: Link
,
1812 entity_pb
.Property
.GD_EMAIL
: Email
,
1813 entity_pb
.Property
.GD_IM
: IM
,
1814 entity_pb
.Property
.GD_PHONENUMBER
: PhoneNumber
,
1815 entity_pb
.Property
.GD_POSTALADDRESS
: PostalAddress
,
1816 entity_pb
.Property
.GD_RATING
: Rating
,
1817 entity_pb
.Property
.BLOB
: Blob
,
1818 entity_pb
.Property
.ENTITY_PROTO
: EmbeddedEntity
,
1819 entity_pb
.Property
.BYTESTRING
: ByteString
,
1820 entity_pb
.Property
.TEXT
: Text
,
1821 entity_pb
.Property
.BLOBKEY
: BlobKey
,
1825 _NON_UTF8_MEANINGS
= frozenset((entity_pb
.Property
.BLOB
,
1826 entity_pb
.Property
.ENTITY_PROTO
,
1827 entity_pb
.Property
.BYTESTRING
,
1828 entity_pb
.Property
.INDEX_VALUE
))
1831 def FromPropertyPb(pb
):
1832 """Converts a property PB to a python value.
1835 pb: entity_pb.Property
1838 # return type is determined by the type of the argument
1839 string, int, bool, double, users.User, or one of the atom or gd types
1845 meaning
= pb
.meaning()
1847 if pbval
.has_stringvalue():
1848 value
= pbval
.stringvalue()
1849 if not pb
.has_meaning() or meaning
not in _NON_UTF8_MEANINGS
:
1850 value
= unicode(value
, 'utf-8')
1851 elif pbval
.has_int64value():
1854 value
= long(pbval
.int64value())
1855 elif pbval
.has_booleanvalue():
1858 value
= bool(pbval
.booleanvalue())
1859 elif pbval
.has_doublevalue():
1860 value
= pbval
.doublevalue()
1861 elif pbval
.has_referencevalue():
1862 value
= FromReferenceProperty(pbval
)
1863 elif pbval
.has_pointvalue():
1864 value
= GeoPt(pbval
.pointvalue().x(), pbval
.pointvalue().y())
1865 elif pbval
.has_uservalue():
1866 email
= unicode(pbval
.uservalue().email(), 'utf-8')
1867 auth_domain
= unicode(pbval
.uservalue().auth_domain(), 'utf-8')
1868 obfuscated_gaiaid
= pbval
.uservalue().obfuscated_gaiaid().decode('utf-8')
1869 obfuscated_gaiaid
= unicode(pbval
.uservalue().obfuscated_gaiaid(), 'utf-8')
1871 federated_identity
= None
1872 if pbval
.uservalue().has_federated_identity():
1873 federated_identity
= unicode(pbval
.uservalue().federated_identity(),
1878 value
= users
.User(email
=email
,
1879 _auth_domain
=auth_domain
,
1880 _user_id
=obfuscated_gaiaid
,
1881 federated_identity
=federated_identity
,
1887 if pb
.has_meaning() and meaning
in _PROPERTY_CONVERSIONS
:
1888 conversion
= _PROPERTY_CONVERSIONS
[meaning
]
1889 value
= conversion(value
)
1890 except (KeyError, ValueError, IndexError, TypeError, AttributeError), msg
:
1891 raise datastore_errors
.BadValueError(
1892 'Error converting pb: %s\nException was: %s' % (pb
, msg
))
1897 def RestoreFromIndexValue(index_value
, data_type
):
1898 """Restores a index value to the correct datastore type.
1900 Projection queries return property values direclty from a datastore index.
1901 These values are the native datastore values, one of str, bool, long, float,
1902 GeoPt, Key or User. This function restores the original value when the the
1903 original type is known.
1905 This function returns the value type returned when decoding a normal entity,
1906 not necessarily of type data_type. For example, data_type=int returns a
1910 index_value: The value returned by FromPropertyPb for the projected
1912 data_type: The type of the value originally given to ToPropertyPb
1915 The restored property value.
1918 datastore_errors.BadValueError if the value cannot be restored.
1920 raw_type
= _PROPERTY_TYPE_TO_INDEX_VALUE_TYPE
.get(data_type
)
1921 if raw_type
is None:
1922 raise datastore_errors
.BadValueError(
1923 'Unsupported data type (%r)' % data_type
)
1925 if index_value
is None:
1930 if not isinstance(index_value
, raw_type
):
1931 raise datastore_errors
.BadValueError(
1932 'Unsupported converstion. Expected %r got %r' %
1933 (type(index_value
), raw_type
))
1935 meaning
= _PROPERTY_MEANINGS
.get(data_type
)
1938 if isinstance(index_value
, str) and meaning
not in _NON_UTF8_MEANINGS
:
1939 index_value
= unicode(index_value
, 'utf-8')
1942 conv
= _PROPERTY_CONVERSIONS
.get(meaning
)
1947 value
= conv(index_value
)
1948 except (KeyError, ValueError, IndexError, TypeError, AttributeError), msg
:
1949 raise datastore_errors
.BadValueError(
1950 'Error converting value: %r\nException was: %s' % (index_value
, msg
))
1954 def PropertyTypeName(value
):
1955 """Returns the name of the type of the given property value, as a string.
1957 Raises BadValueError if the value is not a valid property type.
1960 value: any valid property value
1965 if value
.__class
__ in _PROPERTY_MEANINGS
:
1966 meaning
= _PROPERTY_MEANINGS
[value
.__class
__]
1967 name
= entity_pb
.Property
._Meaning
_NAMES
[meaning
]
1968 return name
.lower().replace('_', ':')
1969 elif isinstance(value
, basestring
):
1971 elif isinstance(value
, users
.User
):
1973 elif isinstance(value
, long):
1978 return typename(value
).lower()
1981 _PROPERTY_TYPE_STRINGS
= {
1989 'entity:proto': EmbeddedEntity
,
1990 'bytestring': ByteString
,
1993 'atom:category': Category
,
1996 'gd:when': datetime
.datetime
,
1997 'georss:point': GeoPt
,
1999 'gd:phonenumber': PhoneNumber
,
2000 'gd:postaladdress': PostalAddress
,
2001 'gd:rating': Rating
,
2006 def FromPropertyTypeName(type_name
):
2007 """Returns the python type given a type name.
2010 type_name: A string representation of a datastore type name.
2015 return _PROPERTY_TYPE_STRINGS
[type_name
]
2018 def PropertyValueFromString(type_
,
2021 """Returns an instance of a property value given a type and string value.
2023 The reverse of this method is just str() and type() of the python value.
2025 Note that this does *not* support non-UTC offsets in ISO 8601-formatted
2026 datetime strings, e.g. the -08:00 suffix in '2002-12-25 00:00:00-08:00'.
2027 It only supports -00:00 and +00:00 suffixes, which are UTC.
2030 type_: A python class.
2031 value_string: A string representation of the value of the property.
2034 An instance of 'type'.
2037 ValueError if type_ is datetime and value_string has a timezone offset.
2039 if type_
== datetime
.datetime
:
2040 value_string
= value_string
.strip()
2042 if value_string
[-6] in ('+', '-'):
2043 if value_string
[-5:] == '00:00':
2044 value_string
= value_string
[:-6]
2047 raise ValueError('Non-UTC offsets in datetimes are not supported.')
2050 split
= value_string
.split('.')
2054 microseconds
= int(split
[1])
2058 time_struct
= time
.strptime(iso_date
, '%Y-%m-%d %H:%M:%S')[0:6]
2059 value
= datetime
.datetime(*(time_struct
+ (microseconds
,)))
2061 elif type_
== Rating
:
2063 return Rating(int(value_string
))
2065 return value_string
== 'True'
2066 elif type_
== users
.User
:
2067 return users
.User(value_string
, _auth_domain
)
2068 elif type_
== type(None):
2070 return type_(value_string
)
2073 def ReferenceToKeyValue(key
):
2074 """Converts a key into a comparable hashable "key" value.
2077 key: The entity_pb.Reference or entity_v4_pb.Key from which to construct
2081 A comparable and hashable representation of the given key that is
2082 compatible with one derived from a key property value.
2084 if isinstance(key
, entity_v4_pb
.Key
):
2086 key
= entity_pb
.Reference()
2087 datastore_pbs
.get_entity_converter().v4_to_v3_reference(v4_key
, key
)
2089 if isinstance(key
, entity_pb
.Reference
):
2090 element_list
= key
.path().element_list()
2091 elif isinstance(key
, entity_pb
.PropertyValue_ReferenceValue
):
2092 element_list
= key
.pathelement_list()
2094 raise datastore_errors
.BadArgumentError(
2095 "key arg expected to be entity_pb.Reference or entity_v4.Key (%r)"
2098 result
= [entity_pb
.PropertyValue
.kReferenceValueGroup
,
2099 key
.app(), key
.name_space()]
2100 for element
in element_list
:
2101 result
.append(element
.type())
2102 if element
.has_name():
2103 result
.append(element
.name())
2105 result
.append(element
.id())
2106 return tuple(result
)
2109 def PropertyValueToKeyValue(prop_value
):
2110 """Converts a entity_pb.PropertyValue into a comparable hashable "key" value.
2112 The values produces by this function mimic the native ording of the datastore
2113 and uniquely identify the given PropertyValue.
2116 prop_value: The entity_pb.PropertyValue from which to construct the
2120 A comparable and hashable representation of the given property value.
2122 if not isinstance(prop_value
, entity_pb
.PropertyValue
):
2123 raise datastore_errors
.BadArgumentError(
2124 'prop_value arg expected to be entity_pb.PropertyValue (%r)' %
2129 if prop_value
.has_stringvalue():
2130 return (entity_pb
.PropertyValue
.kstringValue
, prop_value
.stringvalue())
2131 if prop_value
.has_int64value():
2132 return (entity_pb
.PropertyValue
.kint64Value
, prop_value
.int64value())
2133 if prop_value
.has_booleanvalue():
2134 return (entity_pb
.PropertyValue
.kbooleanValue
, prop_value
.booleanvalue())
2135 if prop_value
.has_doublevalue():
2137 encoder
= sortable_pb_encoder
.Encoder()
2138 encoder
.putDouble(prop_value
.doublevalue())
2139 return (entity_pb
.PropertyValue
.kdoubleValue
, tuple(encoder
.buf
))
2140 if prop_value
.has_pointvalue():
2141 return (entity_pb
.PropertyValue
.kPointValueGroup
,
2142 prop_value
.pointvalue().x(), prop_value
.pointvalue().y())
2143 if prop_value
.has_referencevalue():
2144 return ReferenceToKeyValue(prop_value
.referencevalue())
2145 if prop_value
.has_uservalue():
2147 uservalue
= prop_value
.uservalue()
2148 if uservalue
.has_email():
2149 result
.append((entity_pb
.PropertyValue
.kUserValueemail
,
2151 if uservalue
.has_auth_domain():
2152 result
.append((entity_pb
.PropertyValue
.kUserValueauth_domain
,
2153 uservalue
.auth_domain()))
2154 if uservalue
.has_nickname():
2155 result
.append((entity_pb
.PropertyValue
.kUserValuenickname
,
2156 uservalue
.nickname()))
2157 if uservalue
.has_gaiaid():
2158 result
.append((entity_pb
.PropertyValue
.kUserValuegaiaid
,
2159 uservalue
.gaiaid()))
2160 if uservalue
.has_obfuscated_gaiaid():
2161 result
.append((entity_pb
.PropertyValue
.kUserValueobfuscated_gaiaid
,
2162 uservalue
.obfuscated_gaiaid()))
2163 if uservalue
.has_federated_identity():
2164 result
.append((entity_pb
.PropertyValue
.kUserValuefederated_identity
,
2165 uservalue
.federated_identity()))
2166 if uservalue
.has_federated_provider():
2167 result
.append((entity_pb
.PropertyValue
.kUserValuefederated_provider
,
2168 uservalue
.federated_provider()))
2170 return (entity_pb
.PropertyValue
.kUserValueGroup
, tuple(result
))
2174 def GetPropertyValueTag(value_pb
):
2175 """Returns the tag constant associated with the given entity_pb.PropertyValue.
2177 if value_pb
.has_booleanvalue():
2178 return entity_pb
.PropertyValue
.kbooleanValue
2179 elif value_pb
.has_doublevalue():
2180 return entity_pb
.PropertyValue
.kdoubleValue
2181 elif value_pb
.has_int64value():
2182 return entity_pb
.PropertyValue
.kint64Value
2183 elif value_pb
.has_pointvalue():
2184 return entity_pb
.PropertyValue
.kPointValueGroup
2185 elif value_pb
.has_referencevalue():
2186 return entity_pb
.PropertyValue
.kReferenceValueGroup
2187 elif value_pb
.has_stringvalue():
2188 return entity_pb
.PropertyValue
.kstringValue
2189 elif value_pb
.has_uservalue():
2190 return entity_pb
.PropertyValue
.kUserValueGroup