thirdparty/google_appengine/google/appengine/api/urlfetch.py
changeset 2413 d0b7dac5325c
parent 2309 be1b94099f2d
--- a/thirdparty/google_appengine/google/appengine/api/urlfetch.py	Mon Jun 08 22:34:05 2009 +0200
+++ b/thirdparty/google_appengine/google/appengine/api/urlfetch.py	Fri Jun 19 16:13:32 2009 +0200
@@ -30,7 +30,6 @@
 import urllib2
 import urlparse
 
-from google.appengine.api import apiproxy_rpc
 from google.appengine.api import apiproxy_stub_map
 from google.appengine.api import urlfetch_service_pb
 from google.appengine.api.urlfetch_errors import *
@@ -187,23 +186,23 @@
   return False
 
 
-def __create_rpc(deadline=None, callback=None):
-  """DO NOT USE.  WILL CHANGE AND BREAK YOUR CODE.
-
-  Creates an RPC object for use with the urlfetch API.
+def create_rpc(deadline=None, callback=None):
+  """Creates an RPC object for use with the urlfetch API.
 
   Args:
-    deadline: deadline in seconds for the operation.
-    callback: callable to invoke on completion.
+    deadline: Optional deadline in seconds for the operation; the default
+      is a system-specific deadline (typically 5 seconds).
+    callback: Optional callable to invoke on completion.
 
   Returns:
-    A _URLFetchRPC object.
+    An apiproxy_stub_map.UserRPC object specialized for this service.
   """
-  return _URLFetchRPC(deadline, callback)
+  return apiproxy_stub_map.UserRPC('urlfetch', deadline, callback)
 
 
-def fetch(url, payload=None, method=GET, headers={}, allow_truncated=False,
-          follow_redirects=True, deadline=None):
+def fetch(url, payload=None, method=GET, headers={},
+          allow_truncated=False, follow_redirects=True,
+          deadline=None):
   """Fetches the given HTTP URL, blocking until the result is returned.
 
   Other optional parameters are:
@@ -212,7 +211,7 @@
        this is ignored if the method is not POST or PUT.
      headers: dictionary of HTTP headers to send with the request
      allow_truncated: if true, truncate large responses and return them without
-       error. otherwise, ResponseTooLargeError will be thrown when a response is
+       error. Otherwise, ResponseTooLargeError is raised when a response is
        truncated.
      follow_redirects: if true (the default), redirects are
        transparently followed and the response (if less than 5
@@ -236,158 +235,108 @@
   of the returned structure, so HTTP errors like 404 do not result in an
   exception.
   """
-  rpc = __create_rpc(deadline=deadline)
-  rpc.make_call(url, payload, method, headers, follow_redirects)
-  return rpc.get_result(allow_truncated)
+  rpc = create_rpc(deadline=deadline)
+  make_fetch_call(rpc, url, payload, method, headers,
+                  allow_truncated, follow_redirects)
+  return rpc.get_result()
 
 
-class _URLFetchRPC(object):
-  """A RPC object that manages the urlfetch RPC.
-
-  Its primary functions are the following:
-  1. Convert error codes to the URLFetchServiceError namespace and raise them
-     when get_result is called.
-  2. Wrap the urlfetch response with a _URLFetchResult object.
-  """
-
-  def __init__(self, deadline=None, callback=None):
-    """Construct a new url fetch RPC.
+def make_fetch_call(rpc, url, payload=None, method=GET, headers={},
+                    allow_truncated=False, follow_redirects=True):
+  """Executes the RPC call to fetch a given HTTP URL.
 
-    Args:
-      deadline: deadline in seconds for the operation.
-      callback: callable to invoke on completion.
-    """
-    self.__rpc = apiproxy_stub_map.CreateRPC('urlfetch')
-    self.__rpc.deadline = deadline
-    self.__rpc.callback = callback
-    self.__called_hooks = False
+  The first argument is a UserRPC instance.  See urlfetch.fetch for a
+  thorough description of remaining arguments.
+  """
+  assert rpc.service == 'urlfetch', repr(rpc.service)
+  if isinstance(method, basestring):
+    method = method.upper()
+  method = _URL_STRING_MAP.get(method, method)
+  if method not in _VALID_METHODS:
+    raise InvalidMethodError('Invalid method %s.' % str(method))
 
-  def make_call(self, url, payload=None, method=GET, headers={},
-                follow_redirects=True):
-    """Executes the RPC call to fetch a given HTTP URL.
+  if _is_fetching_self(url, method):
+    raise InvalidURLError("App cannot fetch the same URL as the one used for "
+                          "the request.")
 
-    See urlfetch.fetch for a thorough description of arguments.
-    """
-    assert self.__rpc.state is apiproxy_rpc.RPC.IDLE
-    if isinstance(method, basestring):
-      method = method.upper()
-    method = _URL_STRING_MAP.get(method, method)
-    if method not in _VALID_METHODS:
-      raise InvalidMethodError('Invalid method %s.' % str(method))
+  request = urlfetch_service_pb.URLFetchRequest()
+  response = urlfetch_service_pb.URLFetchResponse()
+  request.set_url(url)
 
-    if _is_fetching_self(url, method):
-      raise InvalidURLError("App cannot fetch the same URL as the one used for "
-                            "the request.")
-
-    self.__request = urlfetch_service_pb.URLFetchRequest()
-    self.__response = urlfetch_service_pb.URLFetchResponse()
-    self.__result = None
-    self.__request.set_url(url)
+  if method == GET:
+    request.set_method(urlfetch_service_pb.URLFetchRequest.GET)
+  elif method == POST:
+    request.set_method(urlfetch_service_pb.URLFetchRequest.POST)
+  elif method == HEAD:
+    request.set_method(urlfetch_service_pb.URLFetchRequest.HEAD)
+  elif method == PUT:
+    request.set_method(urlfetch_service_pb.URLFetchRequest.PUT)
+  elif method == DELETE:
+    request.set_method(urlfetch_service_pb.URLFetchRequest.DELETE)
 
-    if method == GET:
-      self.__request.set_method(urlfetch_service_pb.URLFetchRequest.GET)
-    elif method == POST:
-      self.__request.set_method(urlfetch_service_pb.URLFetchRequest.POST)
-    elif method == HEAD:
-      self.__request.set_method(urlfetch_service_pb.URLFetchRequest.HEAD)
-    elif method == PUT:
-      self.__request.set_method(urlfetch_service_pb.URLFetchRequest.PUT)
-    elif method == DELETE:
-      self.__request.set_method(urlfetch_service_pb.URLFetchRequest.DELETE)
+  if payload and (method == POST or method == PUT):
+    request.set_payload(payload)
 
-    if payload and (method == POST or method == PUT):
-      self.__request.set_payload(payload)
-
-    for key, value in headers.iteritems():
-      header_proto = self.__request.add_header()
-      header_proto.set_key(key)
-      header_proto.set_value(str(value))
+  for key, value in headers.iteritems():
+    header_proto = request.add_header()
+    header_proto.set_key(key)
+    header_proto.set_value(str(value))
 
-    self.__request.set_followredirects(follow_redirects)
-    if self.__rpc.deadline:
-      self.__request.set_deadline(self.__rpc.deadline)
+  request.set_followredirects(follow_redirects)
 
-    apiproxy_stub_map.apiproxy.GetPreCallHooks().Call(
-        'urlfetch', 'Fetch', self.__request, self.__response)
-    self.__rpc.MakeCall('urlfetch', 'Fetch', self.__request, self.__response)
+  if rpc.deadline is not None:
+    request.set_deadline(rpc.deadline)
+
+  rpc.make_call('Fetch', request, response, _get_fetch_result, allow_truncated)
 
-  def wait(self):
-    """Waits for the urlfetch RPC to finish.  Idempotent.
-    """
-    assert self.__rpc.state is not apiproxy_rpc.RPC.IDLE
-    if self.__rpc.state is apiproxy_rpc.RPC.RUNNING:
-      self.__rpc.Wait()
 
-  def check_success(self, allow_truncated=False):
-    """Check success and convert RPC exceptions to urlfetch exceptions.
+def _get_fetch_result(rpc):
+  """Check success, handle exceptions, and return converted RPC result.
 
-    This method waits for the RPC if it has not yet finished, and calls the
-    post-call hooks on the first invocation.
+  This method waits for the RPC if it has not yet finished, and calls the
+  post-call hooks on the first invocation.
 
-    Args:
-      allow_truncated: if False, an error is raised if the response was
-        truncated.
+  Args:
+    rpc: A UserRPC object.
 
-    Raises:
-      InvalidURLError if the url was invalid.
-      DownloadError if there was a problem fetching the url.
-      ResponseTooLargeError if the response was either truncated (and
-        allow_truncated is false) or if it was too big for us to download.
-    """
-    assert self.__rpc.state is not apiproxy_rpc.RPC.IDLE
-    if self.__rpc.state is apiproxy_rpc.RPC.RUNNING:
-      self.wait()
+  Raises:
+    InvalidURLError if the url was invalid.
+    DownloadError if there was a problem fetching the url.
+    ResponseTooLargeError if the response was either truncated (and
+      allow_truncated=False was passed to make_fetch_call()), or if it
+      was too big for us to download.
 
-    try:
-      self.__rpc.CheckSuccess()
-      if not self.__called_hooks:
-        self.__called_hooks = True
-        apiproxy_stub_map.apiproxy.GetPostCallHooks().Call(
-            'urlfetch', 'Fetch', self.__request, self.__response)
-    except apiproxy_errors.ApplicationError, e:
-      if (e.application_error ==
-          urlfetch_service_pb.URLFetchServiceError.INVALID_URL):
-        raise InvalidURLError(str(e))
-      if (e.application_error ==
-          urlfetch_service_pb.URLFetchServiceError.UNSPECIFIED_ERROR):
-        raise DownloadError(str(e))
-      if (e.application_error ==
-          urlfetch_service_pb.URLFetchServiceError.FETCH_ERROR):
-        raise DownloadError(str(e))
-      if (e.application_error ==
-          urlfetch_service_pb.URLFetchServiceError.RESPONSE_TOO_LARGE):
-        raise ResponseTooLargeError(None)
-      if (e.application_error ==
-          urlfetch_service_pb.URLFetchServiceError.DEADLINE_EXCEEDED):
-        raise DownloadError(str(e))
-      raise e
+  Returns:
+    A _URLFetchResult object.
+  """
+  assert rpc.service == 'urlfetch', repr(rpc.service)
+  assert rpc.method == 'Fetch', repr(rpc.method)
+  try:
+    rpc.check_success()
+  except apiproxy_errors.ApplicationError, err:
+    if (err.application_error ==
+        urlfetch_service_pb.URLFetchServiceError.INVALID_URL):
+      raise InvalidURLError(str(err))
+    if (err.application_error ==
+        urlfetch_service_pb.URLFetchServiceError.UNSPECIFIED_ERROR):
+      raise DownloadError(str(err))
+    if (err.application_error ==
+        urlfetch_service_pb.URLFetchServiceError.FETCH_ERROR):
+      raise DownloadError(str(err))
+    if (err.application_error ==
+        urlfetch_service_pb.URLFetchServiceError.RESPONSE_TOO_LARGE):
+      raise ResponseTooLargeError(None)
+    if (err.application_error ==
+        urlfetch_service_pb.URLFetchServiceError.DEADLINE_EXCEEDED):
+      raise DownloadError(str(err))
+    raise err
 
-    if self.__response.contentwastruncated() and not allow_truncated:
-      raise ResponseTooLargeError(_URLFetchResult(self.__response))
-
-  def get_result(self, allow_truncated=False):
-    """Returns the RPC result or raises an exception if the rpc failed.
-
-    This method waits for the RPC if not completed, and checks success.
-
-    Args:
-      allow_truncated: if False, an error is raised if the response was
-        truncated.
-
-    Returns:
-      The urlfetch result.
-
-    Raises:
-      Error if the rpc has not yet finished.
-      InvalidURLError if the url was invalid.
-      DownloadError if there was a problem fetching the url.
-      ResponseTooLargeError if the response was either truncated (and
-        allow_truncated is false) or if it was too big for us to download.
-    """
-    if self.__result is None:
-      self.check_success(allow_truncated)
-      self.__result = _URLFetchResult(self.__response)
-    return self.__result
+  response = rpc.response
+  allow_truncated = rpc.user_data
+  result = _URLFetchResult(response)
+  if response.contentwastruncated() and not allow_truncated:
+    raise ResponseTooLargeError(result)
+  return result
 
 
 Fetch = fetch