thirdparty/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py
changeset 2864 2e0b0af889be
parent 2413 d0b7dac5325c
child 3031 7678f72140e6
--- a/thirdparty/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py	Sat Sep 05 14:04:24 2009 +0200
+++ b/thirdparty/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py	Sun Sep 06 23:31:53 2009 +0200
@@ -71,6 +71,8 @@
 import threading
 import yaml
 
+from google.appengine.api import datastore
+from google.appengine.api import apiproxy_rpc
 from google.appengine.api import apiproxy_stub_map
 from google.appengine.datastore import datastore_pb
 from google.appengine.ext.remote_api import remote_api_pb
@@ -86,6 +88,10 @@
   """Exception for configuration errors."""
 
 
+class UnknownJavaServerError(Error):
+  """Exception for exceptions returned from a Java remote_api handler."""
+
+
 def GetUserAgent():
   """Determines the value of the 'User-agent' header to use for HTTP requests.
 
@@ -136,20 +142,41 @@
     self._server = server
     self._path = path
 
+  def _PreHookHandler(self, service, call, request, response):
+    pass
+
+  def _PostHookHandler(self, service, call, request, response):
+    pass
+
   def MakeSyncCall(self, service, call, request, response):
+    self._PreHookHandler(service, call, request, response)
     request_pb = remote_api_pb.Request()
     request_pb.set_service_name(service)
     request_pb.set_method(call)
     request_pb.mutable_request().set_contents(request.Encode())
 
     response_pb = remote_api_pb.Response()
-    response_pb.ParseFromString(self._server.Send(self._path,
-                                                  request_pb.Encode()))
+    encoded_request = request_pb.Encode()
+    encoded_response = self._server.Send(self._path, encoded_request)
+    response_pb.ParseFromString(encoded_response)
 
-    if response_pb.has_exception():
-      raise pickle.loads(response_pb.exception().contents())
-    else:
-      response.ParseFromString(response_pb.response().contents())
+    try:
+      if response_pb.has_application_error():
+        error_pb = response_pb.application_error()
+        raise datastore._ToDatastoreError(
+            apiproxy_errors.ApplicationError(error_pb.code(), error_pb.detail()))
+      elif response_pb.has_exception():
+        raise pickle.loads(response_pb.exception().contents())
+      elif response_pb.has_java_exception():
+        raise UnknownJavaServerError("An unknown error has occured in the "
+                                     "Java remote_api handler for this call.")
+      else:
+        response.ParseFromString(response_pb.response().contents())
+    finally:
+      self._PostHookHandler(service, call, request, response)
+
+  def CreateRPC(self):
+    return apiproxy_rpc.RPC(stub=self)
 
 
 class RemoteDatastoreStub(RemoteStub):
@@ -192,10 +219,12 @@
       self.__next_local_cursor += 1
     finally:
       self.__local_cursor_lock.release()
+    query.clear_count()
     self.__queries[cursor_id] = query
 
     query_result.mutable_cursor().set_cursor(cursor_id)
     query_result.set_more_results(True)
+    query_result.set_keys_only(query.keys_only())
 
   def _Dynamic_Next(self, next_request, query_result):
     cursor = next_request.cursor().cursor()
@@ -214,6 +243,7 @@
       request.set_limit(min(request.limit(), next_request.count()))
     else:
       request.set_limit(next_request.count())
+    request.set_count(request.limit())
 
     super(RemoteDatastoreStub, self).MakeSyncCall(
         'remote_datastore', 'RunQuery', request, query_result)
@@ -229,8 +259,8 @@
     if get_request.has_transaction():
       txid = get_request.transaction().handle()
       txdata = self.__transactions[txid]
-      assert (txdata.thread_id == thread.get_ident(),
-              "Transactions are single-threaded.")
+      assert (txdata.thread_id ==
+          thread.get_ident()), "Transactions are single-threaded."
 
       keys = [(k, k.Encode()) for k in get_request.key_list()]
 
@@ -296,8 +326,8 @@
 
       txid = put_request.transaction().handle()
       txdata = self.__transactions[txid]
-      assert (txdata.thread_id == thread.get_ident(),
-              "Transactions are single-threaded.")
+      assert (txdata.thread_id ==
+          thread.get_ident()), "Transactions are single-threaded."
       for entity in entities:
         txdata.entities[entity.key().Encode()] = (entity.key(), entity)
         put_response.add_key().CopyFrom(entity.key())
@@ -309,8 +339,8 @@
     if delete_request.has_transaction():
       txid = delete_request.transaction().handle()
       txdata = self.__transactions[txid]
-      assert (txdata.thread_id == thread.get_ident(),
-              "Transactions are single-threaded.")
+      assert (txdata.thread_id ==
+          thread.get_ident()), "Transactions are single-threaded."
       for key in delete_request.key_list():
         txdata.entities[key.Encode()] = (key, None)
     else:
@@ -335,8 +365,8 @@
           'Transaction %d not found.' % (txid,))
 
     txdata = self.__transactions[txid]
-    assert (txdata.thread_id == thread.get_ident(),
-            "Transactions are single-threaded.")
+    assert (txdata.thread_id ==
+        thread.get_ident()), "Transactions are single-threaded."
     del self.__transactions[txid]
 
     tx = remote_api_pb.TransactionRequest()
@@ -367,8 +397,8 @@
             datastore_pb.Error.BAD_REQUEST,
             'Transaction %d not found.' % (txid,))
 
-      assert (txdata[txid].thread_id == thread.get_ident(),
-              "Transactions are single-threaded.")
+      assert (txdata[txid].thread_id ==
+          thread.get_ident()), "Transactions are single-threaded."
       del self.__transactions[txid]
     finally:
       self.__local_tx_lock.release()
@@ -386,14 +416,14 @@
         'The remote datastore does not support index manipulation.')
 
 
-def ConfigureRemoteDatastore(app_id,
-                             path,
-                             auth_func,
-                             servername=None,
-                             rpc_server_factory=appengine_rpc.HttpRpcServer,
-                             rtok=None,
-                             secure=False):
-  """Does necessary setup to allow easy remote access to an AppEngine datastore.
+def ConfigureRemoteApi(app_id,
+                       path,
+                       auth_func,
+                       servername=None,
+                       rpc_server_factory=appengine_rpc.HttpRpcServer,
+                       rtok=None,
+                       secure=False):
+  """Does necessary setup to allow easy remote access to App Engine APIs.
 
   Either servername must be provided or app_id must not be None.  If app_id
   is None and a servername is provided, this function will send a request
@@ -438,10 +468,32 @@
     if not app_info or 'rtok' not in app_info or 'app_id' not in app_info:
       raise ConfigurationError('Error parsing app_id lookup response')
     if app_info['rtok'] != rtok:
-      raise ConfigurationError('Token validation failed during app_id lookup.')
+      raise ConfigurationError('Token validation failed during app_id lookup. '
+                               '(sent %s, got %s)' % (repr(rtok),
+                                                      repr(app_info['rtok'])))
     app_id = app_info['app_id']
 
   os.environ['APPLICATION_ID'] = app_id
   apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
-  stub = RemoteDatastoreStub(server, path)
-  apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)
+  datastore_stub = RemoteDatastoreStub(server, path)
+  apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', datastore_stub)
+  stub = RemoteStub(server, path)
+  for service in ['capability_service', 'images', 'mail', 'memcache',
+                  'urlfetch']:
+    apiproxy_stub_map.apiproxy.RegisterStub(service, stub)
+
+
+def MaybeInvokeAuthentication():
+  """Sends an empty request through to the configured end-point.
+
+  If authentication is necessary, this will cause the rpc_server to invoke
+  interactive authentication.
+  """
+  datastore_stub = apiproxy_stub_map.apiproxy.GetStub('datastore_v3')
+  if isinstance(datastore_stub, RemoteStub):
+    datastore_stub._server.Send(datastore_stub._path, payload=None)
+  else:
+    raise ConfigurationError('remote_api is not configured.')
+
+
+ConfigureRemoteDatastore = ConfigureRemoteApi