thirdparty/google_appengine/google/appengine/api/datastore_file_stub.py
changeset 297 35211afcd563
parent 209 4ba836d74829
child 686 df109be0567c
equal deleted inserted replaced
296:b02dd2a5f329 297:35211afcd563
    62 
    62 
    63 entity_pb.Reference.__hash__ = lambda self: hash(self.Encode())
    63 entity_pb.Reference.__hash__ = lambda self: hash(self.Encode())
    64 datastore_pb.Query.__hash__ = lambda self: hash(self.Encode())
    64 datastore_pb.Query.__hash__ = lambda self: hash(self.Encode())
    65 
    65 
    66 
    66 
       
    67 class _StoredEntity(object):
       
    68   """Simple wrapper around an entity stored by the stub.
       
    69 
       
    70   Public properties:
       
    71     native: Native protobuf Python object, entity_pb.EntityProto.
       
    72     encoded: Encoded binary representation of above protobuf.
       
    73   """
       
    74 
       
    75   def __init__(self, entity):
       
    76     """Create a _StoredEntity object and store an entity.
       
    77 
       
    78     Args:
       
    79       entity: entity_pb.EntityProto to store.
       
    80     """
       
    81     self.native = entity
       
    82 
       
    83     self.encoded = entity.Encode()
       
    84 
       
    85 
    67 class DatastoreFileStub(object):
    86 class DatastoreFileStub(object):
    68   """ Persistent stub for the Python datastore API.
    87   """ Persistent stub for the Python datastore API.
    69 
    88 
    70   Stores all entities in memory, and persists them to a file as pickled
    89   Stores all entities in memory, and persists them to a file as pickled
    71   protocol buffers. A DatastoreFileStub instance handles a single app's data
    90   protocol buffers. A DatastoreFileStub instance handles a single app's data
    72   and is backed by files on disk.
    91   and is backed by files on disk.
    73   """
    92   """
       
    93 
       
    94   _PROPERTY_TYPE_TAGS = {
       
    95     datastore_types.Blob: entity_pb.PropertyValue.kstringValue,
       
    96     bool: entity_pb.PropertyValue.kbooleanValue,
       
    97     datastore_types.Category: entity_pb.PropertyValue.kstringValue,
       
    98     datetime.datetime: entity_pb.PropertyValue.kint64Value,
       
    99     datastore_types.Email: entity_pb.PropertyValue.kstringValue,
       
   100     float: entity_pb.PropertyValue.kdoubleValue,
       
   101     datastore_types.GeoPt: entity_pb.PropertyValue.kPointValueGroup,
       
   102     datastore_types.IM: entity_pb.PropertyValue.kstringValue,
       
   103     int: entity_pb.PropertyValue.kint64Value,
       
   104     datastore_types.Key: entity_pb.PropertyValue.kReferenceValueGroup,
       
   105     datastore_types.Link: entity_pb.PropertyValue.kstringValue,
       
   106     long: entity_pb.PropertyValue.kint64Value,
       
   107     datastore_types.PhoneNumber: entity_pb.PropertyValue.kstringValue,
       
   108     datastore_types.PostalAddress: entity_pb.PropertyValue.kstringValue,
       
   109     datastore_types.Rating: entity_pb.PropertyValue.kint64Value,
       
   110     str: entity_pb.PropertyValue.kstringValue,
       
   111     datastore_types.Text: entity_pb.PropertyValue.kstringValue,
       
   112     type(None): 0,
       
   113     unicode: entity_pb.PropertyValue.kstringValue,
       
   114     users.User: entity_pb.PropertyValue.kUserValueGroup,
       
   115     }
    74 
   116 
    75   def __init__(self, app_id, datastore_file, history_file,
   117   def __init__(self, app_id, datastore_file, history_file,
    76                require_indexes=False):
   118                require_indexes=False):
    77     """Constructor.
   119     """Constructor.
    78 
   120 
   157                                                (self.__datastore_file, e))
   199                                                (self.__datastore_file, e))
   158 
   200 
   159         last_path = entity.key().path().element_list()[-1]
   201         last_path = entity.key().path().element_list()[-1]
   160         app_kind = (entity.key().app(), last_path.type())
   202         app_kind = (entity.key().app(), last_path.type())
   161         kind_dict = self.__entities.setdefault(app_kind, {})
   203         kind_dict = self.__entities.setdefault(app_kind, {})
   162         kind_dict[entity.key()] = entity
   204         kind_dict[entity.key()] = _StoredEntity(entity)
   163 
   205 
   164         if last_path.has_id() and last_path.id() >= self.__next_id:
   206         if last_path.has_id() and last_path.id() >= self.__next_id:
   165           self.__next_id = last_path.id() + 1
   207           self.__next_id = last_path.id() + 1
   166 
   208 
   167       self.__query_history = {}
   209       self.__query_history = {}
   190     """
   232     """
   191     if self.__datastore_file and self.__datastore_file != '/dev/null':
   233     if self.__datastore_file and self.__datastore_file != '/dev/null':
   192       encoded = []
   234       encoded = []
   193       for kind_dict in self.__entities.values():
   235       for kind_dict in self.__entities.values():
   194         for entity in kind_dict.values():
   236         for entity in kind_dict.values():
   195           encoded.append(entity.Encode())
   237           encoded.append(entity.encoded)
   196 
   238 
   197       self.__WritePickled(encoded, self.__datastore_file)
   239       self.__WritePickled(encoded, self.__datastore_file)
   198 
   240 
   199   def __WriteHistory(self):
   241   def __WriteHistory(self):
   200     """ Writes out the history file. Be careful! If the file already exist,
   242     """ Writes out the history file. Be careful! If the file already exist,
   301 
   343 
   302     try:
   344     try:
   303       for clone in clones:
   345       for clone in clones:
   304         last_path = clone.key().path().element_list()[-1]
   346         last_path = clone.key().path().element_list()[-1]
   305         kind_dict = self.__entities.setdefault((app, last_path.type()), {})
   347         kind_dict = self.__entities.setdefault((app, last_path.type()), {})
   306         kind_dict[clone.key()] = clone
   348         kind_dict[clone.key()] = _StoredEntity(clone)
   307     finally:
   349     finally:
   308       self.__entities_lock.release()
   350       self.__entities_lock.release()
   309 
   351 
   310     if not put_request.has_transaction():
   352     if not put_request.has_transaction():
   311       self.__WriteDatastore()
   353       self.__WriteDatastore()
   318         app = key.app()
   360         app = key.app()
   319         last_path = key.path().element_list()[-1]
   361         last_path = key.path().element_list()[-1]
   320 
   362 
   321         group = get_response.add_entity()
   363         group = get_response.add_entity()
   322         try:
   364         try:
   323           entity = self.__entities[app, last_path.type()][key]
   365           entity = self.__entities[app, last_path.type()][key].native
   324         except KeyError:
   366         except KeyError:
   325           entity = None
   367           entity = None
   326 
   368 
   327         if entity:
   369         if entity:
   328           group.mutable_entity().CopyFrom(entity)
   370           group.mutable_entity().CopyFrom(entity)
   388               "You must update the index.yaml file in your application root.")
   430               "You must update the index.yaml file in your application root.")
   389 
   431 
   390     try:
   432     try:
   391       query.set_app(app)
   433       query.set_app(app)
   392       results = self.__entities[app, query.kind()].values()
   434       results = self.__entities[app, query.kind()].values()
   393       results = [datastore.Entity._FromPb(pb) for pb in results]
   435       results = [datastore.Entity._FromPb(entity.native) for entity in results]
   394     except KeyError:
   436     except KeyError:
   395       results = []
   437       results = []
   396 
   438 
   397     if query.has_ancestor():
   439     if query.has_ancestor():
   398       ancestor_path = query.ancestor().path().element_list()
   440       ancestor_path = query.ancestor().path().element_list()
   430             continue
   472             continue
   431 
   473 
   432           for filter_prop in filt.property_list():
   474           for filter_prop in filt.property_list():
   433             filter_val = datastore_types.FromPropertyPb(filter_prop)
   475             filter_val = datastore_types.FromPropertyPb(filter_prop)
   434 
   476 
   435             comp = u'%r %s %r' % (fixed_entity_val, op, filter_val)
   477             fixed_entity_type = self._PROPERTY_TYPE_TAGS.get(
       
   478               fixed_entity_val.__class__)
       
   479             filter_type = self._PROPERTY_TYPE_TAGS.get(filter_val.__class__)
       
   480             if fixed_entity_type == filter_type:
       
   481               comp = u'%r %s %r' % (fixed_entity_val, op, filter_val)
       
   482             elif op != '==':
       
   483               comp = '%r %s %r' % (fixed_entity_type, op, filter_type)
       
   484             else:
       
   485               continue
   436 
   486 
   437             logging.log(logging.DEBUG - 1,
   487             logging.log(logging.DEBUG - 1,
   438                         'Evaling filter expression "%s"', comp)
   488                         'Evaling filter expression "%s"', comp)
   439 
   489 
   440             try:
   490             try:
   461 
   511 
   462     for order in query.order_list():
   512     for order in query.order_list():
   463       prop = order.property().decode('utf-8')
   513       prop = order.property().decode('utf-8')
   464       results = [entity for entity in results if has_prop_indexed(entity, prop)]
   514       results = [entity for entity in results if has_prop_indexed(entity, prop)]
   465 
   515 
   466     def order_compare(a, b):
   516     def order_compare_entities(a, b):
   467       """ Return a negative, zero or positive number depending on whether
   517       """ Return a negative, zero or positive number depending on whether
   468       entity a is considered smaller than, equal to, or larger than b,
   518       entity a is considered smaller than, equal to, or larger than b,
   469       according to the query's orderings. """
   519       according to the query's orderings. """
   470       cmped = 0
   520       cmped = 0
   471       for o in query.order_list():
   521       for o in query.order_list():
   472         prop = o.property().decode('utf-8')
   522         prop = o.property().decode('utf-8')
   473 
   523 
   474         if o.direction() is datastore_pb.Query_Order.ASCENDING:
   524         reverse = (o.direction() is datastore_pb.Query_Order.DESCENDING)
   475           selector = min
       
   476         else:
       
   477           selector = max
       
   478 
   525 
   479         a_val = a[prop]
   526         a_val = a[prop]
   480         if isinstance(a_val, list):
   527         if isinstance(a_val, list):
   481           a_val = selector(a_val)
   528           a_val = sorted(a_val, order_compare_properties, reverse=reverse)[0]
   482 
   529 
   483         b_val = b[prop]
   530         b_val = b[prop]
   484         if isinstance(b_val, list):
   531         if isinstance(b_val, list):
   485           b_val = selector(b_val)
   532           b_val = sorted(b_val, order_compare_properties, reverse=reverse)[0]
   486 
   533 
   487         try:
   534         cmped = order_compare_properties(a_val, b_val)
   488           cmped = cmp(a_val, b_val)
       
   489         except TypeError:
       
   490           cmped = NotImplementedError
       
   491 
       
   492         if cmped == NotImplementedError:
       
   493           cmped = cmp(type(a_val), type(b_val))
       
   494 
   535 
   495         if o.direction() is datastore_pb.Query_Order.DESCENDING:
   536         if o.direction() is datastore_pb.Query_Order.DESCENDING:
   496           cmped = -cmped
   537           cmped = -cmped
   497 
   538 
   498         if cmped != 0:
   539         if cmped != 0:
   499           return cmped
   540           return cmped
       
   541 
   500       if cmped == 0:
   542       if cmped == 0:
   501         return cmp(a.key(), b.key())
   543         return cmp(a.key(), b.key())
   502 
   544 
   503     results.sort(order_compare)
   545     def order_compare_properties(x, y):
       
   546       """Return a negative, zero or positive number depending on whether
       
   547       property value x is considered smaller than, equal to, or larger than
       
   548       property value y. If x and y are different types, they're compared based
       
   549       on the type ordering used in the real datastore, which is based on the
       
   550       tag numbers in the PropertyValue PB.
       
   551       """
       
   552       if isinstance(x, datetime.datetime):
       
   553         x = datastore_types.DatetimeToTimestamp(x)
       
   554       if isinstance(y, datetime.datetime):
       
   555         y = datastore_types.DatetimeToTimestamp(y)
       
   556 
       
   557       x_type = self._PROPERTY_TYPE_TAGS.get(x.__class__)
       
   558       y_type = self._PROPERTY_TYPE_TAGS.get(y.__class__)
       
   559 
       
   560       if x_type == y_type:
       
   561         try:
       
   562           return cmp(x, y)
       
   563         except TypeError:
       
   564           return 0
       
   565       else:
       
   566         return cmp(x_type, y_type)
       
   567 
       
   568     results.sort(order_compare_entities)
   504 
   569 
   505     offset = 0
   570     offset = 0
   506     limit = len(results)
   571     limit = len(results)
   507     if query.has_offset():
   572     if query.has_offset():
   508       offset = query.offset()
   573       offset = query.offset()
   612         kinds.append(kind_pb)
   677         kinds.append(kind_pb)
   613 
   678 
   614         props = {}
   679         props = {}
   615 
   680 
   616         for entity in self.__entities[(app, kind)].values():
   681         for entity in self.__entities[(app, kind)].values():
   617           for prop in entity.property_list():
   682           for prop in entity.native.property_list():
   618             if prop.name() not in props:
   683             if prop.name() not in props:
   619               props[prop.name()] = entity_pb.PropertyValue()
   684               props[prop.name()] = entity_pb.PropertyValue()
   620             props[prop.name()].MergeFrom(prop.value())
   685             props[prop.name()].MergeFrom(prop.value())
   621 
   686 
   622         for value_pb in props.values():
   687         for value_pb in props.values():