Update Google App Engine to 1.2.2 in thirdparty folder.
[Melange.git] / thirdparty / google_appengine / google / appengine / api / datastore.py
blob8c59f31c1aa0b3665dc9bb26743a39eba395e7bf
1 #!/usr/bin/env python
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.
18 """The Python datastore API used by app developers.
20 Defines Entity, Query, and Iterator classes, as well as methods for all of the
21 datastore's calls. Also defines conversions between the Python classes and
22 their PB counterparts.
24 The datastore errors are defined in the datastore_errors module. That module is
25 only required to avoid circular imports. datastore imports datastore_types,
26 which needs BadValueError, so it can't be defined in datastore.
27 """
34 import heapq
35 import itertools
36 import logging
37 import re
38 import string
39 import sys
40 import traceback
41 from xml.sax import saxutils
43 from google.appengine.api import api_base_pb
44 from google.appengine.api import apiproxy_stub_map
45 from google.appengine.api import datastore_errors
46 from google.appengine.api import datastore_types
47 from google.appengine.datastore import datastore_index
48 from google.appengine.datastore import datastore_pb
49 from google.appengine.runtime import apiproxy_errors
50 from google.appengine.datastore import entity_pb
52 MAX_ALLOWABLE_QUERIES = 30
54 DEFAULT_TRANSACTION_RETRIES = 3
56 _MAX_INDEXED_PROPERTIES = 5000
58 Key = datastore_types.Key
59 typename = datastore_types.typename
61 _txes = {}
64 def NormalizeAndTypeCheck(arg, types):
65 """Normalizes and type checks the given argument.
67 Args:
68 arg: an instance, tuple, list, iterator, or generator of the given type(s)
69 types: allowed type or tuple of types
71 Returns:
72 A (list, bool) tuple. The list is a normalized, shallow copy of the
73 argument. The boolean is True if the argument was a sequence, False
74 if it was a single object.
76 Raises:
77 AssertionError: types includes list or tuple.
78 BadArgumentError: arg is not an instance or sequence of one of the given
79 types.
80 """
81 if not isinstance(types, (list, tuple)):
82 types = (types,)
84 assert list not in types and tuple not in types
86 if isinstance(arg, types):
87 return ([arg], False)
88 else:
89 try:
90 for val in arg:
91 if not isinstance(val, types):
92 raise datastore_errors.BadArgumentError(
93 'Expected one of %s; received %s (a %s).' %
94 (types, val, typename(val)))
95 except TypeError:
96 raise datastore_errors.BadArgumentError(
97 'Expected an instance or sequence of %s; received %s (a %s).' %
98 (types, arg, typename(arg)))
100 return (list(arg), True)
103 def NormalizeAndTypeCheckKeys(keys):
104 """Normalizes and type checks that the given argument is a valid key or keys.
106 A wrapper around NormalizeAndTypeCheck() that accepts strings, Keys, and
107 Entities, and normalizes to Keys.
109 Args:
110 keys: a Key or sequence of Keys
112 Returns:
113 A (list of Keys, bool) tuple. See NormalizeAndTypeCheck.
115 Raises:
116 BadArgumentError: arg is not an instance or sequence of one of the given
117 types.
119 keys, multiple = NormalizeAndTypeCheck(keys, (basestring, Entity, Key))
121 keys = [_GetCompleteKeyOrError(key) for key in keys]
123 return (keys, multiple)
126 def Put(entities):
127 """Store one or more entities in the datastore.
129 The entities may be new or previously existing. For new entities, Put() will
130 fill in the app id and key assigned by the datastore.
132 If the argument is a single Entity, a single Key will be returned. If the
133 argument is a list of Entity, a list of Keys will be returned.
135 Args:
136 entities: Entity or list of Entities
138 Returns:
139 Key or list of Keys
141 Raises:
142 TransactionFailedError, if the Put could not be committed.
144 entities, multiple = NormalizeAndTypeCheck(entities, Entity)
146 if multiple and not entities:
147 return []
149 for entity in entities:
150 if not entity.kind() or not entity.app():
151 raise datastore_errors.BadRequestError(
152 'App and kind must not be empty, in entity: %s' % entity)
154 req = datastore_pb.PutRequest()
155 req.entity_list().extend([e._ToPb() for e in entities])
157 keys = [e.key() for e in entities]
158 tx = _MaybeSetupTransaction(req, keys)
159 if tx:
160 tx.RecordModifiedKeys([k for k in keys if k.has_id_or_name()])
162 resp = datastore_pb.PutResponse()
163 try:
164 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Put', req, resp)
165 except apiproxy_errors.ApplicationError, err:
166 raise _ToDatastoreError(err)
168 keys = resp.key_list()
169 num_keys = len(keys)
170 num_entities = len(entities)
171 if num_keys != num_entities:
172 raise datastore_errors.InternalError(
173 'Put accepted %d entities but returned %d keys.' %
174 (num_entities, num_keys))
176 for entity, key in zip(entities, keys):
177 entity._Entity__key._Key__reference.CopyFrom(key)
179 if tx:
180 tx.RecordModifiedKeys([e.key() for e in entities], error_on_repeat=False)
182 if multiple:
183 return [Key._FromPb(k) for k in keys]
184 else:
185 return Key._FromPb(resp.key(0))
188 def Get(keys):
189 """Retrieves one or more entities from the datastore.
191 Retrieves the entity or entities with the given key(s) from the datastore
192 and returns them as fully populated Entity objects, as defined below. If
193 there is an error, raises a subclass of datastore_errors.Error.
195 If keys is a single key or string, an Entity will be returned, or
196 EntityNotFoundError will be raised if no existing entity matches the key.
198 However, if keys is a list or tuple, a list of entities will be returned
199 that corresponds to the sequence of keys. It will include entities for keys
200 that were found and None placeholders for keys that were not found.
202 Args:
203 # the primary key(s) of the entity(ies) to retrieve
204 keys: Key or string or list of Keys or strings
206 Returns:
207 Entity or list of Entity objects
209 keys, multiple = NormalizeAndTypeCheckKeys(keys)
211 if multiple and not keys:
212 return []
213 req = datastore_pb.GetRequest()
214 req.key_list().extend([key._Key__reference for key in keys])
215 _MaybeSetupTransaction(req, keys)
217 resp = datastore_pb.GetResponse()
218 try:
219 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', req, resp)
220 except apiproxy_errors.ApplicationError, err:
221 raise _ToDatastoreError(err)
223 entities = []
224 for group in resp.entity_list():
225 if group.has_entity():
226 entities.append(Entity._FromPb(group.entity()))
227 else:
228 entities.append(None)
230 if multiple:
231 return entities
232 else:
233 if entities[0] is None:
234 raise datastore_errors.EntityNotFoundError()
235 return entities[0]
238 def Delete(keys):
239 """Deletes one or more entities from the datastore. Use with care!
241 Deletes the given entity(ies) from the datastore. You can only delete
242 entities from your app. If there is an error, raises a subclass of
243 datastore_errors.Error.
245 Args:
246 # the primary key(s) of the entity(ies) to delete
247 keys: Key or string or list of Keys or strings
249 Raises:
250 TransactionFailedError, if the Delete could not be committed.
252 keys, multiple = NormalizeAndTypeCheckKeys(keys)
254 if multiple and not keys:
255 return
257 req = datastore_pb.DeleteRequest()
258 req.key_list().extend([key._Key__reference for key in keys])
260 tx = _MaybeSetupTransaction(req, keys)
261 if tx:
262 tx.RecordModifiedKeys(keys)
264 resp = datastore_pb.DeleteResponse()
265 try:
266 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Delete', req, resp)
267 except apiproxy_errors.ApplicationError, err:
268 raise _ToDatastoreError(err)
271 class Entity(dict):
272 """A datastore entity.
274 Includes read-only accessors for app id, kind, and primary key. Also
275 provides dictionary-style access to properties.
277 def __init__(self, kind, parent=None, _app=None, name=None,
278 unindexed_properties=[]):
279 """Constructor. Takes the kind and transaction root, which cannot be
280 changed after the entity is constructed, and an optional parent. Raises
281 BadArgumentError or BadKeyError if kind is invalid or parent is not an
282 existing Entity or Key in the datastore.
284 Args:
285 # this entity's kind
286 kind: string
287 # if provided, this entity's parent. Its key must be complete.
288 parent: Entity or Key
289 # if provided, this entity's name.
290 name: string
291 # if provided, a sequence of property names that should not be indexed
292 # by the built-in single property indices.
293 unindexed_properties: list or tuple of strings
295 ref = entity_pb.Reference()
296 _app = datastore_types.ResolveAppId(_app)
297 ref.set_app(_app)
299 datastore_types.ValidateString(kind, 'kind',
300 datastore_errors.BadArgumentError)
302 if parent is not None:
303 parent = _GetCompleteKeyOrError(parent)
304 if _app != parent.app():
305 raise datastore_errors.BadArgumentError(
306 "_app %s doesn't match parent's app %s" % (_app, parent.app()))
307 ref.CopyFrom(parent._Key__reference)
309 last_path = ref.mutable_path().add_element()
310 last_path.set_type(kind.encode('utf-8'))
312 if name is not None:
313 datastore_types.ValidateString(name, 'name')
314 if name[0] in string.digits:
315 raise datastore_errors.BadValueError('name cannot begin with a digit')
316 last_path.set_name(name.encode('utf-8'))
318 unindexed_properties, multiple = NormalizeAndTypeCheck(unindexed_properties, basestring)
319 if not multiple:
320 raise datastore_errors.BadArgumentError(
321 'unindexed_properties must be a sequence; received %s (a %s).' %
322 (unindexed_properties, typename(unindexed_properties)))
323 for prop in unindexed_properties:
324 datastore_types.ValidateProperty(prop, None)
325 self.__unindexed_properties = frozenset(unindexed_properties)
327 self.__key = Key._FromPb(ref)
329 def app(self):
330 """Returns the name of the application that created this entity, a
331 string.
333 return self.__key.app()
335 def kind(self):
336 """Returns this entity's kind, a string.
338 return self.__key.kind()
340 def key(self):
341 """Returns this entity's primary key, a Key instance.
343 return self.__key
345 def parent(self):
346 """Returns this entity's parent, as a Key. If this entity has no parent,
347 returns None.
349 return self.key().parent()
351 def entity_group(self):
352 """Returns this entity's entity group as a Key.
354 Note that the returned Key will be incomplete if this is a a root entity
355 and its key is incomplete.
357 return self.key().entity_group()
359 def unindexed_properties(self):
360 """Returns this entity's unindexed properties, as a frozenset of strings."""
361 return self.__unindexed_properties
363 def __setitem__(self, name, value):
364 """Implements the [] operator. Used to set property value(s).
366 If the property name is the empty string or not a string, raises
367 BadPropertyError. If the value is not a supported type, raises
368 BadValueError.
370 datastore_types.ValidateProperty(name, value)
371 dict.__setitem__(self, name, value)
373 def setdefault(self, name, value):
374 """If the property exists, returns its value. Otherwise sets it to value.
376 If the property name is the empty string or not a string, raises
377 BadPropertyError. If the value is not a supported type, raises
378 BadValueError.
380 datastore_types.ValidateProperty(name, value)
381 return dict.setdefault(self, name, value)
383 def update(self, other):
384 """Updates this entity's properties from the values in other.
386 If any property name is the empty string or not a string, raises
387 BadPropertyError. If any value is not a supported type, raises
388 BadValueError.
390 for name, value in other.items():
391 self.__setitem__(name, value)
393 def copy(self):
394 """The copy method is not supported.
396 raise NotImplementedError('Entity does not support the copy() method.')
398 def ToXml(self):
399 """Returns an XML representation of this entity. Atom and gd:namespace
400 properties are converted to XML according to their respective schemas. For
401 more information, see:
403 http://www.atomenabled.org/developers/syndication/
404 http://code.google.com/apis/gdata/common-elements.html
406 This is *not* optimized. It shouldn't be used anywhere near code that's
407 performance-critical.
409 xml = u'<entity kind=%s' % saxutils.quoteattr(self.kind())
410 if self.__key.has_id_or_name():
411 xml += ' key=%s' % saxutils.quoteattr(str(self.__key))
412 xml += '>'
413 if self.__key.has_id_or_name():
414 xml += '\n <key>%s</key>' % self.__key.ToTagUri()
417 properties = self.keys()
418 if properties:
419 properties.sort()
420 xml += '\n ' + '\n '.join(self._PropertiesToXml(properties))
422 xml += '\n</entity>\n'
423 return xml
425 def _PropertiesToXml(self, properties):
426 """ Returns a list of the XML representations of each of the given
427 properties. Ignores properties that don't exist in this entity.
429 Arg:
430 properties: string or list of strings
432 Returns:
433 list of strings
435 xml_properties = []
437 for propname in properties:
438 if not self.has_key(propname):
439 continue
441 propname_xml = saxutils.quoteattr(propname)
443 values = self[propname]
444 if not isinstance(values, list):
445 values = [values]
447 proptype = datastore_types.PropertyTypeName(values[0])
448 proptype_xml = saxutils.quoteattr(proptype)
450 escaped_values = self._XmlEscapeValues(propname)
451 open_tag = u'<property name=%s type=%s>' % (propname_xml, proptype_xml)
452 close_tag = u'</property>'
453 xml_properties += [open_tag + val + close_tag for val in escaped_values]
455 return xml_properties
457 def _XmlEscapeValues(self, property):
458 """ Returns a list of the XML-escaped string values for the given property.
459 Raises an AssertionError if the property doesn't exist.
461 Arg:
462 property: string
464 Returns:
465 list of strings
467 assert self.has_key(property)
468 xml = []
470 values = self[property]
471 if not isinstance(values, list):
472 values = [values]
474 for val in values:
475 if hasattr(val, 'ToXml'):
476 xml.append(val.ToXml())
477 else:
478 if val is None:
479 xml.append('')
480 else:
481 xml.append(saxutils.escape(unicode(val)))
483 return xml
485 def _ToPb(self):
486 """Converts this Entity to its protocol buffer representation. Not
487 intended to be used by application developers.
489 Returns:
490 entity_pb.Entity
493 pb = entity_pb.EntityProto()
494 pb.mutable_key().CopyFrom(self.key()._ToPb())
496 group = pb.mutable_entity_group()
497 if self.__key.has_id_or_name():
498 root = pb.key().path().element(0)
499 group.add_element().CopyFrom(root)
501 properties = self.items()
502 properties.sort()
503 for (name, values) in properties:
504 properties = datastore_types.ToPropertyPb(name, values)
505 if not isinstance(properties, list):
506 properties = [properties]
508 sample = values
509 if isinstance(sample, list):
510 sample = values[0]
512 if (isinstance(sample, datastore_types._RAW_PROPERTY_TYPES) or
513 name in self.__unindexed_properties):
514 pb.raw_property_list().extend(properties)
515 else:
516 pb.property_list().extend(properties)
518 if pb.property_size() > _MAX_INDEXED_PROPERTIES:
519 raise datastore_errors.BadRequestError(
520 'Too many indexed properties for entity %r.' % self.key())
522 return pb
524 @staticmethod
525 def _FromPb(pb):
526 """Static factory method. Returns the Entity representation of the
527 given protocol buffer (datastore_pb.Entity). Not intended to be used by
528 application developers.
530 The Entity PB's key must be complete. If it isn't, an AssertionError is
531 raised.
533 Args:
534 # a protocol buffer Entity
535 pb: datastore_pb.Entity
537 Returns:
538 # the Entity representation of the argument
539 Entity
541 assert pb.key().path().element_size() > 0
543 last_path = pb.key().path().element_list()[-1]
544 assert last_path.has_id() ^ last_path.has_name()
545 if last_path.has_id():
546 assert last_path.id() != 0
547 else:
548 assert last_path.has_name()
549 assert last_path.name()
551 unindexed_properties = [p.name() for p in pb.raw_property_list()]
553 e = Entity(unicode(last_path.type().decode('utf-8')),
554 unindexed_properties=unindexed_properties)
555 ref = e.__key._Key__reference
556 ref.CopyFrom(pb.key())
558 temporary_values = {}
560 for prop_list in (pb.property_list(), pb.raw_property_list()):
561 for prop in prop_list:
562 try:
563 value = datastore_types.FromPropertyPb(prop)
564 except (AssertionError, AttributeError, TypeError, ValueError), e:
565 raise datastore_errors.Error(
566 'Property %s is corrupt in the datastore. %s: %s' %
567 (e.__class__, prop.name(), e))
569 multiple = prop.multiple()
570 if multiple:
571 value = [value]
573 name = prop.name()
574 cur_value = temporary_values.get(name)
575 if cur_value is None:
576 temporary_values[name] = value
577 elif not multiple:
578 raise datastore_errors.Error(
579 'Property %s is corrupt in the datastore; it has multiple '
580 'values, but is not marked as multiply valued.' % name)
581 else:
582 cur_value.extend(value)
584 for name, value in temporary_values.iteritems():
585 decoded_name = unicode(name.decode('utf-8'))
587 datastore_types.ValidateReadProperty(decoded_name, value)
589 dict.__setitem__(e, decoded_name, value)
591 return e
594 class Query(dict):
595 """A datastore query.
597 (Instead of this, consider using appengine.ext.gql.Query! It provides a
598 query language interface on top of the same functionality.)
600 Queries are used to retrieve entities that match certain criteria, including
601 app id, kind, and property filters. Results may also be sorted by properties.
603 App id and kind are required. Only entities from the given app, of the given
604 type, are returned. If an ancestor is set, with Ancestor(), only entities
605 with that ancestor are returned.
607 Property filters are used to provide criteria based on individual property
608 values. A filter compares a specific property in each entity to a given
609 value or list of possible values.
611 An entity is returned if its property values match *all* of the query's
612 filters. In other words, filters are combined with AND, not OR. If an
613 entity does not have a value for a property used in a filter, it is not
614 returned.
616 Property filters map filter strings of the form '<property name> <operator>'
617 to filter values. Use dictionary accessors to set property filters, like so:
619 > query = Query('Person')
620 > query['name ='] = 'Ryan'
621 > query['age >='] = 21
623 This query returns all Person entities where the name property is 'Ryan',
624 'Ken', or 'Bret', and the age property is at least 21.
626 Another way to build this query is:
628 > query = Query('Person')
629 > query.update({'name =': 'Ryan', 'age >=': 21})
631 The supported operators are =, >, <, >=, and <=. Only one inequality
632 filter may be used per query. Any number of equals filters may be used in
633 a single Query.
635 A filter value may be a list or tuple of values. This is interpreted as
636 multiple filters with the same filter string and different values, all ANDed
637 together. For example, this query returns everyone with the tags "google"
638 and "app engine":
640 > Query('Person', {'tag =': ('google', 'app engine')})
642 Result entities can be returned in different orders. Use the Order()
643 method to specify properties that results will be sorted by, and in which
644 direction.
646 Note that filters and orderings may be provided at any time before the query
647 is run. When the query is fully specified, Run() runs the query and returns
648 an iterator. The query results can be accessed through the iterator.
650 A query object may be reused after it's been run. Its filters and
651 orderings can be changed to create a modified query.
653 If you know how many result entities you need, use Get() to fetch them:
655 > query = Query('Person', {'age >': 21})
656 > for person in query.Get(4):
657 > print 'I have four pints left. Have one on me, %s!' % person['name']
659 If you don't know how many results you need, or if you need them all, you
660 can get an iterator over the results by calling Run():
662 > for person in Query('Person', {'age >': 21}).Run():
663 > print 'Have a pint on me, %s!' % person['name']
665 Get() is more efficient than Run(), so use Get() whenever possible.
667 Finally, the Count() method returns the number of result entities matched by
668 the query. The returned count is cached; successive Count() calls will not
669 re-scan the datastore unless the query is changed.
671 ASCENDING = datastore_pb.Query_Order.ASCENDING
672 DESCENDING = datastore_pb.Query_Order.DESCENDING
674 ORDER_FIRST = datastore_pb.Query.ORDER_FIRST
675 ANCESTOR_FIRST = datastore_pb.Query.ANCESTOR_FIRST
676 FILTER_FIRST = datastore_pb.Query.FILTER_FIRST
678 OPERATORS = {'<': datastore_pb.Query_Filter.LESS_THAN,
679 '<=': datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL,
680 '>': datastore_pb.Query_Filter.GREATER_THAN,
681 '>=': datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL,
682 '=': datastore_pb.Query_Filter.EQUAL,
683 '==': datastore_pb.Query_Filter.EQUAL,
685 INEQUALITY_OPERATORS = frozenset(['<', '<=', '>', '>='])
686 FILTER_REGEX = re.compile(
687 '^\s*([^\s]+)(\s+(%s)\s*)?$' % '|'.join(OPERATORS.keys()),
688 re.IGNORECASE | re.UNICODE)
690 __kind = None
691 __app = None
692 __orderings = None
693 __cached_count = None
694 __hint = None
695 __ancestor = None
697 __filter_order = None
698 __filter_counter = 0
700 __inequality_prop = None
701 __inequality_count = 0
703 def __init__(self, kind, filters={}, _app=None, keys_only=False):
704 """Constructor.
706 Raises BadArgumentError if kind is not a string. Raises BadValueError or
707 BadFilterError if filters is not a dictionary of valid filters.
709 Args:
710 # kind is required. filters is optional; if provided, it's used
711 # as an initial set of property filters. keys_only defaults to False.
712 kind: string
713 filters: dict
714 keys_only: boolean
716 datastore_types.ValidateString(kind, 'kind',
717 datastore_errors.BadArgumentError)
719 self.__kind = kind
720 self.__orderings = []
721 self.__filter_order = {}
722 self.update(filters)
724 self.__app = datastore_types.ResolveAppId(_app)
725 self.__keys_only = keys_only
727 def Order(self, *orderings):
728 """Specify how the query results should be sorted.
730 Result entities will be sorted by the first property argument, then by the
731 second, and so on. For example, this:
733 > query = Query('Person')
734 > query.Order('bday', ('age', Query.DESCENDING))
736 sorts everyone in order of their birthday, starting with January 1.
737 People with the same birthday are sorted by age, oldest to youngest.
739 The direction for each sort property may be provided; if omitted, it
740 defaults to ascending.
742 Order() may be called multiple times. Each call resets the sort order
743 from scratch.
745 If an inequality filter exists in this Query it must be the first property
746 passed to Order. Any number of sort orders may be used after the
747 inequality filter property. Without inequality filters, any number of
748 filters with different orders may be specified.
750 Entities with multiple values for an order property are sorted by their
751 lowest value.
753 Note that a sort order implies an existence filter! In other words,
754 Entities without the sort order property are filtered out, and *not*
755 included in the query results.
757 If the sort order property has different types in different entities - ie,
758 if bob['id'] is an int and fred['id'] is a string - the entities will be
759 grouped first by the property type, then sorted within type. No attempt is
760 made to compare property values across types.
762 Raises BadArgumentError if any argument is of the wrong format.
764 Args:
765 # the properties to sort by, in sort order. each argument may be either a
766 # string or (string, direction) 2-tuple.
768 Returns:
769 # this query
770 Query
772 orderings = list(orderings)
774 for (order, i) in zip(orderings, range(len(orderings))):
775 if not (isinstance(order, basestring) or
776 (isinstance(order, tuple) and len(order) in [2, 3])):
777 raise datastore_errors.BadArgumentError(
778 'Order() expects strings or 2- or 3-tuples; received %s (a %s). ' %
779 (order, typename(order)))
781 if isinstance(order, basestring):
782 order = (order,)
784 datastore_types.ValidateString(order[0], 'sort order property',
785 datastore_errors.BadArgumentError)
786 property = order[0]
788 direction = order[-1]
789 if direction not in (Query.ASCENDING, Query.DESCENDING):
790 if len(order) == 3:
791 raise datastore_errors.BadArgumentError(
792 'Order() expects Query.ASCENDING or DESCENDING; received %s' %
793 str(direction))
794 direction = Query.ASCENDING
796 orderings[i] = (property, direction)
798 if (orderings and self.__inequality_prop and
799 orderings[0][0] != self.__inequality_prop):
800 raise datastore_errors.BadArgumentError(
801 'First ordering property must be the same as inequality filter '
802 'property, if specified for this query; received %s, expected %s' %
803 (orderings[0][0], self.__inequality_prop))
805 self.__orderings = orderings
806 return self
808 def Hint(self, hint):
809 """Sets a hint for how this query should run.
811 The query hint gives us information about how best to execute your query.
812 Currently, we can only do one index scan, so the query hint should be used
813 to indicates which index we should scan against.
815 Use FILTER_FIRST if your first filter will only match a few results. In
816 this case, it will be most efficient to scan against the index for this
817 property, load the results into memory, and apply the remaining filters
818 and sort orders there.
820 Similarly, use ANCESTOR_FIRST if the query's ancestor only has a few
821 descendants. In this case, it will be most efficient to scan all entities
822 below the ancestor and load them into memory first.
824 Use ORDER_FIRST if the query has a sort order and the result set is large
825 or you only plan to fetch the first few results. In that case, we
826 shouldn't try to load all of the results into memory; instead, we should
827 scan the index for this property, which is in sorted order.
829 Note that hints are currently ignored in the v3 datastore!
831 Arg:
832 one of datastore.Query.[ORDER_FIRST, ANCESTOR_FIRST, FILTER_FIRST]
834 Returns:
835 # this query
836 Query
838 if hint not in [self.ORDER_FIRST, self.ANCESTOR_FIRST, self.FILTER_FIRST]:
839 raise datastore_errors.BadArgumentError(
840 'Query hint must be ORDER_FIRST, ANCESTOR_FIRST, or FILTER_FIRST.')
842 self.__hint = hint
843 return self
845 def Ancestor(self, ancestor):
846 """Sets an ancestor for this query.
848 This restricts the query to only return result entities that are descended
849 from a given entity. In other words, all of the results will have the
850 ancestor as their parent, or parent's parent, or etc.
852 Raises BadArgumentError or BadKeyError if parent is not an existing Entity
853 or Key in the datastore.
855 Args:
856 # the key must be complete
857 ancestor: Entity or Key
859 Returns:
860 # this query
861 Query
863 key = _GetCompleteKeyOrError(ancestor)
864 self.__ancestor = datastore_pb.Reference()
865 self.__ancestor.CopyFrom(key._Key__reference)
866 return self
868 def IsKeysOnly(self):
869 """Returns True if this query is keys only, false otherwise."""
870 return self.__keys_only
872 def Run(self):
873 """Runs this query.
875 If a filter string is invalid, raises BadFilterError. If a filter value is
876 invalid, raises BadValueError. If an IN filter is provided, and a sort
877 order on another property is provided, raises BadQueryError.
879 If you know in advance how many results you want, use Get() instead. It's
880 more efficient.
882 Returns:
883 # an iterator that provides access to the query results
884 Iterator
886 return self._Run()
888 def _Run(self, limit=None, offset=None):
889 """Runs this query, with an optional result limit and an optional offset.
891 Identical to Run, with the extra optional limit and offset parameters.
892 limit and offset must both be integers >= 0.
894 This is not intended to be used by application developers. Use Get()
895 instead!
897 if _CurrentTransactionKey():
898 raise datastore_errors.BadRequestError(
899 "Can't query inside a transaction.")
901 pb = self._ToPb(limit, offset)
902 result = datastore_pb.QueryResult()
904 try:
905 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'RunQuery', pb, result)
906 except apiproxy_errors.ApplicationError, err:
907 try:
908 _ToDatastoreError(err)
909 except datastore_errors.NeedIndexError, exc:
910 yaml = datastore_index.IndexYamlForQuery(
911 *datastore_index.CompositeIndexForQuery(pb)[1:-1])
912 raise datastore_errors.NeedIndexError(
913 str(exc) + '\nThis query needs this index:\n' + yaml)
915 return Iterator._FromPb(result)
917 def Get(self, limit, offset=0):
918 """Fetches and returns a maximum number of results from the query.
920 This method fetches and returns a list of resulting entities that matched
921 the query. If the query specified a sort order, entities are returned in
922 that order. Otherwise, the order is undefined.
924 The limit argument specifies the maximum number of entities to return. If
925 it's greater than the number of remaining entities, all of the remaining
926 entities are returned. In that case, the length of the returned list will
927 be smaller than limit.
929 The offset argument specifies the number of entities that matched the
930 query criteria to skip before starting to return results. The limit is
931 applied after the offset, so if you provide a limit of 10 and an offset of 5
932 and your query matches 20 records, the records whose index is 0 through 4
933 will be skipped and the records whose index is 5 through 14 will be
934 returned.
936 The results are always returned as a list. If there are no results left,
937 an empty list is returned.
939 If you know in advance how many results you want, this method is more
940 efficient than Run(), since it fetches all of the results at once. (The
941 datastore backend sets the the limit on the underlying
942 scan, which makes the scan significantly faster.)
944 Args:
945 # the maximum number of entities to return
946 int or long
947 # the number of entities to skip
948 int or long
950 Returns:
951 # a list of entities
952 [Entity, ...]
954 if not isinstance(limit, (int, long)) or limit <= 0:
955 raise datastore_errors.BadArgumentError(
956 'Argument to Get named \'limit\' must be an int greater than 0; '
957 'received %s (a %s)' % (limit, typename(limit)))
959 if not isinstance(offset, (int, long)) or offset < 0:
960 raise datastore_errors.BadArgumentError(
961 'Argument to Get named \'offset\' must be an int greater than or '
962 'equal to 0; received %s (a %s)' % (offset, typename(offset)))
964 return self._Run(limit, offset)._Next(limit)
966 def Count(self, limit=None):
967 """Returns the number of entities that this query matches. The returned
968 count is cached; successive Count() calls will not re-scan the datastore
969 unless the query is changed.
971 Args:
972 limit, a number. If there are more results than this, stop short and
973 just return this number. Providing this argument makes the count
974 operation more efficient.
975 Returns:
976 The number of results.
978 if self.__cached_count:
979 return self.__cached_count
981 resp = api_base_pb.Integer64Proto()
982 try:
983 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Count',
984 self._ToPb(limit=limit), resp)
985 except apiproxy_errors.ApplicationError, err:
986 raise _ToDatastoreError(err)
987 else:
988 self.__cached_count = resp.value()
990 return self.__cached_count
992 def __iter__(self):
993 raise NotImplementedError(
994 'Query objects should not be used as iterators. Call Run() first.')
996 def __setitem__(self, filter, value):
997 """Implements the [] operator. Used to set filters.
999 If the filter string is empty or not a string, raises BadFilterError. If
1000 the value is not a supported type, raises BadValueError.
1002 if isinstance(value, tuple):
1003 value = list(value)
1005 datastore_types.ValidateProperty(' ', value, read_only=True)
1006 match = self._CheckFilter(filter, value)
1007 property = match.group(1)
1008 operator = match.group(3)
1010 dict.__setitem__(self, filter, value)
1012 if operator in self.INEQUALITY_OPERATORS:
1013 if self.__inequality_prop is None:
1014 self.__inequality_prop = property
1015 else:
1016 assert self.__inequality_prop == property
1017 self.__inequality_count += 1
1019 if filter not in self.__filter_order:
1020 self.__filter_order[filter] = self.__filter_counter
1021 self.__filter_counter += 1
1023 self.__cached_count = None
1025 def setdefault(self, filter, value):
1026 """If the filter exists, returns its value. Otherwise sets it to value.
1028 If the property name is the empty string or not a string, raises
1029 BadPropertyError. If the value is not a supported type, raises
1030 BadValueError.
1032 datastore_types.ValidateProperty(' ', value)
1033 self._CheckFilter(filter, value)
1034 self.__cached_count = None
1035 return dict.setdefault(self, filter, value)
1037 def __delitem__(self, filter):
1038 """Implements the del [] operator. Used to remove filters.
1040 dict.__delitem__(self, filter)
1041 del self.__filter_order[filter]
1042 self.__cached_count = None
1044 match = Query.FILTER_REGEX.match(filter)
1045 property = match.group(1)
1046 operator = match.group(3)
1048 if operator in self.INEQUALITY_OPERATORS:
1049 assert self.__inequality_count >= 1
1050 assert property == self.__inequality_prop
1051 self.__inequality_count -= 1
1052 if self.__inequality_count == 0:
1053 self.__inequality_prop = None
1055 def update(self, other):
1056 """Updates this query's filters from the ones in other.
1058 If any filter string is invalid, raises BadFilterError. If any value is
1059 not a supported type, raises BadValueError.
1061 for filter, value in other.items():
1062 self.__setitem__(filter, value)
1064 def copy(self):
1065 """The copy method is not supported.
1067 raise NotImplementedError('Query does not support the copy() method.')
1069 def _CheckFilter(self, filter, values):
1070 """Type check a filter string and list of values.
1072 Raises BadFilterError if the filter string is empty, not a string, or
1073 invalid. Raises BadValueError if the value type is not supported.
1075 Args:
1076 filter: String containing the filter text.
1077 values: List of associated filter values.
1079 Returns:
1080 re.MatchObject (never None) that matches the 'filter'. Group 1 is the
1081 property name, group 3 is the operator. (Group 2 is unused.)
1083 try:
1084 match = Query.FILTER_REGEX.match(filter)
1085 if not match:
1086 raise datastore_errors.BadFilterError(
1087 'Could not parse filter string: %s' % str(filter))
1088 except TypeError:
1089 raise datastore_errors.BadFilterError(
1090 'Could not parse filter string: %s' % str(filter))
1092 property = match.group(1)
1093 operator = match.group(3)
1094 if operator is None:
1095 operator = '='
1097 if isinstance(values, tuple):
1098 values = list(values)
1099 elif not isinstance(values, list):
1100 values = [values]
1101 if isinstance(values[0], datastore_types._RAW_PROPERTY_TYPES):
1102 raise datastore_errors.BadValueError(
1103 'Filtering on %s properties is not supported.' % typename(values[0]))
1105 if operator in self.INEQUALITY_OPERATORS:
1106 if self.__inequality_prop and property != self.__inequality_prop:
1107 raise datastore_errors.BadFilterError(
1108 'Only one property per query may have inequality filters (%s).' %
1109 ', '.join(self.INEQUALITY_OPERATORS))
1110 elif len(self.__orderings) >= 1 and self.__orderings[0][0] != property:
1111 raise datastore_errors.BadFilterError(
1112 'Inequality operators (%s) must be on the same property as the '
1113 'first sort order, if any sort orders are supplied' %
1114 ', '.join(self.INEQUALITY_OPERATORS))
1116 if property in datastore_types._SPECIAL_PROPERTIES:
1117 if property == datastore_types._KEY_SPECIAL_PROPERTY:
1118 for value in values:
1119 if not isinstance(value, Key):
1120 raise datastore_errors.BadFilterError(
1121 '%s filter value must be a Key; received %s (a %s)' %
1122 (datastore_types._KEY_SPECIAL_PROPERTY, value, typename(value)))
1124 return match
1126 def _ToPb(self, limit=None, offset=None):
1127 """Converts this Query to its protocol buffer representation. Not
1128 intended to be used by application developers. Enforced by hiding the
1129 datastore_pb classes.
1131 Args:
1132 # an upper bound on the number of results returned by the query.
1133 limit: int
1134 # number of results that match the query to skip. limit is applied
1135 # after the offset is fulfilled
1136 offset: int
1138 Returns:
1139 # the PB representation of this Query
1140 datastore_pb.Query
1142 pb = datastore_pb.Query()
1144 pb.set_kind(self.__kind.encode('utf-8'))
1145 pb.set_keys_only(bool(self.__keys_only))
1146 if self.__app:
1147 pb.set_app(self.__app.encode('utf-8'))
1148 if limit is not None:
1149 pb.set_limit(limit)
1150 if offset is not None:
1151 pb.set_offset(offset)
1152 if self.__ancestor:
1153 pb.mutable_ancestor().CopyFrom(self.__ancestor)
1155 if ((self.__hint == self.ORDER_FIRST and self.__orderings) or
1156 (self.__hint == self.ANCESTOR_FIRST and self.__ancestor) or
1157 (self.__hint == self.FILTER_FIRST and len(self) > 0)):
1158 pb.set_hint(self.__hint)
1160 ordered_filters = [(i, f) for f, i in self.__filter_order.iteritems()]
1161 ordered_filters.sort()
1163 for i, filter_str in ordered_filters:
1164 if filter_str not in self:
1165 continue
1167 values = self[filter_str]
1168 match = self._CheckFilter(filter_str, values)
1169 name = match.group(1)
1171 props = datastore_types.ToPropertyPb(name, values)
1172 if not isinstance(props, list):
1173 props = [props]
1175 op = match.group(3)
1176 if op is None:
1177 op = '='
1179 for prop in props:
1180 filter = pb.add_filter()
1181 filter.set_op(self.OPERATORS[op])
1182 filter.add_property().CopyFrom(prop)
1184 for property, direction in self.__orderings:
1185 order = pb.add_order()
1186 order.set_property(property.encode('utf-8'))
1187 order.set_direction(direction)
1189 return pb
1192 class MultiQuery(Query):
1193 """Class representing a query which requires multiple datastore queries.
1195 This class is actually a subclass of datastore.Query as it is intended to act
1196 like a normal Query object (supporting the same interface).
1198 Does not support keys only queries, since it needs whole entities in order
1199 to merge sort them. (That's not true if there are no sort orders, or if the
1200 sort order is on __key__, but allowing keys only queries in those cases, but
1201 not in others, would be confusing.)
1204 def __init__(self, bound_queries, orderings):
1205 if len(bound_queries) > MAX_ALLOWABLE_QUERIES:
1206 raise datastore_errors.BadArgumentError(
1207 'Cannot satisfy query -- too many subqueries (max: %d, got %d).'
1208 ' Probable cause: too many IN/!= filters in query.' %
1209 (MAX_ALLOWABLE_QUERIES, len(bound_queries)))
1211 for query in bound_queries:
1212 if query.IsKeysOnly():
1213 raise datastore_errors.BadQueryError(
1214 'MultiQuery does not support keys_only.')
1216 self.__bound_queries = bound_queries
1217 self.__orderings = orderings
1219 def __str__(self):
1220 res = 'MultiQuery: '
1221 for query in self.__bound_queries:
1222 res = '%s %s' % (res, str(query))
1223 return res
1225 def Get(self, limit, offset=0):
1226 """Get results of the query with a limit on the number of results.
1228 Args:
1229 limit: maximum number of values to return.
1230 offset: offset requested -- if nonzero, this will override the offset in
1231 the original query
1233 Returns:
1234 A list of entities with at most "limit" entries (less if the query
1235 completes before reading limit values).
1237 count = 1
1238 result = []
1240 iterator = self.Run()
1242 try:
1243 for i in xrange(offset):
1244 val = iterator.next()
1245 except StopIteration:
1246 pass
1248 try:
1249 while count <= limit:
1250 val = iterator.next()
1251 result.append(val)
1252 count += 1
1253 except StopIteration:
1254 pass
1255 return result
1257 class SortOrderEntity(object):
1258 """Allow entity comparisons using provided orderings.
1260 The iterator passed to the constructor is eventually consumed via
1261 calls to GetNext(), which generate new SortOrderEntity s with the
1262 same orderings.
1265 def __init__(self, entity_iterator, orderings):
1266 """Ctor.
1268 Args:
1269 entity_iterator: an iterator of entities which will be wrapped.
1270 orderings: an iterable of (identifier, order) pairs. order
1271 should be either Query.ASCENDING or Query.DESCENDING.
1273 self.__entity_iterator = entity_iterator
1274 self.__entity = None
1275 self.__min_max_value_cache = {}
1276 try:
1277 self.__entity = entity_iterator.next()
1278 except StopIteration:
1279 pass
1280 else:
1281 self.__orderings = orderings
1283 def __str__(self):
1284 return str(self.__entity)
1286 def GetEntity(self):
1287 """Gets the wrapped entity."""
1288 return self.__entity
1290 def GetNext(self):
1291 """Wrap and return the next entity.
1293 The entity is retrieved from the iterator given at construction time.
1295 return MultiQuery.SortOrderEntity(self.__entity_iterator,
1296 self.__orderings)
1298 def CmpProperties(self, that):
1299 """Compare two entities and return their relative order.
1301 Compares self to that based on the current sort orderings and the
1302 key orders between them. Returns negative, 0, or positive depending on
1303 whether self is less, equal to, or greater than that. This
1304 comparison returns as if all values were to be placed in ascending order
1305 (highest value last). Only uses the sort orderings to compare (ignores
1306 keys).
1308 Args:
1309 that: SortOrderEntity
1311 Returns:
1312 Negative if self < that
1313 Zero if self == that
1314 Positive if self > that
1316 if not self.__entity:
1317 return cmp(self.__entity, that.__entity)
1319 for (identifier, order) in self.__orderings:
1320 value1 = self.__GetValueForId(self, identifier, order)
1321 value2 = self.__GetValueForId(that, identifier, order)
1323 result = cmp(value1, value2)
1324 if order == Query.DESCENDING:
1325 result = -result
1326 if result:
1327 return result
1328 return 0
1330 def __GetValueForId(self, sort_order_entity, identifier, sort_order):
1331 value = _GetPropertyValue(sort_order_entity.__entity, identifier)
1332 entity_key = sort_order_entity.__entity.key()
1333 if (entity_key, identifier) in self.__min_max_value_cache:
1334 value = self.__min_max_value_cache[(entity_key, identifier)]
1335 elif isinstance(value, list):
1336 if sort_order == Query.DESCENDING:
1337 value = min(value)
1338 else:
1339 value = max(value)
1340 self.__min_max_value_cache[(entity_key, identifier)] = value
1342 return value
1344 def __cmp__(self, that):
1345 """Compare self to that w.r.t. values defined in the sort order.
1347 Compare an entity with another, using sort-order first, then the key
1348 order to break ties. This can be used in a heap to have faster min-value
1349 lookup.
1351 Args:
1352 that: other entity to compare to
1353 Returns:
1354 negative: if self is less than that in sort order
1355 zero: if self is equal to that in sort order
1356 positive: if self is greater than that in sort order
1358 property_compare = self.CmpProperties(that)
1359 if property_compare:
1360 return property_compare
1361 else:
1362 return cmp(self.__entity.key(), that.__entity.key())
1364 def Run(self):
1365 """Return an iterable output with all results in order."""
1366 results = []
1367 count = 1
1368 log_level = logging.DEBUG - 1
1369 for bound_query in self.__bound_queries:
1370 logging.log(log_level, 'Running query #%i' % count)
1371 results.append(bound_query.Run())
1372 count += 1
1374 def IterateResults(results):
1375 """Iterator function to return all results in sorted order.
1377 Iterate over the array of results, yielding the next element, in
1378 sorted order. This function is destructive (results will be empty
1379 when the operation is complete).
1381 Args:
1382 results: list of result iterators to merge and iterate through
1384 Yields:
1385 The next result in sorted order.
1387 result_heap = []
1388 for result in results:
1389 heap_value = MultiQuery.SortOrderEntity(result, self.__orderings)
1390 if heap_value.GetEntity():
1391 heapq.heappush(result_heap, heap_value)
1393 used_keys = set()
1395 while result_heap:
1396 top_result = heapq.heappop(result_heap)
1398 results_to_push = []
1399 if top_result.GetEntity().key() not in used_keys:
1400 yield top_result.GetEntity()
1401 else:
1402 pass
1404 used_keys.add(top_result.GetEntity().key())
1406 results_to_push = []
1407 while result_heap:
1408 next = heapq.heappop(result_heap)
1409 if cmp(top_result, next):
1410 results_to_push.append(next)
1411 break
1412 else:
1413 results_to_push.append(next.GetNext())
1414 results_to_push.append(top_result.GetNext())
1416 for popped_result in results_to_push:
1417 if popped_result.GetEntity():
1418 heapq.heappush(result_heap, popped_result)
1420 return IterateResults(results)
1422 def Count(self, limit=None):
1423 """Return the number of matched entities for this query.
1425 Will return the de-duplicated count of results. Will call the more
1426 efficient Get() function if a limit is given.
1428 Args:
1429 limit: maximum number of entries to count (for any result > limit, return
1430 limit).
1431 Returns:
1432 count of the number of entries returned.
1434 if limit is None:
1435 count = 0
1436 for i in self.Run():
1437 count += 1
1438 return count
1439 else:
1440 return len(self.Get(limit))
1442 def __setitem__(self, query_filter, value):
1443 """Add a new filter by setting it on all subqueries.
1445 If any of the setting operations raise an exception, the ones
1446 that succeeded are undone and the exception is propagated
1447 upward.
1449 Args:
1450 query_filter: a string of the form "property operand".
1451 value: the value that the given property is compared against.
1453 saved_items = []
1454 for index, query in enumerate(self.__bound_queries):
1455 saved_items.append(query.get(query_filter, None))
1456 try:
1457 query[query_filter] = value
1458 except:
1459 for q, old_value in itertools.izip(self.__bound_queries[:index],
1460 saved_items):
1461 if old_value is not None:
1462 q[query_filter] = old_value
1463 else:
1464 del q[query_filter]
1465 raise
1467 def __delitem__(self, query_filter):
1468 """Delete a filter by deleting it from all subqueries.
1470 If a KeyError is raised during the attempt, it is ignored, unless
1471 every subquery raised a KeyError. If any other exception is
1472 raised, any deletes will be rolled back.
1474 Args:
1475 query_filter: the filter to delete.
1477 Raises:
1478 KeyError: No subquery had an entry containing query_filter.
1480 subquery_count = len(self.__bound_queries)
1481 keyerror_count = 0
1482 saved_items = []
1483 for index, query in enumerate(self.__bound_queries):
1484 try:
1485 saved_items.append(query.get(query_filter, None))
1486 del query[query_filter]
1487 except KeyError:
1488 keyerror_count += 1
1489 except:
1490 for q, old_value in itertools.izip(self.__bound_queries[:index],
1491 saved_items):
1492 if old_value is not None:
1493 q[query_filter] = old_value
1494 raise
1496 if keyerror_count == subquery_count:
1497 raise KeyError(query_filter)
1499 def __iter__(self):
1500 return iter(self.__bound_queries)
1503 class Iterator(object):
1504 """An iterator over the results of a datastore query.
1506 Iterators are used to access the results of a Query. An iterator is
1507 obtained by building a Query, then calling Run() on it.
1509 Iterator implements Python's iterator protocol, so results can be accessed
1510 with the for and in statements:
1512 > it = Query('Person').Run()
1513 > for person in it:
1514 > print 'Hi, %s!' % person['name']
1516 def __init__(self, cursor, keys_only=False):
1517 self.__cursor = cursor
1518 self.__buffer = []
1519 self.__more_results = True
1520 self.__keys_only = keys_only
1522 def _Next(self, count):
1523 """Returns the next result(s) of the query.
1525 Not intended to be used by application developers. Use the python
1526 iterator protocol instead.
1528 This method returns the next entities or keys from the list of matching
1529 results. If the query specified a sort order, results are returned in that
1530 order. Otherwise, the order is undefined.
1532 The argument specifies the number of results to return. If it's greater
1533 than the number of remaining results, all of the remaining results are
1534 returned. In that case, the length of the returned list will be smaller
1535 than count.
1537 There is an internal buffer for use with the next() method. If this buffer
1538 is not empty, up to 'count' values are removed from this buffer and
1539 returned. It's best not to mix _Next() and next().
1541 The results are always returned as a list. If there are no results left,
1542 an empty list is returned.
1544 Args:
1545 # the number of results to return; must be >= 1
1546 count: int or long
1548 Returns:
1549 # a list of entities or keys
1550 [Entity or Key, ...]
1552 if not isinstance(count, (int, long)) or count <= 0:
1553 raise datastore_errors.BadArgumentError(
1554 'Argument to _Next must be an int greater than 0; received %s (a %s)' %
1555 (count, typename(count)))
1557 if self.__buffer:
1558 raise datastore_errors.BadRequestError(
1559 'You can\'t mix next() and _Next()')
1561 if not self.__more_results:
1562 return []
1564 req = datastore_pb.NextRequest()
1565 req.set_count(count)
1566 req.mutable_cursor().CopyFrom(self._ToPb())
1567 result = datastore_pb.QueryResult()
1568 try:
1569 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Next', req, result)
1570 except apiproxy_errors.ApplicationError, err:
1571 raise _ToDatastoreError(err)
1573 self.__more_results = result.more_results()
1575 if self.__keys_only:
1576 return [Key._FromPb(e.key()) for e in result.result_list()]
1577 else:
1578 return [Entity._FromPb(e) for e in result.result_list()]
1580 _BUFFER_SIZE = 20
1582 def next(self):
1583 if not self.__buffer:
1584 self.__buffer = self._Next(self._BUFFER_SIZE)
1585 try:
1586 return self.__buffer.pop(0)
1587 except IndexError:
1588 raise StopIteration
1590 def __iter__(self): return self
1592 def _ToPb(self):
1593 """Converts this Iterator to its protocol buffer representation. Not
1594 intended to be used by application developers. Enforced by hiding the
1595 datastore_pb classes.
1597 Returns:
1598 # the PB representation of this Iterator
1599 datastore_pb.Cursor
1601 pb = datastore_pb.Cursor()
1602 pb.set_cursor(self.__cursor)
1603 return pb
1605 @staticmethod
1606 def _FromPb(pb):
1607 """Static factory method. Returns the Iterator representation of the given
1608 protocol buffer (datastore_pb.QueryResult). Not intended to be used by
1609 application developers. Enforced by hiding the datastore_pb classes.
1611 Args:
1612 pb: datastore_pb.QueryResult
1614 Returns:
1615 Iterator
1617 return Iterator(pb.cursor().cursor(), keys_only=pb.keys_only())
1620 class _Transaction(object):
1621 """Encapsulates a transaction currently in progress.
1623 If we've sent a BeginTransaction call, then handle will be a
1624 datastore_pb.Transaction that holds the transaction handle.
1626 If we know the entity group for this transaction, it's stored in the
1627 entity_group attribute, which is set by RecordModifiedKeys().
1629 modified_keys is a set containing the Keys of all entities modified (ie put
1630 or deleted) in this transaction. If an entity is modified more than once, a
1631 BadRequestError is raised.
1633 def __init__(self):
1634 """Initializes modified_keys to the empty set."""
1635 self.handle = None
1636 self.entity_group = None
1637 self.modified_keys = None
1638 self.modified_keys = set()
1640 def RecordModifiedKeys(self, keys, error_on_repeat=True):
1641 """Updates the modified keys seen so far.
1643 Also sets entity_group if it hasn't yet been set.
1645 If error_on_repeat is True and any of the given keys have already been
1646 modified, raises BadRequestError.
1648 Args:
1649 keys: sequence of Keys
1651 keys, _ = NormalizeAndTypeCheckKeys(keys)
1653 if keys and not self.entity_group:
1654 self.entity_group = keys[0].entity_group()
1656 keys = set(keys)
1658 if error_on_repeat:
1659 already_modified = self.modified_keys.intersection(keys)
1660 if already_modified:
1661 raise datastore_errors.BadRequestError(
1662 "Can't update entity more than once in a transaction: %r" %
1663 already_modified.pop())
1665 self.modified_keys.update(keys)
1668 def RunInTransaction(function, *args, **kwargs):
1669 """Runs a function inside a datastore transaction.
1671 Runs the user-provided function inside transaction, retries default
1672 number of times.
1674 Args:
1675 # a function to be run inside the transaction
1676 function: callable
1677 # positional arguments to pass to the function
1678 args: variable number of any type
1680 Returns:
1681 the function's return value, if any
1683 Raises:
1684 TransactionFailedError, if the transaction could not be committed.
1686 return RunInTransactionCustomRetries(
1687 DEFAULT_TRANSACTION_RETRIES, function, *args, **kwargs)
1690 def RunInTransactionCustomRetries(retries, function, *args, **kwargs):
1691 """Runs a function inside a datastore transaction.
1693 Runs the user-provided function inside a full-featured, ACID datastore
1694 transaction. Every Put, Get, and Delete call in the function is made within
1695 the transaction. All entities involved in these calls must belong to the
1696 same entity group. Queries are not supported.
1698 The trailing arguments are passed to the function as positional arguments.
1699 If the function returns a value, that value will be returned by
1700 RunInTransaction. Otherwise, it will return None.
1702 The function may raise any exception to roll back the transaction instead of
1703 committing it. If this happens, the transaction will be rolled back and the
1704 exception will be re-raised up to RunInTransaction's caller.
1706 If you want to roll back intentionally, but don't have an appropriate
1707 exception to raise, you can raise an instance of datastore_errors.Rollback.
1708 It will cause a rollback, but will *not* be re-raised up to the caller.
1710 The function may be run more than once, so it should be idempotent. It
1711 should avoid side effects, and it shouldn't have *any* side effects that
1712 aren't safe to occur multiple times. This includes modifying the arguments,
1713 since they persist across invocations of the function. However, this doesn't
1714 include Put, Get, and Delete calls, of course.
1716 Example usage:
1718 > def decrement(key, amount=1):
1719 > counter = datastore.Get(key)
1720 > counter['count'] -= amount
1721 > if counter['count'] < 0: # don't let the counter go negative
1722 > raise datastore_errors.Rollback()
1723 > datastore.Put(counter)
1725 > counter = datastore.Query('Counter', {'name': 'foo'})
1726 > datastore.RunInTransaction(decrement, counter.key(), amount=5)
1728 Transactions satisfy the traditional ACID properties. They are:
1730 - Atomic. All of a transaction's operations are executed or none of them are.
1732 - Consistent. The datastore's state is consistent before and after a
1733 transaction, whether it committed or rolled back. Invariants such as
1734 "every entity has a primary key" are preserved.
1736 - Isolated. Transactions operate on a snapshot of the datastore. Other
1737 datastore operations do not see intermediated effects of the transaction;
1738 they only see its effects after it has committed.
1740 - Durable. On commit, all writes are persisted to the datastore.
1742 Nested transactions are not supported.
1744 Args:
1745 # number of retries
1746 retries: integer
1747 # a function to be run inside the transaction
1748 function: callable
1749 # positional arguments to pass to the function
1750 args: variable number of any type
1752 Returns:
1753 the function's return value, if any
1755 Raises:
1756 TransactionFailedError, if the transaction could not be committed.
1759 if _CurrentTransactionKey():
1760 raise datastore_errors.BadRequestError(
1761 'Nested transactions are not supported.')
1763 if retries < 0:
1764 raise datastore_errors.BadRequestError(
1765 'Number of retries should be non-negative number.')
1767 tx_key = None
1769 try:
1770 tx_key = _NewTransactionKey()
1771 tx = _Transaction()
1772 _txes[tx_key] = tx
1774 for i in range(0, retries + 1):
1775 tx.modified_keys.clear()
1777 try:
1778 result = function(*args, **kwargs)
1779 except:
1780 original_exception = sys.exc_info()
1782 if tx.handle:
1783 try:
1784 resp = api_base_pb.VoidProto()
1785 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Rollback',
1786 tx.handle, resp)
1787 except:
1788 exc_info = sys.exc_info()
1789 logging.info('Exception sending Rollback:\n' +
1790 ''.join(traceback.format_exception(*exc_info)))
1792 type, value, trace = original_exception
1793 if type is datastore_errors.Rollback:
1794 return
1795 else:
1796 raise type, value, trace
1798 if tx.handle:
1799 try:
1800 resp = datastore_pb.CommitResponse()
1801 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Commit',
1802 tx.handle, resp)
1803 except apiproxy_errors.ApplicationError, err:
1804 if (err.application_error ==
1805 datastore_pb.Error.CONCURRENT_TRANSACTION):
1806 logging.warning('Transaction collision for entity group with '
1807 'key %r. Retrying...', tx.entity_group)
1808 tx.handle = None
1809 tx.entity_group = None
1810 continue
1811 else:
1812 raise _ToDatastoreError(err)
1814 return result
1816 raise datastore_errors.TransactionFailedError(
1817 'The transaction could not be committed. Please try again.')
1819 finally:
1820 if tx_key in _txes:
1821 del _txes[tx_key]
1822 del tx_key
1825 def _MaybeSetupTransaction(request, keys):
1826 """Begins a transaction, if necessary, and populates it in the request.
1828 If we're currently inside a transaction, this records the entity group,
1829 checks that the keys are all in that entity group, creates the transaction
1830 PB, and sends the BeginTransaction. It then populates the transaction handle
1831 in the request.
1833 Raises BadRequestError if the entity has a different entity group than the
1834 current transaction.
1836 Args:
1837 request: GetRequest, PutRequest, or DeleteRequest
1838 keys: sequence of Keys
1840 Returns:
1841 _Transaction if we're inside a transaction, otherwise None
1843 assert isinstance(request, (datastore_pb.GetRequest, datastore_pb.PutRequest,
1844 datastore_pb.DeleteRequest))
1845 tx_key = None
1847 try:
1848 tx_key = _CurrentTransactionKey()
1849 if tx_key:
1850 tx = _txes[tx_key]
1852 groups = [k.entity_group() for k in keys]
1853 if tx.entity_group:
1854 expected_group = tx.entity_group
1855 else:
1856 expected_group = groups[0]
1857 for group in groups:
1858 if (group != expected_group or
1866 (not group.has_id_or_name() and group is not expected_group)):
1867 raise _DifferentEntityGroupError(expected_group, group)
1869 if not tx.handle:
1870 tx.handle = datastore_pb.Transaction()
1871 req = api_base_pb.VoidProto()
1872 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'BeginTransaction', req,
1873 tx.handle)
1875 request.mutable_transaction().CopyFrom(tx.handle)
1877 return tx
1879 finally:
1880 del tx_key
1883 def _DifferentEntityGroupError(a, b):
1884 """Raises a BadRequestError that says the given entity groups are different.
1886 Includes the two entity groups in the message, formatted more clearly and
1887 concisely than repr(Key).
1889 Args:
1890 a, b are both Keys that represent entity groups.
1892 def id_or_name(key):
1893 if key.name():
1894 return 'name=%r' % key.name()
1895 else:
1896 return 'id=%r' % key.id()
1898 raise datastore_errors.BadRequestError(
1899 'Cannot operate on different entity groups in a transaction: '
1900 '(kind=%r, %s) and (kind=%r, %s).' % (a.kind(), id_or_name(a),
1901 b.kind(), id_or_name(b)))
1904 def _FindTransactionFrameInStack():
1905 """Walks the stack to find a RunInTransaction() call.
1907 Returns:
1908 # this is the RunInTransactionCustomRetries() frame record, if found
1909 frame record or None
1911 frame = sys._getframe()
1912 filename = frame.f_code.co_filename
1914 frame = frame.f_back.f_back
1915 while frame:
1916 if (frame.f_code.co_filename == filename and
1917 frame.f_code.co_name == 'RunInTransactionCustomRetries'):
1918 return frame
1919 frame = frame.f_back
1921 return None
1923 _CurrentTransactionKey = _FindTransactionFrameInStack
1925 _NewTransactionKey = sys._getframe
1928 def _GetCompleteKeyOrError(arg):
1929 """Expects an Entity or a Key, and returns the corresponding Key. Raises
1930 BadArgumentError or BadKeyError if arg is a different type or is incomplete.
1932 Args:
1933 arg: Entity or Key
1935 Returns:
1938 if isinstance(arg, Key):
1939 key = arg
1940 elif isinstance(arg, basestring):
1941 key = Key(arg)
1942 elif isinstance(arg, Entity):
1943 key = arg.key()
1944 elif not isinstance(arg, Key):
1945 raise datastore_errors.BadArgumentError(
1946 'Expects argument to be an Entity or Key; received %s (a %s).' %
1947 (arg, typename(arg)))
1948 assert isinstance(key, Key)
1950 if not key.has_id_or_name():
1951 raise datastore_errors.BadKeyError('Key %r is not complete.' % key)
1953 return key
1956 def _GetPropertyValue(entity, property):
1957 """Returns an entity's value for a given property name.
1959 Handles special properties like __key__ as well as normal properties.
1961 Args:
1962 entity: datastore.Entity
1963 property: str; the property name
1965 Returns:
1966 property value. For __key__, a datastore_types.Key.
1968 Raises:
1969 KeyError, if the entity does not have the given property.
1971 if property in datastore_types._SPECIAL_PROPERTIES:
1972 assert property == datastore_types._KEY_SPECIAL_PROPERTY
1973 return entity.key()
1974 else:
1975 return entity[property]
1978 def _AddOrAppend(dictionary, key, value):
1979 """Adds the value to the existing values in the dictionary, if any.
1981 If dictionary[key] doesn't exist, sets dictionary[key] to value.
1983 If dictionary[key] is not a list, sets dictionary[key] to [old_value, value].
1985 If dictionary[key] is a list, appends value to that list.
1987 Args:
1988 dictionary: a dict
1989 key, value: anything
1991 if key in dictionary:
1992 existing_value = dictionary[key]
1993 if isinstance(existing_value, list):
1994 existing_value.append(value)
1995 else:
1996 dictionary[key] = [existing_value, value]
1997 else:
1998 dictionary[key] = value
2001 def _ToDatastoreError(err):
2002 """Converts an apiproxy.ApplicationError to an error in datastore_errors.
2004 Args:
2005 err: apiproxy.ApplicationError
2007 Returns:
2008 a subclass of datastore_errors.Error
2010 errors = {
2011 datastore_pb.Error.BAD_REQUEST: datastore_errors.BadRequestError,
2012 datastore_pb.Error.CONCURRENT_TRANSACTION:
2013 datastore_errors.TransactionFailedError,
2014 datastore_pb.Error.INTERNAL_ERROR: datastore_errors.InternalError,
2015 datastore_pb.Error.NEED_INDEX: datastore_errors.NeedIndexError,
2016 datastore_pb.Error.TIMEOUT: datastore_errors.Timeout,
2019 if err.application_error in errors:
2020 raise errors[err.application_error](err.error_detail)
2021 else:
2022 raise datastore_errors.Error(err.error_detail)