thirdparty/google_appengine/google/appengine/api/apiproxy_stub_map.py
changeset 828 f5fd65cc3bf3
parent 109 620f9b141567
child 2413 d0b7dac5325c
--- a/thirdparty/google_appengine/google/appengine/api/apiproxy_stub_map.py	Tue Jan 20 01:12:43 2009 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/apiproxy_stub_map.py	Tue Jan 20 13:19:45 2009 +0000
@@ -27,10 +27,34 @@
 
 
 
+import inspect
 import sys
 
+def CreateRPC(service):
+  """Creates a RPC instance for the given service.
+
+  The instance is suitable for talking to remote services.
+  Each RPC instance can be used only once, and should not be reused.
+
+  Args:
+    service: string representing which service to call.
+
+  Returns:
+    the rpc object.
+
+  Raises:
+    AssertionError or RuntimeError if the stub for service doesn't supply a
+    CreateRPC method.
+  """
+  stub = apiproxy.GetStub(service)
+  assert stub, 'No api proxy found for service "%s"' % service
+  assert hasattr(stub, 'CreateRPC'), ('The service "%s" doesn\'t have ' +
+                                      'a CreateRPC method.' % service)
+  return stub.CreateRPC()
+
+
 def MakeSyncCall(service, call, request, response):
-  """The APIProxy entry point.
+  """The APIProxy entry point for a synchronous API call.
 
   Args:
     service: string representing which service to call
@@ -41,9 +65,99 @@
   Raises:
     apiproxy_errors.Error or a subclass.
   """
-  stub = apiproxy.GetStub(service)
-  assert stub, 'No api proxy found for service "%s"' % service
-  stub.MakeSyncCall(service, call, request, response)
+  apiproxy.MakeSyncCall(service, call, request, response)
+
+
+class ListOfHooks(object):
+  """An ordered collection of hooks for a particular API call.
+
+  A hook is a function that has exactly the same signature as
+  a service stub. It will be called before or after an api hook is
+  executed, depending on whether this list is for precall of postcall hooks.
+  Hooks can be used for debugging purposes (check certain
+  pre- or postconditions on api calls) or to apply patches to protocol
+  buffers before/after a call gets submitted.
+  """
+
+  def __init__(self):
+    """Constructor."""
+
+    self.__content = []
+
+    self.__unique_keys = set()
+
+  def __len__(self):
+    """Returns the amount of elements in the collection."""
+    return self.__content.__len__()
+
+  def __Insert(self, index, key, function, service=None):
+    """Appends a hook at a certain position in the list.
+
+    Args:
+      index: the index of where to insert the function
+      key: a unique key (within the module) for this particular function.
+        If something from the same module with the same key is already
+        registered, nothing will be added.
+      function: the hook to be added.
+      service: optional argument that restricts the hook to a particular api
+
+    Returns:
+      True if the collection was modified.
+    """
+    unique_key = (key, inspect.getmodule(function))
+    if unique_key in self.__unique_keys:
+      return False
+    self.__content.insert(index, (key, function, service))
+    self.__unique_keys.add(unique_key)
+    return True
+
+  def Append(self, key, function, service=None):
+    """Appends a hook at the end of the list.
+
+    Args:
+      key: a unique key (within the module) for this particular function.
+        If something from the same module with the same key is already
+        registered, nothing will be added.
+      function: the hook to be added.
+      service: optional argument that restricts the hook to a particular api
+
+    Returns:
+      True if the collection was modified.
+    """
+    return self.__Insert(len(self), key, function, service)
+
+  def Push(self, key, function, service=None):
+    """Inserts a hook at the beginning of the list.
+
+    Args:
+      key: a unique key (within the module) for this particular function.
+        If something from the same module with the same key is already
+        registered, nothing will be added.
+      function: the hook to be added.
+      service: optional argument that restricts the hook to a particular api
+
+    Returns:
+      True if the collection was modified.
+    """
+    return self.__Insert(0, key, function, service)
+
+  def Clear(self):
+    """Removes all hooks from the list (useful for unit tests)."""
+    self.__content = []
+    self.__unique_keys = set()
+
+  def Call(self, service, call, request, response):
+    """Invokes all hooks in this collection.
+
+    Args:
+      service: string representing which service to call
+      call: string representing which function to call
+      request: protocol buffer for the request
+      response: protocol buffer for the response
+    """
+    for key, function, srv in self.__content:
+      if srv is None or srv == service:
+        function(service, call, request, response)
 
 
 class APIProxyStubMap:
@@ -70,6 +184,16 @@
     """
     self.__stub_map = {}
     self.__default_stub = default_stub
+    self.__precall_hooks = ListOfHooks()
+    self.__postcall_hooks = ListOfHooks()
+
+  def GetPreCallHooks(self):
+    """Gets a collection for all precall hooks."""
+    return self.__precall_hooks
+
+  def GetPostCallHooks(self):
+    """Gets a collection for all precall hooks."""
+    return self.__postcall_hooks
 
   def RegisterStub(self, service, stub):
     """Register the provided stub for the specified service.
@@ -98,6 +222,25 @@
     """
     return self.__stub_map.get(service, self.__default_stub)
 
+  def MakeSyncCall(self, service, call, request, response):
+    """The APIProxy entry point.
+
+    Args:
+      service: string representing which service to call
+      call: string representing which function to call
+      request: protocol buffer for the request
+      response: protocol buffer for the response
+
+    Raises:
+      apiproxy_errors.Error or a subclass.
+    """
+    stub = self.GetStub(service)
+    assert stub, 'No api proxy found for service "%s"' % service
+    self.__precall_hooks.Call(service, call, request, response)
+    stub.MakeSyncCall(service, call, request, response)
+    self.__postcall_hooks.Call(service, call, request, response)
+
+
 def GetDefaultAPIProxy():
   try:
     runtime = __import__('google.appengine.runtime', globals(), locals(),