Load ../google_appengine into trunk/thirdparty/google_appengine.
authorTodd Larsen <tlarsen@google.com>
Tue, 16 Sep 2008 02:28:33 +0000
changeset 149 f2e327a7c5de
parent 148 37505d64e57b
child 150 715b07485c48
Load ../google_appengine into trunk/thirdparty/google_appengine.
thirdparty/google_appengine/README
thirdparty/google_appengine/RELEASE_NOTES
thirdparty/google_appengine/VERSION
thirdparty/google_appengine/appcfg.py
thirdparty/google_appengine/dev_appserver.py
thirdparty/google_appengine/google/appengine/api/api_base_pb.py
thirdparty/google_appengine/google/appengine/api/appinfo.py
thirdparty/google_appengine/google/appengine/api/datastore.py
thirdparty/google_appengine/google/appengine/api/datastore_admin.py
thirdparty/google_appengine/google/appengine/api/datastore_file_stub.py
thirdparty/google_appengine/google/appengine/api/datastore_types.py
thirdparty/google_appengine/google/appengine/api/images/images_service_pb.py
thirdparty/google_appengine/google/appengine/api/mail.py
thirdparty/google_appengine/google/appengine/api/mail_service_pb.py
thirdparty/google_appengine/google/appengine/api/memcache/__init__.py
thirdparty/google_appengine/google/appengine/api/memcache/memcache_service_pb.py
thirdparty/google_appengine/google/appengine/api/memcache/memcache_stub.py
thirdparty/google_appengine/google/appengine/api/urlfetch.py
thirdparty/google_appengine/google/appengine/api/urlfetch_service_pb.py
thirdparty/google_appengine/google/appengine/api/urlfetch_stub.py
thirdparty/google_appengine/google/appengine/api/user_service_pb.py
thirdparty/google_appengine/google/appengine/api/validation.py
thirdparty/google_appengine/google/appengine/api/yaml_errors.py
thirdparty/google_appengine/google/appengine/datastore/datastore_pb.py
thirdparty/google_appengine/google/appengine/datastore/entity_pb.py
thirdparty/google_appengine/google/appengine/ext/admin/__init__.py
thirdparty/google_appengine/google/appengine/ext/admin/templates/base.html
thirdparty/google_appengine/google/appengine/ext/admin/templates/css/ae.css
thirdparty/google_appengine/google/appengine/ext/admin/templates/css/memcache.css
thirdparty/google_appengine/google/appengine/ext/admin/templates/datastore.html
thirdparty/google_appengine/google/appengine/ext/admin/templates/interactive-output.html
thirdparty/google_appengine/google/appengine/ext/admin/templates/interactive.html
thirdparty/google_appengine/google/appengine/ext/admin/templates/memcache.html
thirdparty/google_appengine/google/appengine/ext/bulkload/__init__.py
thirdparty/google_appengine/google/appengine/ext/db/__init__.py
thirdparty/google_appengine/google/appengine/ext/gql/__init__.py
thirdparty/google_appengine/google/appengine/ext/search/__init__.py
thirdparty/google_appengine/google/appengine/ext/webapp/__init__.py
thirdparty/google_appengine/google/appengine/ext/zipserve/__init__.py
thirdparty/google_appengine/google/appengine/runtime/__init__.py
thirdparty/google_appengine/google/appengine/tools/appcfg.py
thirdparty/google_appengine/google/appengine/tools/dev_appserver.py
thirdparty/google_appengine/google/appengine/tools/dev_appserver_main.py
thirdparty/google_appengine/google/appengine/tools/os_compat.py
--- a/thirdparty/google_appengine/README	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/README	Tue Sep 16 02:28:33 2008 +0000
@@ -71,11 +71,15 @@
                              (Default '')
   --enable_sendmail          Enable sendmail when SMTP not configured.
                              (Default false)
+  --show_mail_body           Log the body of emails in mail stub.
+                             (Default false)
   --auth_domain              Authorization domain that this app runs in.
                              (Default gmail.com)
   --debug_imports            Enables debug logging for module imports, showing
                              search paths used for finding modules and any
                              errors encountered during the import process.
+  --disable_static_caching   Never allow the browser to cache static files.
+                             (Default enable if expiration set in app.yaml)
 
 
 
--- a/thirdparty/google_appengine/RELEASE_NOTES	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/RELEASE_NOTES	Tue Sep 16 02:28:33 2008 +0000
@@ -3,6 +3,22 @@
 
 App Engine SDK - Release Notes
 
+Version 1.1.3 - September 8, 2008
+=================================
+
+  - Added support for zipimport.
+      http://code.google.com/p/googleappengine/issues/detail?id=70
+      http://code.google.com/p/googleappengine/issues/detail?id=161
+  - Added zipserve module for serving static content from a zip file.
+      See google/appengine/ext/zipserve/__init__.py for more information.
+  - Added a memcache viewer to the development console.
+      http://code.google.com/appengine/docs/thedevwebserver.html#The_Development_Console
+  - Added new follow_redirects flag to the URLFetch service.
+      http://code.google.com/p/googleappengine/issues/detail?id=404
+  - Fixed caching headers for static content.
+  - Fixed an issue with incorrectly escaping paths on Windows.
+  - Fixed an issue with the current directory while running applications.
+
 Version 1.1.2 - August 20, 2008
 ===============================
 
--- a/thirdparty/google_appengine/VERSION	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/VERSION	Tue Sep 16 02:28:33 2008 +0000
@@ -1,3 +1,3 @@
-release: "1.1.2"
-timestamp: 1219264385
+release: "1.1.3"
+timestamp: 1220920355
 api_versions: ['1']
--- a/thirdparty/google_appengine/appcfg.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/appcfg.py	Tue Sep 16 02:28:33 2008 +0000
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-"""Convenience wrapper for starting appcfg."""
+"""Convenience wrapper for starting an appengine tool."""
 
 
 import os
@@ -33,16 +33,23 @@
   sys.stderr.write('Warning: Python 2.4 is not supported; this program may '
                    'break. Please use version 2.5 or greater.\n')
 
-APPCFG_PATH = 'google/appengine/tools/appcfg.py'
-
 DIR_PATH = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+SCRIPT_DIR = os.path.join(DIR_PATH, 'google', 'appengine', 'tools')
 
 EXTRA_PATHS = [
   DIR_PATH,
+  os.path.join(DIR_PATH, 'lib', 'django'),
+  os.path.join(DIR_PATH, 'lib', 'webob'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
 ]
 
+SCRIPT_EXCEPTIONS = {
+  "dev_appserver.py" : "dev_appserver_main.py"
+}
+
 if __name__ == '__main__':
   sys.path = EXTRA_PATHS + sys.path
-  script_path = os.path.join(DIR_PATH, APPCFG_PATH)
+  script_name = os.path.basename(__file__)
+  script_name = SCRIPT_EXCEPTIONS.get(script_name, script_name)
+  script_path = os.path.join(SCRIPT_DIR, script_name)
   execfile(script_path, globals())
--- a/thirdparty/google_appengine/dev_appserver.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/dev_appserver.py	Tue Sep 16 02:28:33 2008 +0000
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-"""Convenience wrapper for starting dev_appserver."""
+"""Convenience wrapper for starting an appengine tool."""
 
 
 import os
@@ -33,9 +33,8 @@
   sys.stderr.write('Warning: Python 2.4 is not supported; this program may '
                    'break. Please use version 2.5 or greater.\n')
 
-DEV_APPSERVER_PATH = 'google/appengine/tools/dev_appserver_main.py'
-
 DIR_PATH = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+SCRIPT_DIR = os.path.join(DIR_PATH, 'google', 'appengine', 'tools')
 
 EXTRA_PATHS = [
   DIR_PATH,
@@ -44,7 +43,13 @@
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
 ]
 
+SCRIPT_EXCEPTIONS = {
+  "dev_appserver.py" : "dev_appserver_main.py"
+}
+
 if __name__ == '__main__':
   sys.path = EXTRA_PATHS + sys.path
-  script_path = os.path.join(DIR_PATH, DEV_APPSERVER_PATH)
+  script_name = os.path.basename(__file__)
+  script_name = SCRIPT_EXCEPTIONS.get(script_name, script_name)
+  script_path = os.path.join(SCRIPT_DIR, script_name)
   execfile(script_path, globals())
--- a/thirdparty/google_appengine/google/appengine/api/api_base_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/api_base_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -23,9 +23,10 @@
                    unusednames=printElemNumber,debug_strs no-special"""
 
 class StringProto(ProtocolBuffer.ProtocolMessage):
+  has_value_ = 0
+  value_ = ""
+
   def __init__(self, contents=None):
-    self.value_ = ""
-    self.has_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def value(self): return self.value_
@@ -108,9 +109,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class Integer32Proto(ProtocolBuffer.ProtocolMessage):
+  has_value_ = 0
+  value_ = 0
+
   def __init__(self, contents=None):
-    self.value_ = 0
-    self.has_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def value(self): return self.value_
@@ -193,9 +195,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class Integer64Proto(ProtocolBuffer.ProtocolMessage):
+  has_value_ = 0
+  value_ = 0
+
   def __init__(self, contents=None):
-    self.value_ = 0
-    self.has_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def value(self): return self.value_
@@ -278,9 +281,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class BoolProto(ProtocolBuffer.ProtocolMessage):
+  has_value_ = 0
+  value_ = 0
+
   def __init__(self, contents=None):
-    self.value_ = 0
-    self.has_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def value(self): return self.value_
@@ -362,9 +366,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class DoubleProto(ProtocolBuffer.ProtocolMessage):
+  has_value_ = 0
+  value_ = 0.0
+
   def __init__(self, contents=None):
-    self.value_ = 0.0
-    self.has_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def value(self): return self.value_
@@ -446,6 +451,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class VoidProto(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
--- a/thirdparty/google_appengine/google/appengine/api/appinfo.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/appinfo.py	Tue Sep 16 02:28:33 2008 +0000
@@ -40,6 +40,13 @@
 _DELTA_REGEX = r'([1-9][0-9]*)([DdHhMm]|[sS]?)'
 _EXPIRATION_REGEX = r'\s*(%s)(\s+%s)*\s*' % (_DELTA_REGEX, _DELTA_REGEX)
 
+_EXPIRATION_CONVERSIONS = {
+  'd': 60 * 60 * 24,
+  'h': 60 * 60,
+  'm': 60,
+  's': 1,
+}
+
 APP_ID_MAX_LEN = 100
 MAJOR_VERSION_ID_MAX_LEN = 100
 MAX_URL_MAPS = 100
@@ -340,6 +347,24 @@
   return app_infos[0]
 
 
+def ParseExpiration(expiration):
+  """Parses an expiration delta string.
+
+  Args:
+    expiration: String that matches _DELTA_REGEX.
+
+  Returns:
+    Time delta in seconds.
+  """
+  delta = 0
+  for match in re.finditer(_DELTA_REGEX, expiration):
+    amount = int(match.group(1))
+    units = _EXPIRATION_CONVERSIONS.get(match.group(2).lower(), 1)
+    delta += amount * units
+  return delta
+
+
+
 _file_path_positive_re = re.compile(r'^[ 0-9a-zA-Z\._\+/\$-]{1,256}$')
 
 _file_path_negative_1_re = re.compile(r'\.\.|^\./|\.$|/\./|^-')
--- a/thirdparty/google_appengine/google/appengine/api/datastore.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/datastore.py	Tue Sep 16 02:28:33 2008 +0000
@@ -47,8 +47,6 @@
 from google.appengine.runtime import apiproxy_errors
 from google.appengine.datastore import entity_pb
 
-_LOCAL_APP_ID = datastore_types._LOCAL_APP_ID
-
 TRANSACTION_RETRIES = 3
 
 _MAX_INDEXED_PROPERTIES = 5000
@@ -141,6 +139,8 @@
   """
   entities, multiple = NormalizeAndTypeCheck(entities, Entity)
 
+  if multiple and not entities:
+    return []
 
   for entity in entities:
     if not entity.kind() or not entity.app():
@@ -204,6 +204,8 @@
   """
   keys, multiple = NormalizeAndTypeCheckKeys(keys)
 
+  if multiple and not keys:
+    return []
   req = datastore_pb.GetRequest()
   req.key_list().extend([key._Key__reference for key in keys])
   _MaybeSetupTransaction(req, keys)
@@ -243,8 +245,10 @@
   Raises:
     TransactionFailedError, if the Put could not be committed.
   """
-  keys, _ = NormalizeAndTypeCheckKeys(keys)
+  keys, multiple = NormalizeAndTypeCheckKeys(keys)
 
+  if multiple and not keys:
+    return
 
   req = datastore_pb.DeleteRequest()
   req.key_list().extend([key._Key__reference for key in keys])
@@ -281,21 +285,18 @@
       name: string
     """
     ref = entity_pb.Reference()
-    if _app is not None:
-      datastore_types.ValidateString(_app, '_app',
-                                     datastore_errors.BadArgumentError)
-      ref.set_app(_app)
-    else:
-      ref.set_app(_LOCAL_APP_ID)
+    _app = datastore_types.ResolveAppId(_app)
+    ref.set_app(_app)
 
     datastore_types.ValidateString(kind, 'kind',
                                    datastore_errors.BadArgumentError)
 
     if parent is not None:
-      if _app is not None and _app != parent.app():
+      parent = _GetCompleteKeyOrError(parent)
+      if _app != parent.app():
         raise datastore_errors.BadArgumentError(
             "_app %s doesn't match parent's app %s" % (_app, parent.app()))
-      ref.CopyFrom(_GetCompleteKeyOrError(parent)._Key__reference)
+      ref.CopyFrom(parent._Key__reference)
 
     last_path = ref.mutable_path().add_element()
     last_path.set_type(kind.encode('utf-8'))
@@ -345,7 +346,7 @@
     BadPropertyError. If the value is not a supported type, raises
     BadValueError.
     """
-    datastore_types.ToPropertyPb(name, value)
+    datastore_types.ValidateProperty(name, value)
     dict.__setitem__(self, name, value)
 
   def setdefault(self, name, value):
@@ -355,7 +356,7 @@
     BadPropertyError. If the value is not a supported type, raises
     BadValueError.
     """
-    datastore_types.ToPropertyPb(name, value)
+    datastore_types.ValidateProperty(name, value)
     return dict.setdefault(self, name, value)
 
   def update(self, other):
@@ -524,35 +525,48 @@
     else:
       assert last_path.has_name()
       assert last_path.name()
+
     e = Entity(unicode(last_path.type().decode('utf-8')))
-
     ref = e.__key._Key__reference
     ref.CopyFrom(pb.key())
 
-    for prop_list in [pb.property_list(), pb.raw_property_list()]:
+    temporary_values = {}
+
+    for prop_list in (pb.property_list(), pb.raw_property_list()):
       for prop in prop_list:
-        value = datastore_types.FromPropertyPb(prop)
-
         if not prop.has_multiple():
           raise datastore_errors.Error(
-            "Property %s is corrupt in the datastore; it's missing the "
-            'multiply valued field.' % name)
+            'Property %s is corrupt in the datastore; it\'s missing the '
+            'multiple valued field.' % prop.name())
 
-        if prop.multiple():
+        try:
+          value = datastore_types.FromPropertyPb(prop)
+        except (AssertionError, AttributeError, TypeError, ValueError), e:
+          raise datastore_errors.Error(
+            'Property %s is corrupt in the datastore. %s: %s' %
+            (e.__class__, prop.name(), e))
+
+        multiple = prop.multiple()
+        if multiple:
           value = [value]
-        name = unicode(prop.name().decode('utf-8'))
 
-        if not e.has_key(name):
-          e[name] = value
+        name = prop.name()
+        cur_value = temporary_values.get(name)
+        if cur_value is None:
+          temporary_values[name] = value
+        elif not multiple:
+          raise datastore_errors.Error(
+            'Property %s is corrupt in the datastore; it has multiple '
+            'values, but is not marked as multiply valued.' % name)
         else:
-          if not prop.multiple():
-            raise datastore_errors.Error(
-              'Property %s is corrupt in the datastore; it has multiple '
-              'values, but is not marked as multiply valued.' % name)
+          cur_value.extend(value)
 
-          cur_value = e[name]
-          assert isinstance(cur_value, list)
-          cur_value += value
+    for name, value in temporary_values.iteritems():
+      decoded_name = unicode(name.decode('utf-8'))
+
+      datastore_types.ValidateReadProperty(decoded_name, value)
+
+      dict.__setitem__(e, decoded_name, value)
 
     return e
 
@@ -654,7 +668,7 @@
     re.IGNORECASE | re.UNICODE)
 
   __kind = None
-  __app = _LOCAL_APP_ID
+  __app = None
   __orderings = None
   __cached_count = None
   __hint = None
@@ -686,10 +700,7 @@
     self.__filter_order = {}
     self.update(filters)
 
-    if _app is not None:
-      datastore_types.ValidateString(_app, '_app',
-                                     datastore_errors.BadArgumentError)
-      self.__app = _app
+    self.__app = datastore_types.ResolveAppId(_app)
 
   def Order(self, *orderings):
     """Specify how the query results should be sorted.
@@ -931,9 +942,6 @@
     count is cached; successive Count() calls will not re-scan the datastore
     unless the query is changed.
 
-    Raises BadQueryError if the Query has more than one filter. Multiple
-    filters aren't supported yet.
-
     Args:
       limit, a number. If there are more results than this, stop short and
       just return this number. Providing this argument makes the count
@@ -968,7 +976,7 @@
     if isinstance(value, tuple):
       value = list(value)
 
-    datastore_types.ToPropertyPb(' ', value)
+    datastore_types.ValidateProperty(' ', value)
     match = self._CheckFilter(filter, value)
     property = match.group(1)
     operator = match.group(3)
@@ -995,7 +1003,7 @@
     BadPropertyError. If the value is not a supported type, raises
     BadValueError.
     """
-    datastore_types.ToPropertyPb(' ', value)
+    datastore_types.ValidateProperty(' ', value)
     self._CheckFilter(filter, value)
     self.__cached_count = None
     return dict.setdefault(self, filter, value)
--- a/thirdparty/google_appengine/google/appengine/api/datastore_admin.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/datastore_admin.py	Tue Sep 16 02:28:33 2008 +0000
@@ -30,8 +30,6 @@
 from google.appengine.runtime import apiproxy_errors
 from google.appengine.datastore import entity_pb
 
-_LOCAL_APP_ID = datastore_types._LOCAL_APP_ID
-
 
 _DIRECTION_MAP = {
     'asc':        entity_pb.Index_Property.ASCENDING,
@@ -41,7 +39,7 @@
     }
 
 
-def GetSchema(_app=_LOCAL_APP_ID):
+def GetSchema(_app=None):
   """Infers an app's schema from the entities in the datastore.
 
   Note that the PropertyValue PBs in the returned EntityProtos are empty
@@ -54,21 +52,21 @@
     list of entity_pb.EntityProto, with kind and property names and types
   """
   req = api_base_pb.StringProto()
-  req.set_value(_app)
+  req.set_value(datastore_types.ResolveAppId(_app))
   resp = datastore_pb.Schema()
 
   _Call('GetSchema', req, resp)
   return resp.kind_list()
 
 
-def GetIndices(_app=_LOCAL_APP_ID):
+def GetIndices(_app=None):
   """Fetches all composite indices in the datastore for this app.
 
   Returns:
     list of entity_pb.CompositeIndex
   """
   req = api_base_pb.StringProto()
-  req.set_value(_app)
+  req.set_value(datastore_types.ResolveAppId(_app))
   resp = datastore_pb.CompositeIndices()
   try:
     apiproxy_stub_map.MakeSyncCall('datastore_v3', 'GetIndices', req, resp)
@@ -119,8 +117,8 @@
       local app.
     resp: the response PB
   """
-  if hasattr(req, 'app_id') and not req.app_id():
-    req.set_app_id(_LOCAL_APP_ID)
+  if hasattr(req, 'app_id'):
+    req.set_app_id(datastore_types.ResolveAppId(req.app_id(), 'req.app_id()'))
 
   try:
     apiproxy_stub_map.MakeSyncCall('datastore_v3', call, req, resp)
--- a/thirdparty/google_appengine/google/appengine/api/datastore_file_stub.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/datastore_file_stub.py	Tue Sep 16 02:28:33 2008 +0000
@@ -43,7 +43,6 @@
 import sys
 import tempfile
 import threading
-import types
 import warnings
 
 from google.appengine.api import api_base_pb
@@ -89,7 +88,7 @@
           exist in index.yaml for queries that need them.
     """
 
-    assert isinstance(app_id, types.StringTypes) and app_id != ''
+    assert isinstance(app_id, basestring) and app_id != ''
     self.__app_id = app_id
     self.__datastore_file = datastore_file
     self.__history_file = history_file
@@ -265,16 +264,6 @@
 
     assert response.IsInitialized(explanation), explanation
 
-  def ResolveAppId(self, app):
-    """ If the given app name is the placeholder for the local app, returns
-    our app_id. Otherwise returns the app name unchanged.
-    """
-    assert app != ''
-    if app == datastore._LOCAL_APP_ID:
-      return self.__app_id
-    else:
-      return app
-
   def QueryHistory(self):
     """Returns a dict that maps Query PBs to times they've been run.
     """
@@ -291,9 +280,7 @@
       assert clone.has_key()
       assert clone.key().path().element_size() > 0
 
-      app = self.ResolveAppId(clone.key().app())
-      clone.mutable_key().set_app(app)
-
+      app = clone.key().app()
       last_path = clone.key().path().element_list()[-1]
       if last_path.id() == 0 and not last_path.has_name():
         self.__id_lock.acquire()
@@ -328,8 +315,7 @@
 
   def _Dynamic_Get(self, get_request, get_response):
     for key in get_request.key_list():
-        app = self.ResolveAppId(key.app())
-        key.set_app(app)
+        app = key.app()
         last_path = key.path().element_list()[-1]
 
         group = get_response.add_entity()
@@ -347,8 +333,7 @@
     try:
       for key in delete_request.key_list():
         try:
-          app = self.ResolveAppId(key.app())
-          key.set_app(app)
+          app = key.app()
           kind = key.path().element_list()[-1].type()
           del self.__entities[app, kind][key]
           if not self.__entities[app, kind]:
@@ -369,7 +354,7 @@
     else:
       self.__tx_lock.release()
 
-    app = self.ResolveAppId(query.app())
+    app = query.app()
 
     if self.__require_indexes:
       required_index = datastore_index.CompositeIndexForQuery(query)
@@ -432,7 +417,7 @@
       def passes(entity):
         """ Returns True if the entity passes the filter, False otherwise. """
         entity_vals = entity.get(prop, [])
-        if type(entity_vals) is not types.ListType:
+        if not isinstance(entity_vals, list):
           entity_vals = [entity_vals]
 
         entity_property_list = [datastore_types.ToPropertyPb(prop, value)
@@ -441,7 +426,7 @@
         for entity_prop in entity_property_list:
           fixed_entity_val = datastore_types.FromPropertyPb(entity_prop)
 
-          if isinstance(fixed_entity_val, datastore_types._RAW_PROPERTY_TYPES):
+          if type(fixed_entity_val) in datastore_types._RAW_PROPERTY_TYPES:
             continue
 
           for filter_prop in filt.property_list():
@@ -470,7 +455,7 @@
         values = [values]
 
       for value in values:
-        if not isinstance(value, datastore_types._RAW_PROPERTY_TYPES):
+        if type(value) not in datastore_types._RAW_PROPERTY_TYPES:
           return True
       return False
 
@@ -610,7 +595,7 @@
   def _Dynamic_GetSchema(self, app_str, schema):
     minint = -sys.maxint - 1
 
-    app_str = self.ResolveAppId(app_str.value())
+    app_str = app_str.value()
 
     kinds = []
 
@@ -674,7 +659,7 @@
 
     clone = entity_pb.CompositeIndex()
     clone.CopyFrom(index)
-    app = self.ResolveAppId(index.app_id())
+    app = index.app_id()
     clone.set_app_id(app)
 
     self.__indexes_lock.acquire()
@@ -687,7 +672,7 @@
 
   def _Dynamic_GetIndices(self, app_str, composite_indices):
     composite_indices.index_list().extend(
-      self.__indexes.get(self.ResolveAppId(app_str.value()), []))
+      self.__indexes.get(app_str.value(), []))
 
   def _Dynamic_UpdateIndex(self, index, void):
     stored_index = self.__FindIndex(index)
@@ -713,7 +698,7 @@
       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
                                              "Index doesn't exist.")
 
-    app = self.ResolveAppId(index.app_id())
+    app = index.app_id()
     self.__indexes_lock.acquire()
     try:
       self.__indexes[app].remove(stored_index)
@@ -729,8 +714,7 @@
     Returns:
       entity_pb.CompositeIndex, if it exists; otherwise None
     """
-    app = self.ResolveAppId(index.app_id())
-
+    app = index.app_id()
     if app in self.__indexes:
       for stored_index in self.__indexes[app]:
         if index.definition() == stored_index.definition():
--- a/thirdparty/google_appengine/google/appengine/api/datastore_types.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/datastore_types.py	Tue Sep 16 02:28:33 2008 +0000
@@ -50,13 +50,11 @@
 from google.net.proto import ProtocolBuffer
 from google.appengine.datastore import entity_pb
 
-_LOCAL_APP_ID = u':self'
-
 _MAX_STRING_LENGTH = 500
 
 _MAX_LINK_PROPERTY_LENGTH = 2083
 
-RESERVED_PROPERTY_NAME = re.compile('^__.*__$');
+RESERVED_PROPERTY_NAME = re.compile('^__.*__$')
 
 class UtcTzinfo(datetime.tzinfo):
   def utcoffset(self, dt): return datetime.timedelta(0)
@@ -76,7 +74,8 @@
     return type(obj).__name__
 
 
-def ValidateString(value, name='Value',
+def ValidateString(value,
+                   name='unused',
                    exception=datastore_errors.BadValueError,
                    max_len=_MAX_STRING_LENGTH):
   """Raises an exception if value is not a valid string or a subclass thereof.
@@ -94,12 +93,34 @@
   if not isinstance(value, basestring) or isinstance(value, Blob):
     raise exception('%s should be a string; received %s (a %s):' %
                     (name, value, typename(value)))
-  elif value == '':
+  if not value:
     raise exception('%s must not be empty.' % name)
-  elif len(value.encode('utf-8')) > max_len:
+
+  if len(value.encode('utf-8')) > max_len:
     raise exception('%s must be under %d bytes.' % (name, max_len))
 
 
+def ResolveAppId(app, name='_app'):
+  """Validate app id, providing a default.
+
+  If the argument is None, $APPLICATION_ID is substituted.
+
+  Args:
+    app: The app id argument value to be validated.
+    name: The argument name, for error messages.
+
+  Returns:
+    The value of app, or the substituted default.  Always a non-empty string.
+
+  Raises:
+    BadArgumentError if the value is empty or not a string.
+  """
+  if app is None:
+    app = os.environ.get('APPLICATION_ID', '')
+  ValidateString(app, '_app', datastore_errors.BadArgumentError)
+  return app
+
+
 class Key(object):
   """The primary key for a datastore entity.
 
@@ -178,7 +199,7 @@
       BadKeyError if the parent key is incomplete.
     """
     parent = kwds.pop('parent', None)
-    _app = kwds.pop('_app', None)
+    _app = ResolveAppId(kwds.pop('_app', None))
 
     if kwds:
       raise datastore_errors.BadArgumentError(
@@ -189,12 +210,6 @@
           'A non-zero even number of positional arguments is required '
           '(kind, id or name, kind, id or name, ...); received %s' % repr(args))
 
-    if _app is not None:
-      if not isinstance(_app, basestring):
-        raise datastore_errors.BadArgumentError(
-          'Expected a string _app; received %r (a %s).' %
-          (_app, typename(_app)))
-
     if parent is not None:
       if not isinstance(parent, Key):
         raise datastore_errors.BadArgumentError(
@@ -203,7 +218,7 @@
       if not parent.has_id_or_name():
         raise datastore_errors.BadKeyError(
             'The parent Key is incomplete.')
-      if _app is not None and _app != parent.app():
+      if _app != parent.app():
         raise datastore_errors.BadArgumentError(
             'The _app argument (%r) should match parent.app() (%s)' %
             (_app, parent.app()))
@@ -212,10 +227,8 @@
     ref = key.__reference
     if parent is not None:
       ref.CopyFrom(parent.__reference)
-    elif _app is not None:
+    else:
       ref.set_app(_app)
-    else:
-      ref.set_app(_LOCAL_APP_ID)
 
     path = ref.mutable_path()
     for i in xrange(0, len(args), 2):
@@ -443,12 +456,8 @@
     self_args = []
     other_args = []
 
-    if (self.app() in (_LOCAL_APP_ID, None) or
-        other.app() in (_LOCAL_APP_ID, None)):
-      pass
-    else:
-      self_args.append(self.__reference.app().decode('utf-8'))
-      other_args.append(other.__reference.app().decode('utf-8'))
+    self_args.append(self.__reference.app().decode('utf-8'))
+    other_args.append(other.__reference.app().decode('utf-8'))
 
     for elem in self.__reference.path().element_list():
       self_args.append(repr(elem.type()))
@@ -632,6 +641,7 @@
     return u'<georss:point>%s %s</georss:point>' % (unicode(self.lat),
                                                     unicode(self.lon))
 
+
 class IM(object):
   """An instant messaging handle. Includes both an address and its protocol.
   The protocol value is either a standard IM scheme or a URL identifying the
@@ -713,6 +723,7 @@
   def __len__(self):
     return len(unicode(self))
 
+
 class PhoneNumber(unicode):
   """A human-readable phone number or address.
 
@@ -845,29 +856,6 @@
                     type(arg).__name__)
 
 
-_PROPERTY_TYPES = [
-  str,
-  unicode,
-  bool,
-  int,
-  long,
-  type(None),
-  float,
-  Key,
-  datetime.datetime,
-  Blob,
-  Text,
-  users.User,
-  Category,
-  Link,
-  Email,
-  GeoPt,
-  IM,
-  PhoneNumber,
-  PostalAddress,
-  Rating,
-  ]
-
 _PROPERTY_MEANINGS = {
 
 
@@ -883,45 +871,171 @@
   PhoneNumber:       entity_pb.Property.GD_PHONENUMBER,
   PostalAddress:     entity_pb.Property.GD_POSTALADDRESS,
   Rating:            entity_pb.Property.GD_RATING,
-  }
+}
 
-_RAW_PROPERTY_TYPES = (
+_PROPERTY_TYPES = frozenset([
+  Blob,
+  bool,
+  Category,
+  datetime.datetime,
+  Email,
+  float,
+  GeoPt,
+  IM,
+  int,
+  Key,
+  Link,
+  long,
+  PhoneNumber,
+  PostalAddress,
+  Rating,
+  str,
+  Text,
+  type(None),
+  unicode,
+  users.User,
+])
+
+_RAW_PROPERTY_TYPES = frozenset([
   Blob,
   Text,
-)
+])
+
+_STRING_TYPES = frozenset([
+  str,
+  unicode,
+])
+
+def ValidatePropertyInteger(name, value):
+  """Raises an exception if the supplied integer is invalid.
+
+  Args:
+    name: Name of the property this is for.
+    value: Integer value.
+
+  Raises:
+    OverflowError if the value does not fit within a signed int64.
+  """
+  if not (-0x8000000000000000 <= value <= 0x7fffffffffffffff):
+    raise OverflowError('%d is out of bounds for int64' % value)
+
+
+def ValidateStringLength(name, value, max_len):
+  """Raises an exception if the supplied string is too long.
 
-def ToPropertyPb(name, values):
-  """Creates a type-specific onestore property PB from a property name and a
-  value or list of values. Determines the type of property based on the type
-  of the value(s).
+  Args:
+    name: Name of the property this is for.
+    value: String value.
+    max_len: Maximum length the string may be.
+
+  Raises:
+    OverflowError if the value is larger than the maximum length.
+  """
+  if len(value) > max_len:
+    raise datastore_errors.BadValueError(
+      'Property %s is %d bytes long; it must be %d or less. '
+      'Consider Text instead, which can store strings of any length.' %
+      (name, len(value), max_len))
 
-  If name is invalid, Serialize throws a BadPropertyError. If values is
-  an unsupported type, or an empty list, or a list with elements of different
-  types, Serialize throws a BadValueError.
+
+def ValidatePropertyString(name, value):
+  """Validates the length of an indexed string property.
+
+  Args:
+    name: Name of the property this is for.
+    value: String value.
+  """
+  ValidateStringLength(name, value, max_len=_MAX_STRING_LENGTH)
+
+
+def ValidatePropertyLink(name, value):
+  """Validates the length of an indexed Link property.
 
   Args:
-    # the property name
-    name: string
-    # either a supported type or a list of them. if a list, all
-    # of the list's elements should be of the same type
-    values: string, int, long, float, datetime, Key, or list
+    name: Name of the property this is for.
+    value: String value.
+  """
+  ValidateStringLength(name, value, max_len=_MAX_LINK_PROPERTY_LENGTH)
+
+
+def ValidatePropertyNothing(name, value):
+  """No-op validation function.
+
+  Args:
+    name: Name of the property this is for.
+    value: Not used.
+  """
+  pass
+
+
+def ValidatePropertyKey(name, value):
+  """Raises an exception if the supplied datastore.Key instance is invalid.
+
+  Args:
+    name: Name of the property this is for.
+    value: A datastore.Key instance.
+
+  Raises:
+    datastore_errors.BadValueError if the value is invalid.
+  """
+  if not value.has_id_or_name():
+    raise datastore_errors.BadValueError(
+        'Incomplete key found for reference property %s.' % name)
+
 
-  Returns:
-    # a list of or single StringProperty, Int64Property, BoolProperty,
-    # DoubleProperty, PointProperty, UserProperty, or ReferenceProperty.
-    [entity_pb.*Property, ...]
+_VALIDATE_PROPERTY_VALUES = {
+  Blob: ValidatePropertyNothing,
+  bool: ValidatePropertyNothing,
+  Category: ValidatePropertyString,
+  datetime.datetime: ValidatePropertyNothing,
+  Email: ValidatePropertyString,
+  float: ValidatePropertyNothing,
+  GeoPt: ValidatePropertyNothing,
+  IM: ValidatePropertyString,
+  int: ValidatePropertyInteger,
+  Key: ValidatePropertyKey,
+  Link: ValidatePropertyLink,
+  long: ValidatePropertyInteger,
+  PhoneNumber: ValidatePropertyString,
+  PostalAddress: ValidatePropertyString,
+  Rating: ValidatePropertyInteger,
+  str: ValidatePropertyString,
+  Text: ValidatePropertyNothing,
+  type(None): ValidatePropertyNothing,
+  unicode: ValidatePropertyString,
+  users.User: ValidatePropertyNothing,
+}
+
+assert set(_VALIDATE_PROPERTY_VALUES.iterkeys()) == _PROPERTY_TYPES
+
+
+def ValidateProperty(name, values):
+  """Helper function for validating property values.
+
+  Args:
+    name: Name of the property this is for.
+    value: Value for the property as a Python native type.
+
+  Raises:
+    BadPropertyError if the property name is invalid. BadValueError if the
+    property did not validate correctly, a list property did not have values
+    all of the same type, or the value was an empty list. Other exception types
+    (like OverflowError) if the property value does not meet type-specific
+    criteria.
   """
   ValidateString(name, 'property name', datastore_errors.BadPropertyError)
   if RESERVED_PROPERTY_NAME.match(name):
-    raise datastore_errors.BadPropertyError('%s is a reserved property name.' %
-                                            name)
+    raise datastore_errors.BadPropertyError(
+        '%s is a reserved property name.' % name)
 
-  if isinstance(values, tuple):
+  values_type = type(values)
+
+  if values_type is tuple:
     raise datastore_errors.BadValueError(
         'May not use tuple property value; property %s is %s.' %
         (name, repr(values)))
 
-  if isinstance(values, list):
+  if values_type is list:
     multiple = True
   else:
     multiple = False
@@ -932,91 +1046,202 @@
         'May not use the empty list as a property value; property %s is %s.' %
         (name, repr(values)))
 
-  def long_if_int(val):
-    if isinstance(val, int) and not isinstance(val, bool):
-      return long(val)
-    else:
-      return val
-
-  values = [long_if_int(v) for v in values]
-
   try:
     proptype = values[0].__class__
+    prop_validator = _VALIDATE_PROPERTY_VALUES.get(proptype)
+    if prop_validator is None:
+      raise datastore_errors.BadValueError(
+        'Unsupported type for property %s: %s' % (name, proptype))
+
     for v in values:
       if v is not None:
         if (v.__class__ is not proptype and not
-            (v.__class__ in (str, unicode) and proptype in (str, unicode))):
+            (v.__class__ in _STRING_TYPES and proptype in _STRING_TYPES)):
           raise datastore_errors.BadValueError(
               'Values for property %s have mismatched types: %s (a %s) and '
               '%s (a %s).' % (name, values[0], proptype, v, typename(v)))
-        elif (isinstance(v, Key) and not v.has_id_or_name()):
-          raise datastore_errors.BadValueError(
-              'Incomplete key found for reference property %s.' % name)
+
+        prop_validator(name, v)
   except (KeyError, ValueError, TypeError, IndexError, AttributeError), msg:
     raise datastore_errors.BadValueError(
       'Error type checking values for property %s: %s' % (name, msg))
 
-  if proptype not in _PROPERTY_TYPES:
-    raise datastore_errors.BadValueError(
-      'Unsupported type for property %s: %s' % (name, proptype))
+
+ValidateReadProperty = ValidateProperty
+
+
+def PackBlob(name, value, pbvalue):
+  """Packs a Blob property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: A Blob instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  pbvalue.set_stringvalue(value)
+
+
+def PackString(name, value, pbvalue):
+  """Packs a string-typed property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: A string, unicode, or string-like value instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  pbvalue.set_stringvalue(unicode(value).encode('utf-8'))
+
+
+def PackDatetime(name, value, pbvalue):
+  """Packs a datetime-typed property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: A datetime.datetime instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  if value.tzinfo:
+    value = value.astimezone(UTC)
+  pbvalue.set_int64value(
+    long(calendar.timegm(value.timetuple()) * 1000000L) + value.microsecond)
+
+
+def PackGeoPt(name, value, pbvalue):
+  """Packs a GeoPt property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: A GeoPt instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  pbvalue.mutable_pointvalue().set_x(value.lat)
+  pbvalue.mutable_pointvalue().set_y(value.lon)
+
+
+def PackUser(name, value, pbvalue):
+  """Packs a User property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: A users.User instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  pbvalue.mutable_uservalue().set_email(value.email().encode('utf-8'))
+  pbvalue.mutable_uservalue().set_auth_domain(
+      value.auth_domain().encode('utf-8'))
+  pbvalue.mutable_uservalue().set_gaiaid(0)
+
+
+def PackKey(name, value, pbvalue):
+  """Packs a reference property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: A Key instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  ref = value._Key__reference
+  pbvalue.mutable_referencevalue().set_app(ref.app())
+  for elem in ref.path().element_list():
+    pbvalue.mutable_referencevalue().add_pathelement().CopyFrom(elem)
+
+
+def PackBool(name, value, pbvalue):
+  """Packs a boolean property into a entity_pb.PropertyValue.
 
+  Args:
+    name: The name of the property as a string.
+    value: A boolean instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  pbvalue.set_booleanvalue(value)
+
+
+def PackInteger(name, value, pbvalue):
+  """Packs an integer property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: An int or long instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  pbvalue.set_int64value(value)
+
+
+def PackFloat(name, value, pbvalue):
+  """Packs a float property into a entity_pb.PropertyValue.
+
+  Args:
+    name: The name of the property as a string.
+    value: A float instance.
+    pbvalue: The entity_pb.PropertyValue to pack this value into.
+  """
+  pbvalue.set_doublevalue(value)
+
+_PACK_PROPERTY_VALUES = {
+  Blob: PackBlob,
+  bool: PackBool,
+  Category: PackString,
+  datetime.datetime: PackDatetime,
+  Email: PackString,
+  float: PackFloat,
+  GeoPt: PackGeoPt,
+  IM: PackString,
+  int: PackInteger,
+  Key: PackKey,
+  Link: PackString,
+  long: PackInteger,
+  PhoneNumber: PackString,
+  PostalAddress: PackString,
+  Rating: PackInteger,
+  str: PackString,
+  Text: PackString,
+  type(None): lambda name, value, pbvalue: None,
+  unicode: PackString,
+  users.User: PackUser,
+}
+
+assert set(_PACK_PROPERTY_VALUES.iterkeys()) == _PROPERTY_TYPES
+
+
+def ToPropertyPb(name, values):
+  """Creates type-specific entity_pb.PropertyValues.
+
+  Determines the type and meaning of the PropertyValue based on the Python
+  type of the input value(s).
+
+  NOTE: This function does not validate anything!
+
+  Args:
+    name: string or unicode; the property name
+    values: The values for this property, either a single one or a list of them.
+      All values must be a supported type. Lists of values must all be of the
+      same type.
+
+  Returns:
+    A list of entity_pb.PropertyValue instances.
+  """
+  encoded_name = name.encode('utf-8')
+
+  values_type = type(values)
+  if values_type is list:
+    multiple = True
+    proptype = type(values[0])
+  else:
+    multiple = False
+    proptype = type(values)
+    values = [values]
+
+  pack_prop = _PACK_PROPERTY_VALUES[proptype]
   pbs = []
   for v in values:
     pb = entity_pb.Property()
-    pb.set_name(name.encode('utf-8'))
+    pb.set_name(encoded_name)
     pb.set_multiple(multiple)
-    if _PROPERTY_MEANINGS.has_key(proptype):
-      pb.set_meaning(_PROPERTY_MEANINGS[proptype])
-
-    pbvalue = pb.mutable_value()
-    if v is None:
-      pass
-    elif isinstance(v, Blob):
-      pbvalue.set_stringvalue(v)
-    elif isinstance(v, (basestring, IM)):
-      if not isinstance(v, Text):
-        if isinstance(v, Link):
-          max_len = _MAX_LINK_PROPERTY_LENGTH
-        else:
-          max_len = _MAX_STRING_LENGTH
-        if len(v) > max_len:
-          raise datastore_errors.BadValueError(
-            'Property %s is %d bytes long; it must be %d or less. '
-            'Consider Text instead, which can store strings of any length.' %
-            (name, len(v), max_len))
-      pbvalue.set_stringvalue(unicode(v).encode('utf-8'))
-    elif isinstance(v, datetime.datetime):
-      if v.tzinfo:
-        v = v.astimezone(UTC)
-      pbvalue.set_int64value(
-        long(calendar.timegm(v.timetuple()) * 1000000L) + v.microsecond)
-    elif isinstance(v, GeoPt):
-      pbvalue.mutable_pointvalue().set_x(v.lat)
-      pbvalue.mutable_pointvalue().set_y(v.lon)
-    elif isinstance(v, users.User):
-      pbvalue.mutable_uservalue().set_email(v.email().encode('utf-8'))
-      pbvalue.mutable_uservalue().set_auth_domain(
-        v.auth_domain().encode('utf-8'))
-      pbvalue.mutable_uservalue().set_gaiaid(0)
-    elif isinstance(v, Key):
-      ref = v._Key__reference
-      pbvalue.mutable_referencevalue().set_app(ref.app())
-      for elem in ref.path().element_list():
-        pbvalue.mutable_referencevalue().add_pathelement().CopyFrom(elem)
-    elif isinstance(v, bool):
-      pbvalue.set_booleanvalue(v)
-    elif isinstance(v, long):
-      pbvalue.set_int64value(v)
-      try:
-        pbvalue.Encode()
-      except ProtocolBuffer.ProtocolBufferEncodeError, e:
-        pbvalue.clear_int64value()
-        raise OverflowError(e)
-    elif isinstance(v, float):
-      pbvalue.set_doublevalue(v)
-    else:
-      assert False, "Shouldn't reach here; property type was validated above."
-
+    meaning = _PROPERTY_MEANINGS.get(proptype)
+    if meaning is not None:
+      pb.set_meaning(meaning)
+    pbvalue = pack_prop(name, v, pb.mutable_value())
     pbs.append(pb)
 
   if multiple:
@@ -1026,14 +1251,16 @@
 
 
 def FromReferenceProperty(value):
-  """Converts a reference PropertyValue to a Key. Raises BadValueError is prop
-  is not a PropertyValue.
+  """Converts a reference PropertyValue to a Key.
 
   Args:
     value: entity_pb.PropertyValue
 
   Returns:
     Key
+
+  Raises:
+    BadValueError if the value is not a PropertyValue.
   """
   assert isinstance(value, entity_pb.PropertyValue)
   assert value.has_referencevalue()
@@ -1066,10 +1293,11 @@
   entity_pb.Property.GD_RATING:         Rating,
   entity_pb.Property.BLOB:              Blob,
   entity_pb.Property.TEXT:              Text,
-  }
+}
+
 
 def FromPropertyPb(pb):
-  """Converts a onestore property PB to a python value.
+  """Converts a property PB to a python value.
 
   Args:
     pb: entity_pb.Property
@@ -1078,30 +1306,27 @@
     # return type is determined by the type of the argument
     string, int, bool, double, users.User, or one of the atom or gd types
   """
-  if not isinstance(pb, entity_pb.Property):
-    raise datastore_errors.BadValueError(
-      'Expected PropertyValue; received %s (a %s).' % (pb, typename(pb)))
+  pbval = pb.value()
+  meaning = pb.meaning()
 
-  pbval = pb.value()
-
-  if (pbval.has_stringvalue()):
+  if pbval.has_stringvalue():
     value = pbval.stringvalue()
-    if pb.meaning() != entity_pb.Property.BLOB:
+    if meaning != entity_pb.Property.BLOB:
       value = unicode(value.decode('utf-8'))
+  elif pbval.has_int64value():
+    value = long(pbval.int64value())
+  elif pbval.has_booleanvalue():
+    value = bool(pbval.booleanvalue())
+  elif pbval.has_doublevalue():
+    value = pbval.doublevalue()
+  elif pbval.has_referencevalue():
+    value = FromReferenceProperty(pbval)
   elif pbval.has_pointvalue():
     value = (pbval.pointvalue().x(), pbval.pointvalue().y())
   elif pbval.has_uservalue():
     email = unicode(pbval.uservalue().email().decode('utf-8'))
     auth_domain = unicode(pbval.uservalue().auth_domain().decode('utf-8'))
     value = users.User(email=email, _auth_domain=auth_domain)
-  elif pbval.has_referencevalue():
-    value = FromReferenceProperty(pbval)
-  elif pbval.has_int64value():
-    value = long(pbval.int64value())
-  elif pbval.has_booleanvalue():
-    value = bool(pbval.booleanvalue())
-  elif pbval.has_doublevalue():
-    value = float(pbval.doublevalue())
   else:
     if pb.multiple():
       raise datastore_errors.BadValueError(
@@ -1110,8 +1335,9 @@
       value = None
 
   try:
-    if pb.has_meaning() and pb.meaning() in _PROPERTY_CONVERSIONS:
-      value = _PROPERTY_CONVERSIONS[pb.meaning()](value)
+    if pb.has_meaning():
+      conversion = _PROPERTY_CONVERSIONS[meaning]
+      value = conversion(value)
   except (KeyError, ValueError, IndexError, TypeError, AttributeError), msg:
     raise datastore_errors.BadValueError(
       'Error converting pb: %s\nException was: %s' % (pb, msg))
--- a/thirdparty/google_appengine/google/appengine/api/images/images_service_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/images/images_service_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -41,6 +41,7 @@
   def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
   ErrorCode_Name = classmethod(ErrorCode_Name)
 
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -116,6 +117,7 @@
   def Type_Name(cls, x): return cls._Type_NAMES.get(x, "")
   Type_Name = classmethod(Type_Name)
 
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -171,27 +173,28 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class Transform(ProtocolBuffer.ProtocolMessage):
+  has_width_ = 0
+  width_ = 0
+  has_height_ = 0
+  height_ = 0
+  has_rotate_ = 0
+  rotate_ = 0
+  has_horizontal_flip_ = 0
+  horizontal_flip_ = 0
+  has_vertical_flip_ = 0
+  vertical_flip_ = 0
+  has_crop_left_x_ = 0
+  crop_left_x_ = 0.0
+  has_crop_top_y_ = 0
+  crop_top_y_ = 0.0
+  has_crop_right_x_ = 0
+  crop_right_x_ = 1.0
+  has_crop_bottom_y_ = 0
+  crop_bottom_y_ = 1.0
+  has_autolevels_ = 0
+  autolevels_ = 0
+
   def __init__(self, contents=None):
-    self.width_ = 0
-    self.height_ = 0
-    self.rotate_ = 0
-    self.horizontal_flip_ = 0
-    self.vertical_flip_ = 0
-    self.crop_left_x_ = 0.0
-    self.crop_top_y_ = 0.0
-    self.crop_right_x_ = 1.0
-    self.crop_bottom_y_ = 1.0
-    self.autolevels_ = 0
-    self.has_width_ = 0
-    self.has_height_ = 0
-    self.has_rotate_ = 0
-    self.has_horizontal_flip_ = 0
-    self.has_vertical_flip_ = 0
-    self.has_crop_left_x_ = 0
-    self.has_crop_top_y_ = 0
-    self.has_crop_right_x_ = 0
-    self.has_crop_bottom_y_ = 0
-    self.has_autolevels_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def width(self): return self.width_
@@ -523,9 +526,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class ImageData(ProtocolBuffer.ProtocolMessage):
+  has_content_ = 0
+  content_ = ""
+
   def __init__(self, contents=None):
-    self.content_ = ""
-    self.has_content_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def content(self): return self.content_
@@ -620,9 +624,10 @@
   def MIME_TYPE_Name(cls, x): return cls._MIME_TYPE_NAMES.get(x, "")
   MIME_TYPE_Name = classmethod(MIME_TYPE_Name)
 
+  has_mime_type_ = 0
+  mime_type_ = 0
+
   def __init__(self, contents=None):
-    self.mime_type_ = 0
-    self.has_mime_type_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def mime_type(self): return self.mime_type_
@@ -702,12 +707,13 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class ImagesTransformRequest(ProtocolBuffer.ProtocolMessage):
+  has_image_ = 0
+  has_output_ = 0
+
   def __init__(self, contents=None):
     self.image_ = ImageData()
     self.transform_ = []
     self.output_ = OutputSettings()
-    self.has_image_ = 0
-    self.has_output_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def image(self): return self.image_
@@ -773,8 +779,8 @@
       if debug_strs is not None:
         debug_strs.append('Required field: image not set.')
     elif not self.image_.IsInitialized(debug_strs): initialized = 0
-    for i in xrange(len(self.transform_)):
-      if (not self.transform_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.transform_:
+      if not p.IsInitialized(debug_strs): initialized=0
     if (not self.has_output_):
       initialized = 0
       if debug_strs is not None:
@@ -876,9 +882,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class ImagesTransformResponse(ProtocolBuffer.ProtocolMessage):
+  has_image_ = 0
+
   def __init__(self, contents=None):
     self.image_ = ImageData()
-    self.has_image_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def image(self): return self.image_
--- a/thirdparty/google_appengine/google/appengine/api/mail.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/mail.py	Tue Sep 16 02:28:33 2008 +0000
@@ -291,6 +291,19 @@
 MailMessageToMIMEMessage = mail_message_to_mime_message
 
 
+def _to_str(value):
+  """Helper function to make sure unicode values converted to utf-8
+
+  Args:
+    value: str or unicode to convert to utf-8.
+
+  Returns:
+    UTF-8 encoded str of value, otherwise value unchanged.
+  """
+  if isinstance(value, unicode):
+    return value.encode('utf-8')
+  return value
+
 class _EmailMessageBase(object):
   """Base class for email API service objects.
 
@@ -392,23 +405,32 @@
     return self.is_initialized()
 
   def ToProto(self):
+    """Convert mail message to protocol message.
+
+    Unicode strings are converted to UTF-8 for all fields.
+
+    This method is overriden by EmailMessage to support the sender fields.
+
+    Returns:
+      MailMessage protocol version of mail message.
+    """
     self.check_initialized()
     message = mail_service_pb.MailMessage()
-    message.set_sender(self.sender)
+    message.set_sender(_to_str(self.sender))
 
     if hasattr(self, 'reply_to'):
-      message.set_replyto(self.reply_to)
-    message.set_subject(self.subject)
+      message.set_replyto(_to_str(self.reply_to))
+    message.set_subject(_to_str(self.subject))
     if hasattr(self, 'body'):
-      message.set_textbody(self.body)
+      message.set_textbody(_to_str(self.body))
     if hasattr(self, 'html'):
-      message.set_htmlbody(self.html)
+      message.set_htmlbody(_to_str(self.html))
 
     if hasattr(self, 'attachments'):
       for file_name, data in _attachment_sequence(self.attachments):
         attachment = message.add_attachment()
-        attachment.set_filename(file_name)
-        attachment.set_data(data)
+        attachment.set_filename(_to_str(file_name))
+        attachment.set_data(_to_str(data))
     return message
 
   def to_mime_message(self):
@@ -555,6 +577,9 @@
 
   def ToProto(self):
     """Does addition conversion of recipient fields to protocol buffer.
+
+    Returns:
+      MailMessage protocol version of mail message including sender fields.
     """
     message = super(EmailMessage, self).ToProto()
 
@@ -563,7 +588,7 @@
                              ('bcc', message.add_bcc)):
       if hasattr(self, attribute):
         for address in _email_sequence(getattr(self, attribute)):
-          adder(address)
+          adder(_to_str(address))
     return message
 
   def __setattr__(self, attr, value):
--- a/thirdparty/google_appengine/google/appengine/api/mail_service_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/mail_service_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -42,6 +42,7 @@
   def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
   ErrorCode_Name = classmethod(ErrorCode_Name)
 
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -97,11 +98,12 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MailAttachment(ProtocolBuffer.ProtocolMessage):
+  has_filename_ = 0
+  filename_ = ""
+  has_data_ = 0
+  data_ = ""
+
   def __init__(self, contents=None):
-    self.filename_ = ""
-    self.data_ = ""
-    self.has_filename_ = 0
-    self.has_data_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def filename(self): return self.filename_
@@ -215,21 +217,22 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MailMessage(ProtocolBuffer.ProtocolMessage):
+  has_sender_ = 0
+  sender_ = ""
+  has_replyto_ = 0
+  replyto_ = ""
+  has_subject_ = 0
+  subject_ = ""
+  has_textbody_ = 0
+  textbody_ = ""
+  has_htmlbody_ = 0
+  htmlbody_ = ""
+
   def __init__(self, contents=None):
-    self.sender_ = ""
-    self.replyto_ = ""
     self.to_ = []
     self.cc_ = []
     self.bcc_ = []
-    self.subject_ = ""
-    self.textbody_ = ""
-    self.htmlbody_ = ""
     self.attachment_ = []
-    self.has_sender_ = 0
-    self.has_replyto_ = 0
-    self.has_subject_ = 0
-    self.has_textbody_ = 0
-    self.has_htmlbody_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def sender(self): return self.sender_
@@ -408,8 +411,8 @@
       initialized = 0
       if debug_strs is not None:
         debug_strs.append('Required field: subject not set.')
-    for i in xrange(len(self.attachment_)):
-      if (not self.attachment_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.attachment_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
--- a/thirdparty/google_appengine/google/appengine/api/memcache/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/memcache/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -74,6 +74,7 @@
 TYPE_PICKLED = 2
 TYPE_INT = 3
 TYPE_LONG = 4
+TYPE_BOOL = 5
 
 
 def _key_string(key, key_prefix='', server_to_user_dict=None):
@@ -156,6 +157,9 @@
   elif isinstance(value, unicode):
     stored_value = value.encode('utf-8')
     flags |= TYPE_UNICODE
+  elif isinstance(value, bool):
+    stored_value = str(int(value))
+    flags |= TYPE_BOOL
   elif isinstance(value, int):
     stored_value = str(value)
     flags |= TYPE_INT
@@ -204,6 +208,8 @@
     return value.decode('utf-8')
   elif type_number == TYPE_PICKLED:
     return do_unpickle(value)
+  elif type_number == TYPE_BOOL:
+    return bool(int(value))
   elif type_number == TYPE_INT:
     return int(value)
   elif type_number == TYPE_LONG:
--- a/thirdparty/google_appengine/google/appengine/api/memcache/memcache_service_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/memcache/memcache_service_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -36,6 +36,7 @@
   def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
   ErrorCode_Name = classmethod(ErrorCode_Name)
 
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -91,6 +92,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheGetRequest(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.key_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -182,13 +184,14 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheGetResponse_Item(ProtocolBuffer.ProtocolMessage):
+  has_key_ = 0
+  key_ = ""
+  has_value_ = 0
+  value_ = ""
+  has_flags_ = 0
+  flags_ = 0
+
   def __init__(self, contents=None):
-    self.key_ = ""
-    self.value_ = ""
-    self.flags_ = 0
-    self.has_key_ = 0
-    self.has_value_ = 0
-    self.has_flags_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def key(self): return self.key_
@@ -308,6 +311,7 @@
     return res
 
 class MemcacheGetResponse(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.item_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -348,8 +352,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.item_)):
-      if (not self.item_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.item_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -417,17 +421,18 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheSetRequest_Item(ProtocolBuffer.ProtocolMessage):
+  has_key_ = 0
+  key_ = ""
+  has_value_ = 0
+  value_ = ""
+  has_flags_ = 0
+  flags_ = 0
+  has_set_policy_ = 0
+  set_policy_ = 1
+  has_expiration_time_ = 0
+  expiration_time_ = 0
+
   def __init__(self, contents=None):
-    self.key_ = ""
-    self.value_ = ""
-    self.flags_ = 0
-    self.set_policy_ = 1
-    self.expiration_time_ = 0
-    self.has_key_ = 0
-    self.has_value_ = 0
-    self.has_flags_ = 0
-    self.has_set_policy_ = 0
-    self.has_expiration_time_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def key(self): return self.key_
@@ -609,6 +614,7 @@
   def SetPolicy_Name(cls, x): return cls._SetPolicy_NAMES.get(x, "")
   SetPolicy_Name = classmethod(SetPolicy_Name)
 
+
   def __init__(self, contents=None):
     self.item_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -649,8 +655,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.item_)):
-      if (not self.item_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.item_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -740,6 +746,7 @@
   def SetStatusCode_Name(cls, x): return cls._SetStatusCode_NAMES.get(x, "")
   SetStatusCode_Name = classmethod(SetStatusCode_Name)
 
+
   def __init__(self, contents=None):
     self.set_status_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -831,11 +838,12 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheDeleteRequest_Item(ProtocolBuffer.ProtocolMessage):
+  has_key_ = 0
+  key_ = ""
+  has_delete_time_ = 0
+  delete_time_ = 0
+
   def __init__(self, contents=None):
-    self.key_ = ""
-    self.delete_time_ = 0
-    self.has_key_ = 0
-    self.has_delete_time_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def key(self): return self.key_
@@ -928,6 +936,7 @@
     return res
 
 class MemcacheDeleteRequest(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.item_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -968,8 +977,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.item_)):
-      if (not self.item_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.item_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -1045,6 +1054,7 @@
   def DeleteStatusCode_Name(cls, x): return cls._DeleteStatusCode_NAMES.get(x, "")
   DeleteStatusCode_Name = classmethod(DeleteStatusCode_Name)
 
+
   def __init__(self, contents=None):
     self.delete_status_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -1148,13 +1158,14 @@
   def Direction_Name(cls, x): return cls._Direction_NAMES.get(x, "")
   Direction_Name = classmethod(Direction_Name)
 
+  has_key_ = 0
+  key_ = ""
+  has_delta_ = 0
+  delta_ = 1
+  has_direction_ = 0
+  direction_ = 1
+
   def __init__(self, contents=None):
-    self.key_ = ""
-    self.delta_ = 1
-    self.direction_ = 1
-    self.has_key_ = 0
-    self.has_delta_ = 0
-    self.has_direction_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def key(self): return self.key_
@@ -1293,9 +1304,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheIncrementResponse(ProtocolBuffer.ProtocolMessage):
+  has_new_value_ = 0
+  new_value_ = 0
+
   def __init__(self, contents=None):
-    self.new_value_ = 0
-    self.has_new_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def new_value(self): return self.new_value_
@@ -1375,6 +1387,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheFlushRequest(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -1430,6 +1443,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheFlushResponse(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -1485,6 +1499,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheStatsRequest(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -1540,19 +1555,20 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MergedNamespaceStats(ProtocolBuffer.ProtocolMessage):
+  has_hits_ = 0
+  hits_ = 0
+  has_misses_ = 0
+  misses_ = 0
+  has_byte_hits_ = 0
+  byte_hits_ = 0
+  has_items_ = 0
+  items_ = 0
+  has_bytes_ = 0
+  bytes_ = 0
+  has_oldest_item_age_ = 0
+  oldest_item_age_ = 0
+
   def __init__(self, contents=None):
-    self.hits_ = 0
-    self.misses_ = 0
-    self.byte_hits_ = 0
-    self.items_ = 0
-    self.bytes_ = 0
-    self.oldest_item_age_ = 0
-    self.has_hits_ = 0
-    self.has_misses_ = 0
-    self.has_byte_hits_ = 0
-    self.has_items_ = 0
-    self.has_bytes_ = 0
-    self.has_oldest_item_age_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def hits(self): return self.hits_
@@ -1789,9 +1805,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class MemcacheStatsResponse(ProtocolBuffer.ProtocolMessage):
+  has_stats_ = 0
+  stats_ = None
+
   def __init__(self, contents=None):
-    self.stats_ = None
-    self.has_stats_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
--- a/thirdparty/google_appengine/google/appengine/api/memcache/memcache_stub.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/memcache/memcache_stub.py	Tue Sep 16 02:28:33 2008 +0000
@@ -114,6 +114,7 @@
     self._hits = 0
     self._misses = 0
     self._byte_hits = 0
+    self._cache_creation_time = self._gettime()
 
   def MakeSyncCall(self, service, call, request, response):
     """The main RPC entry point.
@@ -214,7 +215,7 @@
       delete_status = MemcacheDeleteResponse.DELETED
       if entry is None:
         delete_status = MemcacheDeleteResponse.NOT_FOUND
-      elif item.delete_time == 0:
+      elif item.delete_time() == 0:
         del self._the_cache[key]
       else:
         entry.ExpireAndLock(item.delete_time())
@@ -279,4 +280,4 @@
       total_bytes += len(entry.value)
     stats.set_bytes(total_bytes)
 
-    stats.set_oldest_item_age(1800)
+    stats.set_oldest_item_age(self._gettime() - self._cache_creation_time)
--- a/thirdparty/google_appengine/google/appengine/api/urlfetch.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/urlfetch.py	Tue Sep 16 02:28:33 2008 +0000
@@ -25,13 +25,17 @@
 
 
 
+import os
 import UserDict
+import urllib2
+import urlparse
 
 from google.appengine.api import apiproxy_stub_map
 from google.appengine.api import urlfetch_service_pb
 from google.appengine.api.urlfetch_errors import *
 from google.appengine.runtime import apiproxy_errors
 
+MAX_REDIRECTS = 5
 
 GET = 1
 POST = 2
@@ -153,7 +157,33 @@
     return dict(self)
 
 
-def fetch(url, payload=None, method=GET, headers={}, allow_truncated=False):
+def _is_fetching_self(url, method):
+  """Checks if the fetch is for the same URL from which it originated.
+
+  Args:
+    url: str, The URL being fetched.
+    method: value from _VALID_METHODS.
+
+  Returns:
+    boolean indicating whether or not it seems that the app is trying to fetch
+      itself.
+  """
+  if (method != GET or
+      "HTTP_HOST" not in os.environ or
+      "PATH_INFO" not in os.environ):
+    return False
+
+  scheme, host_port, path, query, fragment = urlparse.urlsplit(url)
+
+  if (host_port == os.environ['HTTP_HOST'] and
+      urllib2.unquote(path) == urllib2.unquote(os.environ['PATH_INFO'])):
+    return True
+
+  return False
+
+
+def fetch(url, payload=None, method=GET, headers={}, allow_truncated=False,
+          follow_redirects=True):
   """Fetches the given HTTP URL, blocking until the result is returned.
 
   Other optional parameters are:
@@ -161,8 +191,15 @@
      payload: POST or PUT payload (implies method is not GET, HEAD, or DELETE)
      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
-     truncated.
+       error. otherwise, ResponseTooLargeError will be thrown when a response is
+       truncated.
+     follow_redirects: if true (the default), redirects are
+       transparently followed and the response (if less than 5
+       redirects) contains the final destination's payload and the
+       response status is 200.  You lose, however, the redirect chain
+       information.  If false, you see the HTTP response yourself,
+       including the 'Location' header, and redirects are not
+       followed.
 
   We use a HTTP/1.1 compliant proxy to fetch the result.
 
@@ -177,15 +214,20 @@
   of the returned structure, so HTTP errors like 404 do not result in an
   exception.
   """
-  request = urlfetch_service_pb.URLFetchRequest()
-  response = urlfetch_service_pb.URLFetchResponse()
-  request.set_url(url)
-
   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))
+
+  if _is_fetching_self(url, method):
+    raise InvalidURLError("App cannot fetch the same URL as the one used for "
+                          "the request.")
+
+  request = urlfetch_service_pb.URLFetchRequest()
+  response = urlfetch_service_pb.URLFetchResponse()
+  request.set_url(url)
+
   if method == GET:
     request.set_method(urlfetch_service_pb.URLFetchRequest.GET)
   elif method == POST:
@@ -205,6 +247,8 @@
     header_proto.set_key(key)
     header_proto.set_value(value)
 
+  request.set_followredirects(follow_redirects)
+
   try:
     apiproxy_stub_map.MakeSyncCall('urlfetch', 'Fetch', request, response)
   except apiproxy_errors.ApplicationError, e:
--- a/thirdparty/google_appengine/google/appengine/api/urlfetch_service_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/urlfetch_service_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -42,6 +42,7 @@
   def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
   ErrorCode_Name = classmethod(ErrorCode_Name)
 
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -97,11 +98,12 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class URLFetchRequest_Header(ProtocolBuffer.ProtocolMessage):
+  has_key_ = 0
+  key_ = ""
+  has_value_ = 0
+  value_ = ""
+
   def __init__(self, contents=None):
-    self.key_ = ""
-    self.value_ = ""
-    self.has_key_ = 0
-    self.has_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def key(self): return self.key_
@@ -215,14 +217,17 @@
   def RequestMethod_Name(cls, x): return cls._RequestMethod_NAMES.get(x, "")
   RequestMethod_Name = classmethod(RequestMethod_Name)
 
+  has_method_ = 0
+  method_ = 0
+  has_url_ = 0
+  url_ = ""
+  has_payload_ = 0
+  payload_ = ""
+  has_followredirects_ = 0
+  followredirects_ = 1
+
   def __init__(self, contents=None):
-    self.method_ = 0
-    self.url_ = ""
     self.header_ = []
-    self.payload_ = ""
-    self.has_method_ = 0
-    self.has_url_ = 0
-    self.has_payload_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def method(self): return self.method_
@@ -277,6 +282,18 @@
 
   def has_payload(self): return self.has_payload_
 
+  def followredirects(self): return self.followredirects_
+
+  def set_followredirects(self, x):
+    self.has_followredirects_ = 1
+    self.followredirects_ = x
+
+  def clear_followredirects(self):
+    self.has_followredirects_ = 0
+    self.followredirects_ = 1
+
+  def has_followredirects(self): return self.has_followredirects_
+
 
   def MergeFrom(self, x):
     assert x is not self
@@ -284,6 +301,7 @@
     if (x.has_url()): self.set_url(x.url())
     for i in xrange(x.header_size()): self.add_header().CopyFrom(x.header(i))
     if (x.has_payload()): self.set_payload(x.payload())
+    if (x.has_followredirects()): self.set_followredirects(x.followredirects())
 
   def Equals(self, x):
     if x is self: return 1
@@ -296,6 +314,8 @@
       if e1 != e2: return 0
     if self.has_payload_ != x.has_payload_: return 0
     if self.has_payload_ and self.payload_ != x.payload_: return 0
+    if self.has_followredirects_ != x.has_followredirects_: return 0
+    if self.has_followredirects_ and self.followredirects_ != x.followredirects_: return 0
     return 1
 
   def __eq__(self, other):
@@ -314,8 +334,8 @@
       initialized = 0
       if debug_strs is not None:
         debug_strs.append('Required field: url not set.')
-    for i in xrange(len(self.header_)):
-      if (not self.header_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.header_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -325,6 +345,7 @@
     n += 2 * len(self.header_)
     for i in xrange(len(self.header_)): n += self.header_[i].ByteSize()
     if (self.has_payload_): n += 1 + self.lengthString(len(self.payload_))
+    if (self.has_followredirects_): n += 2
     return n + 2
 
   def Clear(self):
@@ -332,6 +353,7 @@
     self.clear_url()
     self.clear_header()
     self.clear_payload()
+    self.clear_followredirects()
 
   def OutputUnchecked(self, out):
     out.putVarInt32(8)
@@ -345,6 +367,9 @@
     if (self.has_payload_):
       out.putVarInt32(50)
       out.putPrefixedString(self.payload_)
+    if (self.has_followredirects_):
+      out.putVarInt32(56)
+      out.putBoolean(self.followredirects_)
 
   def TryMerge(self, d):
     while d.avail() > 0:
@@ -361,6 +386,9 @@
       if tt == 50:
         self.set_payload(d.getPrefixedString())
         continue
+      if tt == 56:
+        self.set_followredirects(d.getBoolean())
+        continue
       if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
       d.skipData(tt)
 
@@ -378,6 +406,7 @@
       res+=prefix+"}\n"
       cnt+=1
     if self.has_payload_: res+=prefix+("Payload: %s\n" % self.DebugFormatString(self.payload_))
+    if self.has_followredirects_: res+=prefix+("FollowRedirects: %s\n" % self.DebugFormatBool(self.followredirects_))
     return res
 
   kMethod = 1
@@ -386,6 +415,7 @@
   kHeaderKey = 4
   kHeaderValue = 5
   kPayload = 6
+  kFollowRedirects = 7
 
   _TEXT = (
    "ErrorCode",
@@ -395,6 +425,7 @@
    "Key",
    "Value",
    "Payload",
+   "FollowRedirects",
   )
 
   _TYPES = (
@@ -411,16 +442,19 @@
 
    ProtocolBuffer.Encoder.STRING,
 
+   ProtocolBuffer.Encoder.NUMERIC,
+
   )
 
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class URLFetchResponse_Header(ProtocolBuffer.ProtocolMessage):
+  has_key_ = 0
+  key_ = ""
+  has_value_ = 0
+  value_ = ""
+
   def __init__(self, contents=None):
-    self.key_ = ""
-    self.value_ = ""
-    self.has_key_ = 0
-    self.has_value_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def key(self): return self.key_
@@ -516,14 +550,15 @@
     return res
 
 class URLFetchResponse(ProtocolBuffer.ProtocolMessage):
+  has_content_ = 0
+  content_ = ""
+  has_statuscode_ = 0
+  statuscode_ = 0
+  has_contentwastruncated_ = 0
+  contentwastruncated_ = 0
+
   def __init__(self, contents=None):
-    self.content_ = ""
-    self.statuscode_ = 0
     self.header_ = []
-    self.contentwastruncated_ = 0
-    self.has_content_ = 0
-    self.has_statuscode_ = 0
-    self.has_contentwastruncated_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def content(self): return self.content_
@@ -611,8 +646,8 @@
       initialized = 0
       if debug_strs is not None:
         debug_strs.append('Required field: statuscode not set.')
-    for i in xrange(len(self.header_)):
-      if (not self.header_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.header_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
--- a/thirdparty/google_appengine/google/appengine/api/urlfetch_stub.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/urlfetch_stub.py	Tue Sep 16 02:28:33 2008 +0000
@@ -24,6 +24,7 @@
 import socket
 import urlparse
 
+from google.appengine.api import urlfetch
 from google.appengine.api import urlfetch_errors
 from google.appengine.api import urlfetch_service_pb
 from google.appengine.runtime import apiproxy_errors
@@ -31,7 +32,7 @@
 
 MAX_RESPONSE_SIZE = 2 ** 24
 
-MAX_REDIRECTS = 5
+MAX_REDIRECTS = urlfetch.MAX_REDIRECTS
 
 REDIRECT_STATUSES = frozenset([
   httplib.MOVED_PERMANENTLY,
@@ -93,9 +94,11 @@
         urlfetch_service_pb.URLFetchServiceError.INVALID_URL)
 
     self._RetrieveURL(request.url(), payload, method,
-                      request.header_list(), response)
+                      request.header_list(), response,
+                      follow_redirects=request.followredirects())
 
-  def _RetrieveURL(self, url, payload, method, headers, response):
+  def _RetrieveURL(self, url, payload, method, headers, response,
+                   follow_redirects=True):
     """Retrieves a URL.
 
     Args:
@@ -104,6 +107,8 @@
       method: HTTP method to use (e.g., 'GET')
       headers: List of additional header objects to use for the request.
       response: Response object
+      follow_redirects: optional setting (defaulting to True) for whether or not
+        we should transparently follow redirects (up to MAX_REDIRECTS)
 
     Raises:
       Raises an apiproxy_errors.ApplicationError exception with FETCH_ERROR
@@ -164,7 +169,7 @@
         raise apiproxy_errors.ApplicationError(
           urlfetch_service_pb.URLFetchServiceError.FETCH_ERROR, str(e))
 
-      if http_response.status in REDIRECT_STATUSES:
+      if http_response.status in REDIRECT_STATUSES and follow_redirects:
         url = http_response.getheader('Location', None)
         if url is None:
           error_msg = 'Redirecting response was missing "Location" header'
--- a/thirdparty/google_appengine/google/appengine/api/user_service_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/user_service_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -36,6 +36,7 @@
   def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
   ErrorCode_Name = classmethod(ErrorCode_Name)
 
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
--- a/thirdparty/google_appengine/google/appengine/api/validation.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/validation.py	Tue Sep 16 02:28:33 2008 +0000
@@ -57,7 +57,7 @@
 
   def __init__(self, message, cause=None):
     """Initialize exception."""
-    if hasattr(cause, 'args'):
+    if hasattr(cause, 'args') and cause.args:
       Error.__init__(self, message, *cause.args)
     else:
       Error.__init__(self, message)
--- a/thirdparty/google_appengine/google/appengine/api/yaml_errors.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/yaml_errors.py	Tue Sep 16 02:28:33 2008 +0000
@@ -68,7 +68,7 @@
 
   def __init__(self, cause):
     """Initialize event-listener error."""
-    if hasattr(cause, 'args'):
+    if hasattr(cause, 'args') and cause.args:
       Error.__init__(self, *cause.args)
     else:
       Error.__init__(self, str(cause))
--- a/thirdparty/google_appengine/google/appengine/datastore/datastore_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/datastore/datastore_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -31,9 +31,10 @@
 from google.appengine.datastore.entity_pb import Property
 from google.appengine.datastore.entity_pb import Reference
 class Transaction(ProtocolBuffer.ProtocolMessage):
+  has_handle_ = 0
+  handle_ = 0
+
   def __init__(self, contents=None):
-    self.handle_ = 0
-    self.has_handle_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def handle(self): return self.handle_
@@ -137,10 +138,11 @@
   def Operator_Name(cls, x): return cls._Operator_NAMES.get(x, "")
   Operator_Name = classmethod(Operator_Name)
 
+  has_op_ = 0
+  op_ = 0
+
   def __init__(self, contents=None):
-    self.op_ = 0
     self.property_ = []
-    self.has_op_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def op(self): return self.op_
@@ -198,8 +200,8 @@
       initialized = 0
       if debug_strs is not None:
         debug_strs.append('Required field: op not set.')
-    for i in xrange(len(self.property_)):
-      if (not self.property_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.property_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -264,11 +266,12 @@
   def Direction_Name(cls, x): return cls._Direction_NAMES.get(x, "")
   Direction_Name = classmethod(Direction_Name)
 
+  has_property_ = 0
+  property_ = ""
+  has_direction_ = 0
+  direction_ = 1
+
   def __init__(self, contents=None):
-    self.property_ = ""
-    self.direction_ = 1
-    self.has_property_ = 0
-    self.has_direction_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def property(self): return self.property_
@@ -375,26 +378,27 @@
   def Plan_Name(cls, x): return cls._Plan_NAMES.get(x, "")
   Plan_Name = classmethod(Plan_Name)
 
+  has_app_ = 0
+  app_ = ""
+  has_kind_ = 0
+  kind_ = ""
+  has_ancestor_ = 0
+  ancestor_ = None
+  has_search_query_ = 0
+  search_query_ = ""
+  has_hint_ = 0
+  hint_ = 0
+  has_offset_ = 0
+  offset_ = 0
+  has_limit_ = 0
+  limit_ = 0
+  has_require_perfect_plan_ = 0
+  require_perfect_plan_ = 0
+
   def __init__(self, contents=None):
-    self.app_ = ""
-    self.kind_ = ""
-    self.ancestor_ = None
     self.filter_ = []
-    self.search_query_ = ""
     self.order_ = []
-    self.hint_ = 0
-    self.offset_ = 0
-    self.limit_ = 0
     self.composite_index_ = []
-    self.require_perfect_plan_ = 0
-    self.has_app_ = 0
-    self.has_kind_ = 0
-    self.has_ancestor_ = 0
-    self.has_search_query_ = 0
-    self.has_hint_ = 0
-    self.has_offset_ = 0
-    self.has_limit_ = 0
-    self.has_require_perfect_plan_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -604,12 +608,12 @@
       if debug_strs is not None:
         debug_strs.append('Required field: app not set.')
     if (self.has_ancestor_ and not self.ancestor_.IsInitialized(debug_strs)): initialized = 0
-    for i in xrange(len(self.filter_)):
-      if (not self.filter_[i].IsInitialized(debug_strs)): initialized=0
-    for i in xrange(len(self.order_)):
-      if (not self.order_[i].IsInitialized(debug_strs)): initialized=0
-    for i in xrange(len(self.composite_index_)):
-      if (not self.composite_index_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.filter_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    for p in self.order_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    for p in self.composite_index_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -853,14 +857,15 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class QueryExplanation(ProtocolBuffer.ProtocolMessage):
+  has_native_ancestor_ = 0
+  native_ancestor_ = 0
+  has_native_offset_ = 0
+  native_offset_ = 0
+  has_native_limit_ = 0
+  native_limit_ = 0
+
   def __init__(self, contents=None):
-    self.native_ancestor_ = 0
     self.native_index_ = []
-    self.native_offset_ = 0
-    self.native_limit_ = 0
-    self.has_native_ancestor_ = 0
-    self.has_native_offset_ = 0
-    self.has_native_limit_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def native_ancestor(self): return self.native_ancestor_
@@ -944,8 +949,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.native_index_)):
-      if (not self.native_index_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.native_index_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -1043,9 +1048,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class Cursor(ProtocolBuffer.ProtocolMessage):
+  has_cursor_ = 0
+  cursor_ = 0
+
   def __init__(self, contents=None):
-    self.cursor_ = 0
-    self.has_cursor_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def cursor(self): return self.cursor_
@@ -1145,6 +1151,7 @@
   def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
   ErrorCode_Name = classmethod(ErrorCode_Name)
 
+
   def __init__(self, contents=None):
     pass
     if contents is not None: self.MergeFromString(contents)
@@ -1200,10 +1207,11 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class GetRequest(ProtocolBuffer.ProtocolMessage):
+  has_transaction_ = 0
+  transaction_ = None
+
   def __init__(self, contents=None):
     self.key_ = []
-    self.transaction_ = None
-    self.has_transaction_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -1263,8 +1271,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.key_)):
-      if (not self.key_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.key_:
+      if not p.IsInitialized(debug_strs): initialized=0
     if (self.has_transaction_ and not self.transaction_.IsInitialized(debug_strs)): initialized = 0
     return initialized
 
@@ -1344,9 +1352,10 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class GetResponse_Entity(ProtocolBuffer.ProtocolMessage):
+  has_entity_ = 0
+  entity_ = None
+
   def __init__(self, contents=None):
-    self.entity_ = None
-    self.has_entity_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -1426,6 +1435,7 @@
     return res
 
 class GetResponse(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.entity_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -1466,8 +1476,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.entity_)):
-      if (not self.entity_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.entity_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -1527,11 +1537,12 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class PutRequest(ProtocolBuffer.ProtocolMessage):
+  has_transaction_ = 0
+  transaction_ = None
+
   def __init__(self, contents=None):
     self.entity_ = []
-    self.transaction_ = None
     self.composite_index_ = []
-    self.has_transaction_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -1611,11 +1622,11 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.entity_)):
-      if (not self.entity_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.entity_:
+      if not p.IsInitialized(debug_strs): initialized=0
     if (self.has_transaction_ and not self.transaction_.IsInitialized(debug_strs)): initialized = 0
-    for i in xrange(len(self.composite_index_)):
-      if (not self.composite_index_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.composite_index_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -1719,6 +1730,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class PutResponse(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.key_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -1759,8 +1771,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.key_)):
-      if (not self.key_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.key_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -1819,10 +1831,11 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class DeleteRequest(ProtocolBuffer.ProtocolMessage):
+  has_transaction_ = 0
+  transaction_ = None
+
   def __init__(self, contents=None):
     self.key_ = []
-    self.transaction_ = None
-    self.has_transaction_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -1882,8 +1895,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.key_)):
-      if (not self.key_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.key_:
+      if not p.IsInitialized(debug_strs): initialized=0
     if (self.has_transaction_ and not self.transaction_.IsInitialized(debug_strs)): initialized = 0
     return initialized
 
@@ -1975,11 +1988,12 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class NextRequest(ProtocolBuffer.ProtocolMessage):
+  has_cursor_ = 0
+  has_count_ = 0
+  count_ = 1
+
   def __init__(self, contents=None):
     self.cursor_ = Cursor()
-    self.count_ = 1
-    self.has_cursor_ = 0
-    self.has_count_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def cursor(self): return self.cursor_
@@ -2094,12 +2108,13 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class QueryResult(ProtocolBuffer.ProtocolMessage):
+  has_cursor_ = 0
+  cursor_ = None
+  has_more_results_ = 0
+  more_results_ = 0
+
   def __init__(self, contents=None):
-    self.cursor_ = None
     self.result_ = []
-    self.more_results_ = 0
-    self.has_cursor_ = 0
-    self.has_more_results_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -2175,8 +2190,8 @@
   def IsInitialized(self, debug_strs=None):
     initialized = 1
     if (self.has_cursor_ and not self.cursor_.IsInitialized(debug_strs)): initialized = 0
-    for i in xrange(len(self.result_)):
-      if (not self.result_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.result_:
+      if not p.IsInitialized(debug_strs): initialized=0
     if (not self.has_more_results_):
       initialized = 0
       if debug_strs is not None:
@@ -2270,6 +2285,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class Schema(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.kind_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -2310,8 +2326,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.kind_)):
-      if (not self.kind_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.kind_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -2370,6 +2386,7 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class CompositeIndices(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.index_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -2410,8 +2427,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.index_)):
-      if (not self.index_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.index_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
--- a/thirdparty/google_appengine/google/appengine/datastore/entity_pb.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/datastore/entity_pb.py	Tue Sep 16 02:28:33 2008 +0000
@@ -23,13 +23,14 @@
                    unusednames=printElemNumber,debug_strs no-special"""
 
 class PropertyValue_ReferenceValuePathElement(ProtocolBuffer.ProtocolMessage):
+  has_type_ = 0
+  type_ = ""
+  has_id_ = 0
+  id_ = 0
+  has_name_ = 0
+  name_ = ""
+
   def __init__(self, contents=None):
-    self.type_ = ""
-    self.id_ = 0
-    self.name_ = ""
-    self.has_type_ = 0
-    self.has_id_ = 0
-    self.has_name_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def type(self): return self.type_
@@ -146,11 +147,12 @@
     return res
 
 class PropertyValue_PointValue(ProtocolBuffer.ProtocolMessage):
+  has_x_ = 0
+  x_ = 0.0
+  has_y_ = 0
+  y_ = 0.0
+
   def __init__(self, contents=None):
-    self.x_ = 0.0
-    self.y_ = 0.0
-    self.has_x_ = 0
-    self.has_y_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def x(self): return self.x_
@@ -244,15 +246,16 @@
     return res
 
 class PropertyValue_UserValue(ProtocolBuffer.ProtocolMessage):
+  has_email_ = 0
+  email_ = ""
+  has_auth_domain_ = 0
+  auth_domain_ = ""
+  has_nickname_ = 0
+  nickname_ = ""
+  has_gaiaid_ = 0
+  gaiaid_ = 0
+
   def __init__(self, contents=None):
-    self.email_ = ""
-    self.auth_domain_ = ""
-    self.nickname_ = ""
-    self.gaiaid_ = 0
-    self.has_email_ = 0
-    self.has_auth_domain_ = 0
-    self.has_nickname_ = 0
-    self.has_gaiaid_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def email(self): return self.email_
@@ -399,10 +402,11 @@
     return res
 
 class PropertyValue_ReferenceValue(ProtocolBuffer.ProtocolMessage):
+  has_app_ = 0
+  app_ = ""
+
   def __init__(self, contents=None):
-    self.app_ = ""
     self.pathelement_ = []
-    self.has_app_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def app(self): return self.app_
@@ -460,8 +464,8 @@
       initialized = 0
       if debug_strs is not None:
         debug_strs.append('Required field: app not set.')
-    for i in xrange(len(self.pathelement_)):
-      if (not self.pathelement_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.pathelement_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -511,21 +515,22 @@
     return res
 
 class PropertyValue(ProtocolBuffer.ProtocolMessage):
+  has_int64value_ = 0
+  int64value_ = 0
+  has_booleanvalue_ = 0
+  booleanvalue_ = 0
+  has_stringvalue_ = 0
+  stringvalue_ = ""
+  has_doublevalue_ = 0
+  doublevalue_ = 0.0
+  has_pointvalue_ = 0
+  pointvalue_ = None
+  has_uservalue_ = 0
+  uservalue_ = None
+  has_referencevalue_ = 0
+  referencevalue_ = None
+
   def __init__(self, contents=None):
-    self.int64value_ = 0
-    self.booleanvalue_ = 0
-    self.stringvalue_ = ""
-    self.doublevalue_ = 0.0
-    self.pointvalue_ = None
-    self.uservalue_ = None
-    self.referencevalue_ = None
-    self.has_int64value_ = 0
-    self.has_booleanvalue_ = 0
-    self.has_stringvalue_ = 0
-    self.has_doublevalue_ = 0
-    self.has_pointvalue_ = 0
-    self.has_uservalue_ = 0
-    self.has_referencevalue_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -886,17 +891,18 @@
   def Meaning_Name(cls, x): return cls._Meaning_NAMES.get(x, "")
   Meaning_Name = classmethod(Meaning_Name)
 
+  has_meaning_ = 0
+  meaning_ = 0
+  has_meaning_uri_ = 0
+  meaning_uri_ = ""
+  has_name_ = 0
+  name_ = ""
+  has_value_ = 0
+  has_multiple_ = 0
+  multiple_ = 0
+
   def __init__(self, contents=None):
-    self.meaning_ = 0
-    self.meaning_uri_ = ""
-    self.name_ = ""
     self.value_ = PropertyValue()
-    self.multiple_ = 0
-    self.has_meaning_ = 0
-    self.has_meaning_uri_ = 0
-    self.has_name_ = 0
-    self.has_value_ = 0
-    self.has_multiple_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def meaning(self): return self.meaning_
@@ -1098,13 +1104,14 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class Path_Element(ProtocolBuffer.ProtocolMessage):
+  has_type_ = 0
+  type_ = ""
+  has_id_ = 0
+  id_ = 0
+  has_name_ = 0
+  name_ = ""
+
   def __init__(self, contents=None):
-    self.type_ = ""
-    self.id_ = 0
-    self.name_ = ""
-    self.has_type_ = 0
-    self.has_id_ = 0
-    self.has_name_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def type(self): return self.type_
@@ -1221,6 +1228,7 @@
     return res
 
 class Path(ProtocolBuffer.ProtocolMessage):
+
   def __init__(self, contents=None):
     self.element_ = []
     if contents is not None: self.MergeFromString(contents)
@@ -1261,8 +1269,8 @@
 
   def IsInitialized(self, debug_strs=None):
     initialized = 1
-    for i in xrange(len(self.element_)):
-      if (not self.element_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.element_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -1330,11 +1338,12 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class Reference(ProtocolBuffer.ProtocolMessage):
+  has_app_ = 0
+  app_ = ""
+  has_path_ = 0
+
   def __init__(self, contents=None):
-    self.app_ = ""
     self.path_ = Path()
-    self.has_app_ = 0
-    self.has_path_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def app(self): return self.app_
@@ -1488,15 +1497,16 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class User(ProtocolBuffer.ProtocolMessage):
+  has_email_ = 0
+  email_ = ""
+  has_auth_domain_ = 0
+  auth_domain_ = ""
+  has_nickname_ = 0
+  nickname_ = ""
+  has_gaiaid_ = 0
+  gaiaid_ = 0
+
   def __init__(self, contents=None):
-    self.email_ = ""
-    self.auth_domain_ = ""
-    self.nickname_ = ""
-    self.gaiaid_ = 0
-    self.has_email_ = 0
-    self.has_auth_domain_ = 0
-    self.has_nickname_ = 0
-    self.has_gaiaid_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def email(self): return self.email_
@@ -1683,19 +1693,20 @@
   def Kind_Name(cls, x): return cls._Kind_NAMES.get(x, "")
   Kind_Name = classmethod(Kind_Name)
 
+  has_key_ = 0
+  has_entity_group_ = 0
+  has_owner_ = 0
+  owner_ = None
+  has_kind_ = 0
+  kind_ = 0
+  has_kind_uri_ = 0
+  kind_uri_ = ""
+
   def __init__(self, contents=None):
     self.key_ = Reference()
     self.entity_group_ = Path()
-    self.owner_ = None
-    self.kind_ = 0
-    self.kind_uri_ = ""
     self.property_ = []
     self.raw_property_ = []
-    self.has_key_ = 0
-    self.has_entity_group_ = 0
-    self.has_owner_ = 0
-    self.has_kind_ = 0
-    self.has_kind_uri_ = 0
     self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
@@ -1838,10 +1849,10 @@
         debug_strs.append('Required field: entity_group not set.')
     elif not self.entity_group_.IsInitialized(debug_strs): initialized = 0
     if (self.has_owner_ and not self.owner_.IsInitialized(debug_strs)): initialized = 0
-    for i in xrange(len(self.property_)):
-      if (not self.property_[i].IsInitialized(debug_strs)): initialized=0
-    for i in xrange(len(self.raw_property_)):
-      if (not self.raw_property_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.property_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    for p in self.raw_property_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -2039,10 +2050,11 @@
   _STYLE = """"""
   _STYLE_CONTENT_TYPE = """"""
 class CompositeProperty(ProtocolBuffer.ProtocolMessage):
+  has_index_id_ = 0
+  index_id_ = 0
+
   def __init__(self, contents=None):
-    self.index_id_ = 0
     self.value_ = []
-    self.has_index_id_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def index_id(self): return self.index_id_
@@ -2175,11 +2187,12 @@
   def Direction_Name(cls, x): return cls._Direction_NAMES.get(x, "")
   Direction_Name = classmethod(Direction_Name)
 
+  has_name_ = 0
+  name_ = ""
+  has_direction_ = 0
+  direction_ = 1
+
   def __init__(self, contents=None):
-    self.name_ = ""
-    self.direction_ = 1
-    self.has_name_ = 0
-    self.has_direction_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def name(self): return self.name_
@@ -2272,12 +2285,13 @@
     return res
 
 class Index(ProtocolBuffer.ProtocolMessage):
+  has_entity_type_ = 0
+  entity_type_ = ""
+  has_ancestor_ = 0
+  ancestor_ = 0
+
   def __init__(self, contents=None):
-    self.entity_type_ = ""
-    self.ancestor_ = 0
     self.property_ = []
-    self.has_entity_type_ = 0
-    self.has_ancestor_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def entity_type(self): return self.entity_type_
@@ -2354,8 +2368,8 @@
       initialized = 0
       if debug_strs is not None:
         debug_strs.append('Required field: ancestor not set.')
-    for i in xrange(len(self.property_)):
-      if (not self.property_[i].IsInitialized(debug_strs)): initialized=0
+    for p in self.property_:
+      if not p.IsInitialized(debug_strs): initialized=0
     return initialized
 
   def ByteSize(self):
@@ -2458,15 +2472,16 @@
   def State_Name(cls, x): return cls._State_NAMES.get(x, "")
   State_Name = classmethod(State_Name)
 
+  has_app_id_ = 0
+  app_id_ = ""
+  has_id_ = 0
+  id_ = 0
+  has_definition_ = 0
+  has_state_ = 0
+  state_ = 0
+
   def __init__(self, contents=None):
-    self.app_id_ = ""
-    self.id_ = 0
     self.definition_ = Index()
-    self.state_ = 0
-    self.has_app_id_ = 0
-    self.has_id_ = 0
-    self.has_definition_ = 0
-    self.has_state_ = 0
     if contents is not None: self.MergeFromString(contents)
 
   def app_id(self): return self.app_id_
--- a/thirdparty/google_appengine/google/appengine/ext/admin/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -28,7 +28,10 @@
 import logging
 import math
 import mimetypes
+import os
 import os.path
+import pickle
+import pprint
 import random
 import sys
 import time
@@ -39,8 +42,10 @@
 import wsgiref.handlers
 
 from google.appengine.api import datastore
+from google.appengine.api import datastore_admin
 from google.appengine.api import datastore_types
 from google.appengine.api import datastore_errors
+from google.appengine.api import memcache
 from google.appengine.api import users
 from google.appengine.ext import db
 from google.appengine.ext import webapp
@@ -93,6 +98,7 @@
   def generate(self, template_name, template_values={}):
     base_path = self.base_path()
     values = {
+      'application_name': self.request.environ['APPLICATION_ID'],
       'user': users.get_current_user(),
       'request': self.request,
       'home_path': base_path + DefaultPageHandler.PATH,
@@ -101,6 +107,7 @@
       'datastore_batch_edit_path': base_path + DatastoreBatchEditHandler.PATH,
       'interactive_path': base_path + InteractivePageHandler.PATH,
       'interactive_execute_path': base_path + InteractiveExecuteHandler.PATH,
+      'memcache_path': base_path + MemcachePageHandler.PATH,
     }
     values.update(template_values)
     directory = os.path.dirname(__file__)
@@ -166,11 +173,10 @@
   PATH = InteractivePageHandler.PATH + '/execute'
 
   def post(self):
-    self.response.headers['Content-Type'] = 'text/plain'
-
     save_stdout = sys.stdout
+    results_io = cStringIO.StringIO()
     try:
-      sys.stdout = self.response.out
+      sys.stdout = results_io
 
       code = self.request.get('code')
       code = code.replace("\r\n", "\n")
@@ -179,11 +185,211 @@
         compiled_code = compile(code, '<string>', 'exec')
         exec(compiled_code, globals())
       except Exception, e:
-        lines = traceback.format_exception(*sys.exc_info())
-        self.response.out.write(''.join(lines))
+        traceback.print_exc(file=results_io)
     finally:
       sys.stdout = save_stdout
 
+    results = results_io.getvalue()
+    self.generate('interactive-output.html', {'output': results})
+
+
+class MemcachePageHandler(BaseRequestHandler):
+  """Shows stats about memcache and query form to get values."""
+  PATH = '/memcache'
+
+  TYPES = ((str, str, 'String'),
+           (unicode, unicode, 'Unicode String'),
+           (bool, lambda value: MemcachePageHandler._ToBool(value), 'Boolean'),
+           (int, int, 'Integer'),
+           (long, long, 'Long Integer'),
+           (float, float, 'Float'))
+  DEFAULT_TYPESTR_FOR_NEW = 'String'
+
+  @staticmethod
+  def _ToBool(string_value):
+    """Convert string to boolean value.
+
+    Args:
+      string_value: A string.
+
+    Returns:
+      Boolean.  True if string_value is "true", False if string_value is
+      "false".  This is case-insensitive.
+
+    Raises:
+      ValueError: string_value not "true" or "false".
+    """
+    string_value_low = string_value.lower()
+    if string_value_low not in ('false', 'true'):
+      raise ValueError('invalid literal for boolean: %s' % string_value)
+    return string_value_low == 'true'
+
+  def _GetValueAndType(self, key):
+    """Fetch value from memcache and detect its type.
+
+    Args:
+      key: String
+
+    Returns:
+      (value, type), value is a Python object or None if the key was not set in
+      the cache, type is a string describing the type of the value.
+    """
+    try:
+      value = memcache.get(key)
+    except (pickle.UnpicklingError, AttributeError, EOFError, ImportError,
+            IndexError), e:
+      msg = 'Failed to retrieve value from cache: %s' % e
+      return msg, 'error'
+
+    if value is None:
+      return None, self.DEFAULT_TYPESTR_FOR_NEW
+
+    for typeobj, _, typestr in self.TYPES:
+      if isinstance(value, typeobj):
+        break
+    else:
+      typestr = 'pickled'
+      value = pprint.pformat(value, indent=2)
+
+    return value, typestr
+
+  def _SetValue(self, key, type_, value):
+    """Convert a string value and store the result in memcache.
+
+    Args:
+      key: String
+      type_: String, describing what type the value should have in the cache.
+      value: String, will be converted according to type_.
+
+    Returns:
+      Result of memcache.set(ket, converted_value).  True if value was set.
+
+    Raises:
+      ValueError: Value can't be converted according to type_.
+    """
+    for _, converter, typestr in self.TYPES:
+      if typestr == type_:
+        value = converter(value)
+        break
+    else:
+      raise ValueError('Type %s not supported.' % type_)
+    return memcache.set(key, value)
+
+  def get(self):
+    """Show template and prepare stats and/or key+value to display/edit."""
+    values = {'request': self.request,
+              'message': self.request.get('message')}
+
+    edit = self.request.get('edit')
+    key = self.request.get('key')
+    if edit:
+      key = edit
+      values['show_stats'] = False
+      values['show_value'] = False
+      values['show_valueform'] = True
+      values['types'] = [typestr for _, _, typestr in self.TYPES]
+    elif key:
+      values['show_stats'] = True
+      values['show_value'] = True
+      values['show_valueform'] = False
+    else:
+      values['show_stats'] = True
+      values['show_valueform'] = False
+      values['show_value'] = False
+
+    if key:
+      values['key'] = key
+      values['value'], values['type'] = self._GetValueAndType(key)
+      values['key_exists'] = values['value'] is not None
+
+      if values['type'] in ('pickled', 'error'):
+        values['writable'] = False
+      else:
+        values['writable'] = True
+
+    if values['show_stats']:
+      memcache_stats = memcache.get_stats()
+      values['stats'] = memcache_stats
+      try:
+        hitratio = memcache_stats['hits'] * 100 / (memcache_stats['hits']
+                                                   + memcache_stats['misses'])
+      except ZeroDivisionError:
+        hitratio = 0
+      values['hitratio'] = hitratio
+      delta_t = datetime.timedelta(seconds=memcache_stats['oldest_item_age'])
+      values['oldest_item_age'] = datetime.datetime.now() - delta_t
+
+    self.generate('memcache.html', values)
+
+  def _urlencode(self, query):
+    """Encode a dictionary into a URL query string.
+
+    In contrast to urllib this encodes unicode characters as UTF8.
+
+    Args:
+      query: Dictionary of key/value pairs.
+
+    Returns:
+      String.
+    """
+    return '&'.join('%s=%s' % (urllib.quote_plus(k.encode('utf8')),
+                               urllib.quote_plus(v.encode('utf8')))
+                    for k, v in query.iteritems())
+
+  def post(self):
+    """Handle modifying actions and/or redirect to GET page."""
+    next_param = {}
+
+    if self.request.get('action:flush'):
+      if memcache.flush_all():
+        next_param['message'] = 'Cache flushed, all keys dropped.'
+      else:
+        next_param['message'] = 'Flushing the cache failed.  Please try again.'
+
+    elif self.request.get('action:display'):
+      next_param['key'] = self.request.get('key')
+
+    elif self.request.get('action:edit'):
+      next_param['edit'] = self.request.get('key')
+
+    elif self.request.get('action:delete'):
+      key = self.request.get('key')
+      result = memcache.delete(key)
+      if result == memcache.DELETE_NETWORK_FAILURE:
+        next_param['message'] = ('ERROR: Network failure, key "%s" not deleted.'
+                                 % key)
+      elif result == memcache.DELETE_ITEM_MISSING:
+        next_param['message'] = 'Key "%s" not in cache.' % key
+      elif result == memcache.DELETE_SUCCESSFUL:
+        next_param['message'] = 'Key "%s" deleted.' % key
+      else:
+        next_param['message'] = ('Unknown return value.  Key "%s" might still '
+                                 'exist.' % key)
+
+    elif self.request.get('action:save'):
+      key = self.request.get('key')
+      value = self.request.get('value')
+      type_ = self.request.get('type')
+      next_param['key'] = key
+      try:
+        if self._SetValue(key, type_, value):
+          next_param['message'] = 'Key "%s" saved.' % key
+        else:
+          next_param['message'] = 'ERROR: Failed to save key "%s".' % key
+      except ValueError, e:
+        next_param['message'] = 'ERROR: Unable to encode value: %s' % e
+
+    elif self.request.get('action:cancel'):
+      next_param['key'] = self.request.get('key')
+
+    else:
+      next_param['message'] = 'Unknown action.'
+
+    next = self.request.path_url
+    if next_param:
+      next = '%s?%s' % (next, self._urlencode(next_param))
+    self.redirect(next)
+
 
 class DatastoreRequestHandler(BaseRequestHandler):
   """The base request handler for our datastore admin pages.
@@ -261,6 +467,31 @@
 
   PATH = '/datastore'
 
+  SCHEMA_CACHE_TIMEOUT = 60
+
+  def get_kinds(self, cache={}):
+    """Return sorted list of kind names the datastore knows about.
+
+    The list of kinds is cached for a short time.
+    """
+    server_software = os.environ['SERVER_SOFTWARE']
+    in_production = not server_software.startswith('Development')
+
+    if in_production and ('kinds' in cache):
+      if cache['kinds_timestamp'] + self.SCHEMA_CACHE_TIMEOUT > time.time():
+        return cache['kinds']
+      else:
+        del cache['kinds']
+    schema = datastore_admin.GetSchema()
+    kinds = []
+    for entity_proto in schema:
+      kinds.append(entity_proto.key().path().element_list()[-1].type())
+    kinds.sort()
+    if in_production:
+      cache['kinds'] = kinds
+      cache['kinds_timestamp'] = time.time()
+    return kinds
+
   def get(self):
     """Formats the results from execute_query() for datastore.html.
 
@@ -324,6 +555,7 @@
 
     values = {
       'request': self.request,
+      'kinds': self.get_kinds(),
       'kind': self.request.get('kind'),
       'order': self.request.get('order'),
       'headers': headers,
@@ -857,6 +1089,7 @@
     ('.*' + DatastoreBatchEditHandler.PATH, DatastoreBatchEditHandler),
     ('.*' + InteractivePageHandler.PATH, InteractivePageHandler),
     ('.*' + InteractiveExecuteHandler.PATH, InteractiveExecuteHandler),
+    ('.*' + MemcachePageHandler.PATH, MemcachePageHandler),
     ('.*' + ImageHandler.PATH, ImageHandler),
     ('.*', DefaultPageHandler),
   ], debug=_DEBUG)
--- a/thirdparty/google_appengine/google/appengine/ext/admin/templates/base.html	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/templates/base.html	Tue Sep 16 02:28:33 2008 +0000
@@ -9,7 +9,7 @@
     {% block head %}{% endblock %}
   </head>
   <body {% block bodyattributes %}{% endblock %}>
-    <div class="g-doc-1024">
+    <div class="g-doc">
     
     <div id="hd" class="g-section">
 
@@ -19,7 +19,7 @@
       </div>
       
       <div id="ae-appbar-lrg" class="g-section">
-        <h1>Development Console</h1>
+        <h1>{{ application_name }} Development Console</h1>
       </div>
       
     </div>
@@ -36,6 +36,7 @@
             <ul id="menu">
               <li><a href="{{ datastore_path }}">Datastore Viewer</a></li>
               <li><a href="{{ interactive_path }}">Interactive Console</a></li>
+              <li><a href="{{ memcache_path }}">Memcache Viewer</a></li>
             </ul>
         
           </div>
--- a/thirdparty/google_appengine/google/appengine/ext/admin/templates/css/ae.css	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/templates/css/ae.css	Tue Sep 16 02:28:33 2008 +0000
@@ -3,6 +3,11 @@
   font-size: 1.5em;
 }
 
+.g-doc {
+  width: auto;
+  margin: 0 10px;
+}
+
 /* Header Selectors */
 #ae-logo {
   margin-bottom: 0;
@@ -37,4 +42,4 @@
 #ae-content {
   padding-left: 1em;
   border-left: 3px solid #e5ecf9;
-}
\ No newline at end of file
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/templates/css/memcache.css	Tue Sep 16 02:28:33 2008 +0000
@@ -0,0 +1,54 @@
+.message {
+  color: red;
+  margin-bottom: 1em;
+}
+
+#flush_form {
+  display: inline;
+  margin-left: 2em;
+}
+
+#memcache_search {
+  margin-bottom: 2em;
+}
+
+#value_display {
+  border: 1px solid #c5d7ef;
+}
+
+#value_display_key {
+  text-align: left;
+  padding: 1ex;
+  background: #e5ecf9;
+}
+
+#value_display_value {
+  height: 20em;
+  margin: 0;
+  padding: 1ex;
+  background: #f9f9f9;
+  font-family: monospace;
+  overflow: auto;
+  white-space: -moz-pre-wrap;
+  white-space: -pre-wrap;
+  white-space: -o-pre-wrap;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+
+#memcache_edit th {
+  font-weight: bold;
+  padding: 2ex 3ex 0 0;
+}
+
+#memcache_edit td {
+  padding: 2ex 0 0 0;
+}
+
+#memcache_edit th#value_key {
+  vertical-align: top;
+}
+
+#memcache_edit div#value_key_text {
+  padding-top: 3px;
+}
--- a/thirdparty/google_appengine/google/appengine/ext/admin/templates/datastore.html	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/templates/datastore.html	Tue Sep 16 02:28:33 2008 +0000
@@ -8,18 +8,6 @@
   <script type="text/javascript">
   //<![CDATA[
 
-  function disableCreateButton() {
-    var input = document.getElementById("kind_input");
-    var button = document.getElementById("create_button");
-    if (input && button) {
-      if (input.value.length == 0) {
-        button.disabled = true;
-      } else {
-        button.disabled = false;
-      }
-    }
-  }
-
   {% if entities %}
   function checkAllEntities() {
     var allCheckBox = document.getElementById("allkeys");
@@ -71,11 +59,18 @@
   </div>
   {% endif %}
   
+  {% if kinds %}
   <form action="{{ request.path }}" method="get">
     <div id="datastore_search">
       <span class="field">
         <span class="name">Entity Kind:</span>
-        <span class="value"><input id="kind_input" name="kind" type="text" size="8" value="{{ kind|escape }}" onkeyup="disableCreateButton()" onkeydown="disableCreateButton()"/></span>
+        <span class="value">
+          <select name="kind" id="kind_input">
+            {% for a_kind in kinds %}
+            <option value="{{ a_kind|escape }}"{% ifequal a_kind kind %} selected="selected"{% endifequal %}>{{ a_kind|escape }}</option>
+            {% endfor %}
+          </select>
+        </span>
       </span>
       <span class="buttons">
         <input type="submit" value="List Entities"/>
@@ -83,6 +78,11 @@
       </span>
     </div>
   </form>
+  {% else %}
+  <div id="datastore_empty">
+    The datastore is empty.  You need to add data programatically before you can use this tool to view and edit it.
+  </div>
+  {% endif %}
 
   {% if entities %}
     <form action="{{ datastore_batch_edit_path }}" method="post">
@@ -146,7 +146,6 @@
   <script type="text/javascript">
   //<![CDATA[
 
-  disableCreateButton();
   updateDeleteButtonAndCheckbox();
   document.getElementById("kind_input").focus();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/templates/interactive-output.html	Tue Sep 16 02:28:33 2008 +0000
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+<title>Results</title>
+<style type="text/css">
+body {
+  margin: 0;
+  padding: 0;
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background-color: #f5f5f5;
+}
+#output {
+  font-family: monospace;
+  font-size: 10pt;
+  margin: 0;
+  padding: 0;
+  height: 100%;
+  width: 100%;
+  overflow: auto;
+  white-space: -moz-pre-wrap;
+  white-space: -pre-wrap;
+  white-space: -o-pre-wrap;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+</style>
+</head>
+<body>
+<pre id="output">{{ output|escape }}</pre>
+</body>
+</html>
--- a/thirdparty/google_appengine/google/appengine/ext/admin/templates/interactive.html	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/templates/interactive.html	Tue Sep 16 02:28:33 2008 +0000
@@ -10,6 +10,7 @@
   <style type="text/css">
 
   #console {
+    width: 100%;
     border-collapse: collapse;
   }
 
@@ -21,34 +22,43 @@
     padding-right: 25px;
   }
 
+  #code {
+    overflow: auto;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+  }
+
   #output {
-    height: 350px;
-    width: 100%;
-    border: 0px;
+    border: 1px solid silver;
+    background-color: #f5f5f5;
+    overflow: auto;
   }
 
   #code, #output {
-    font-family: "bogus font here", monospace;
+    font-family: monospace;
     font-size: 10pt;
+    height: 25em;
+    width: 100%;
+    padding: 0;
+    margin: 0;
   }
 
-  #code {
-    width: 100%;
-    border: 1px solid silver;
-    background-color: #f5f5f5;
-    padding: 0.5em;
+  #submitbutton {
+    text-align: center;
+    margin-top: 1em;
   }
-
   </style>
 {% endblock %}
 
 {% block body %}
 <h3>Interactive Console</h3>
-<table id="console">
-  <tr>
-    <td>
-      <form action="{{ interactive_execute_path }}" target="output" method="post">
-        <div><textarea id="code" name="code" rows="20" cols="80"># Say hello to the current user
+<form action="{{ interactive_execute_path }}" target="output" method="post">
+  <table id="console">
+    <tr>
+      <td>
+        <textarea id="code" name="code" wrap="off" rows="20" cols="80">from google.appengine.api import users
+
+# Say hello to the current user
 user = users.get_current_user()
 if user:
   nickname = user.nickname()
@@ -56,16 +66,39 @@
   nickname = "guest"
 print "Hello, " + nickname
 
-</textarea></div>
-        <div style="margin-top: 1em"><input type="submit" value="Run Program"/></div>
-      </form>
-    </td>
-    <td>
-      <iframe name="output" id="output"></iframe>
-    </td>
-  </tr>
-</table>
+</textarea>
+      </td>
+      <td>
+	<iframe name="output" id="output"></iframe>
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <div id="submitbutton"><input type="submit" value="Run Program"/></div>
+      </td>
+    </tr>
+  </table>
+</form>
+{% endblock %}
+
+{% block final %}
 <script type="text/javascript">
+//<![CDATA[
+var iframe = document.getElementById('output');
+var idoc = null;
+if (iframe.contentDocument) {
+  // DOM
+  idoc = iframe.contentDocument;
+} else if (iframe.contentWindow) {
+  // IE
+  idoc = iframe.contentWindow.document;
+}
+if (idoc) {
+  idoc.open();
+  idoc.write('<html><body style="background-color:#f5f5f5;margin:0;padding:0"><pre style="margin:0;padding:0;color:#888">Press "Run Program" to see the<br/>output of your code in this frame!</pre></body></html>');
+  idoc.close();
+}
 document.getElementById('code').focus();
+//]]>
 </script>
 {% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/admin/templates/memcache.html	Tue Sep 16 02:28:33 2008 +0000
@@ -0,0 +1,119 @@
+{% extends "base.html" %}
+
+{% block title %}{{ application_name }} Development Console - Memcache Viewer{% endblock %}
+
+{% block head %}
+  <style type="text/css">{% include "css/memcache.css" %}</style>
+{% endblock %}
+
+{% block breadcrumbs %}
+  <span class="item"><a href="">Memcache Viewer</a></span>
+{% endblock %}
+
+{% block body %}
+<h3>Memcache Viewer</h3>
+
+{% if message %}
+<div class="message">
+{{ message|escape }}
+</div>
+{% endif %}
+
+{% if show_stats %}
+<div id="stats">
+  <ul>
+    <li>Hit ratio: {{ hitratio }}% ({{ stats.hits }} hit{{ stats.hits|pluralize }} and {{ stats.misses }} miss{{ stats.misses|pluralize:"es" }})</li>
+    <li>Size of cache: {{ stats.items }} item{{ stats.items|pluralize }}, {{ stats.bytes|filesizeformat }}
+          <form id="flush_form" action="{{ request.path }}" method="post">
+            <input type="submit" name="action:flush" value="Flush Cache" onclick="return confirm('Are you sure you want to flush all keys from the cache?');"/>
+          </form>
+    </li>
+    <li>Cache contains items up to {{ oldest_item_age|timesince }} old.</li>
+  </ul>
+</div>
+
+<div id="memcache_search">
+  <form action="{{ request.path }}" method="post">
+    <span class="field">
+      <span class="name">Key:</span>
+      <span class="value"><input id="key_input" name="key" type="text" size="40" value="{{ key|escape }}"/></span>
+    </span>
+    <span class="buttons">
+      <input type="submit" name="action:display" value="Display"/>
+      <input type="submit" name="action:edit" value="Edit/Create"/>
+      <input type="submit" name="action:delete" value="Delete" onclick="return confirm('Are you sure you want to permanently delete this key?');"/>
+    </span>
+  </form>
+</div>
+{% endif %}
+
+{% if show_value %}
+{% if key_exists %}
+{% ifequal type "error" %}
+<div class="message">Error fetching {{ key|escape }}: {{ value|escape }}</div>
+{% else %}
+<div id="value_display">
+  <div id="value_display_key">"<b>{{ key|escape }}</b>" is a <b>{{ type|escape }}</b>:</div>
+  <pre id="value_display_value">{{ value|escape }}</pre>
+</div>
+{% endifequal %}
+{% else %}
+<div class="message">No such key: {{ key|escape }}</div>
+{% endif %}
+{% endif %}
+
+{% if show_valueform %}
+<div id="memcache_edit">
+  <form action="{{ request.path }}" method="post">
+    <table>
+      <tr>
+        <th>Key</th>
+        <td>
+          <input name="key" type="hidden" value="{{ key|escape }}"/>
+          {{ key|escape }}
+        </td>
+      </tr>
+      <tr>
+        <th>Type</th>
+        <td>
+          {% if key_exists %}
+          <input name="type" type="hidden" value="{{ type|escape }}"/>
+          {{ type|escape }}
+          {% else %}
+          <select name="type" size="1">
+            {% for typeopt in types %}
+            <option>{{ typeopt }}</option>
+            {% endfor %}
+          </select>
+          {% endif %}
+        </td>
+      </tr>
+      <tr>
+        <th id="value_key"><div id="value_key_text">Value</div></th>
+        <td>
+          <textarea id="value_input" name="value" cols="80" rows="20"{% if not writable %} readonly{% endif %}>{{ value|default_if_none:""|escape }}</textarea>
+        </td>
+      </tr>
+      <tr>
+        <th>&nbsp;</th>
+        <td>
+          {% if writable %}
+          <input type="submit" name="action:save" value="Save"/>
+          {% endif %}
+          <input type="submit" name="action:cancel" value="Cancel"/>
+        </td>
+      </tr>
+    </table>
+  </form>
+</div>
+{% endif %}
+
+{% endblock %}
+
+{% block final %}
+<script type="text/javascript">
+//<![CDATA[
+document.getElementById('key_input').focus();
+//]]>
+</script>
+{% endblock %}
--- a/thirdparty/google_appengine/google/appengine/ext/bulkload/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/bulkload/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -37,18 +37,15 @@
 See the Loader class for more information. Then, add a handler for it in your
 app.yaml, e.g.:
 
-  urlmap:
-  - regex: /load
-    handler:
-      type: 1
-      path: bulkload.py
-      requires_login: true
-      admin_only: true
+  handlers:
+  - url: /load
+    script: bulkload.py
+    login: admin
 
-Finally, deploy your app and run bulkload_client.py. For example, to load the
+Finally, deploy your app and run bulkloader.py. For example, to load the
 file people.csv into a dev_appserver running on your local machine:
 
-./bulkload_client.py --filename people.csv --kind Person --cookie ... \
+./bulkloader.py --filename people.csv --kind Person --cookie ... \
                      --url http://localhost:8080/load
 
 The kind parameter is used to look up the Loader instance that will be used.
@@ -110,6 +107,7 @@
 import sys
 import traceback
 import types
+import struct
 
 
 import google
@@ -139,15 +137,18 @@
 
 
 class Loader(object):
-  """ A base class for creating datastore entities from CSV input data.
+  """A base class for creating datastore entities from input data.
 
   To add a handler for bulk loading a new entity kind into your datastore,
   write a subclass of this class that calls Loader.__init__ from your
   class's __init__.
 
-  If you need to run extra code to convert entities from CSV, create new
-  properties, or otherwise modify the entities before they're inserted,
-  override HandleEntity.
+  If you need to run extra code to convert entities from the input
+  data, create new properties, or otherwise modify the entities before
+  they're inserted, override HandleEntity.
+
+  See the CreateEntity method for the creation of entities from the
+  (parsed) input data.
   """
 
   __loaders = {}
@@ -200,12 +201,12 @@
     """
     return self.__kind
 
-
-  def CreateEntity(self, values):
+  def CreateEntity(self, values, key_name=None):
     """ Creates an entity from a list of property values.
 
     Args:
-      values: list of str
+      values: list/tuple of str
+      key_name: if provided, the name for the (single) resulting Entity
 
     Returns:
       list of datastore.Entity
@@ -215,22 +216,23 @@
       the constructor, and passed through HandleEntity. They're ready to be
       inserted.
 
-    Raises an AssertionError if the number of values doesn't match the number
-    of properties in the properties map.
+    Raises:
+      AssertionError if the number of values doesn't match the number
+        of properties in the properties map.
     """
-    Validate(values, list)
+    Validate(values, (list, tuple))
     assert len(values) == len(self.__properties), (
       'Expected %d CSV columns, found %d.' %
       (len(self.__properties), len(values)))
 
-    entity = datastore.Entity(self.__kind)
+    entity = datastore.Entity(self.__kind, name=key_name)
     for (name, converter), val in zip(self.__properties, values):
       entity[name] = converter(val)
 
     entities = self.HandleEntity(entity)
 
     if entities is not None:
-      if not isinstance(entities, list):
+      if not isinstance(entities, (list, tuple)):
         entities = [entities]
 
       for entity in entities:
@@ -269,7 +271,18 @@
 
 
 class BulkLoad(webapp.RequestHandler):
-  """ A handler for bulk load requests.
+  """A handler for bulk load requests.
+
+  This class contains handlers for the bulkloading process. One for
+  GET to provide cookie information for the upload script, and one
+  handler for a POST request to upload the entities.
+
+  In the POST request, the body contains the data representing the
+  entities' property values. The original format was a sequences of
+  lines of comma-separated values (and is handled by the Load
+  method). The current (version 1) format is a binary format described
+  in the Tools and Libraries section of the documentation, and is
+  handled by the LoadV1 method).
   """
 
   def get(self):
@@ -283,8 +296,13 @@
     """ Handle a POST. Reads CSV data, converts to entities, and stores them.
     """
     self.response.headers['Content-Type'] = 'text/plain'
-    response, output = self.Load(self.request.get(constants.KIND_PARAM),
-                                 self.request.get(constants.CSV_PARAM))
+    version = self.request.headers.get('GAE-Uploader-Version', '0')
+    if version == '1':
+      kind = self.request.headers.get('GAE-Uploader-Kind')
+      response, output = self.LoadV1(kind, self.request.body)
+    else:
+      response, output = self.Load(self.request.get(constants.KIND_PARAM),
+                                   self.request.get(constants.CSV_PARAM))
     self.response.set_status(response)
     self.response.out.write(output)
 
@@ -369,8 +387,7 @@
             entities.extend(new_entities)
           output.append('done.')
         except:
-          exc_info = sys.exc_info()
-          stacktrace = traceback.format_exception(*exc_info)
+          stacktrace = traceback.format_exc()
           output.append('error:\n%s' % stacktrace)
           return (httplib.BAD_REQUEST, ''.join(output))
 
@@ -381,6 +398,69 @@
 
     return (httplib.OK, ''.join(output))
 
+  def LoadV1(self, kind, data):
+    """Parses version-1 format data, converts to entities, and stores them.
+
+    On error, fails fast. Returns a "bad request" HTTP response code and
+    includes the traceback in the output.
+
+    Args:
+      kind: a string containing the entity kind that this loader handles
+      data: a string containing the (v1 format) data to load
+
+    Returns:
+      tuple (response code, output) where:
+        response code: integer HTTP response code to return
+        output: string containing the HTTP response body
+    """
+    Validate(kind, basestring)
+    Validate(data, basestring)
+    output = []
+
+    try:
+      loader = Loader.RegisteredLoaders()[kind]
+    except KeyError:
+      output.append('Error: no Loader defined for kind %s.' % kind)
+      return httplib.BAD_REQUEST, ''.join(output)
+
+    entities = []
+
+    column_count, = struct.unpack_from('!i', data)
+
+    offset = 4
+
+    lengths_format = '!%di' % (column_count,)
+
+    while offset < len(data):
+      id_num = struct.unpack_from('!i', data, offset=offset)
+      offset += 4
+
+      key_name = 'i%010d' % id_num
+
+      value_lengths = struct.unpack_from(lengths_format, data, offset=offset)
+      offset += 4 * column_count
+
+      columns = struct.unpack_from(''.join('%ds' % length
+                                           for length in value_lengths), data,
+                                   offset=offset)
+      offset += sum(value_lengths)
+
+      try:
+        output.append('Loading key_name=%s... ' % key_name)
+        new_entities = loader.CreateEntity(columns, key_name=key_name)
+        if new_entities:
+          entities.extend(new_entities)
+        output.append('done.\n')
+      except:
+        stacktrace = traceback.format_exc()
+        output.append('error:\n%s' % stacktrace)
+        return httplib.BAD_REQUEST, ''.join(output)
+
+    for entity in entities:
+      datastore.Put(entity)
+
+    return httplib.OK, ''.join(output)
+
 
 def main(*loaders):
   """Starts bulk upload.
--- a/thirdparty/google_appengine/google/appengine/ext/db/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/db/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -2129,6 +2129,8 @@
 
     Note that the only permissible value for 'required' is True.
     """
+    if item_type is str:
+      item_type = basestring
     if not isinstance(item_type, type):
       raise TypeError('Item type should be a type object')
     if item_type not in _ALLOWED_PROPERTY_TYPES:
--- a/thirdparty/google_appengine/google/appengine/ext/gql/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/gql/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -248,7 +248,9 @@
                                               unused_values)
 
     if enumerated_queries:
-      logging.debug('Multiple Queries Bound: %s' % enumerated_queries)
+      logging.log(LOG_LEVEL,
+                  'Multiple Queries Bound: %s',
+                  enumerated_queries)
 
       for (query, enumerated_query) in zip(queries, enumerated_queries):
         query.update(enumerated_query)
@@ -1073,6 +1075,12 @@
     self.__bound_queries = bound_queries
     self.__orderings = orderings
 
+  def __str__(self):
+    res = 'MultiQuery: '
+    for query in self.__bound_queries:
+      res = '%s %s' % (res, str(query))
+    return res
+
   def Get(self, limit, offset=0):
     """Get results of the query with a limit on the number of results.
 
@@ -1082,7 +1090,7 @@
               the original query
 
     Returns:
-      An array of entities with at most "limit" entries (less if the query
+      A list of entities with at most "limit" entries (less if the query
       completes before reading limit values).
     """
     count = 1
@@ -1250,3 +1258,24 @@
             heapq.heappush(result_heap, popped_result)
 
     return IterateResults(results)
+
+  def Count(self, limit=None):
+    """Return the number of matched entities for this query.
+
+    Will return the de-duplicated count of results.  Will call the more
+    efficient Get() function if a limit is given.
+
+    Args:
+      limit: maximum number of entries to count (for any result > limit, return
+      limit).
+    Returns:
+      count of the number of entries returned.
+    """
+    if limit is None:
+      count = 0
+      for value in self.Run():
+        count += 1
+      return count
+    else:
+      return len(self.Get(limit))
+
--- a/thirdparty/google_appengine/google/appengine/ext/search/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/search/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -186,7 +186,7 @@
       text = cls._PUNCTUATION_REGEX.sub(' ', text)
       words = text.lower().split()
 
-      words = set(words)
+      words = set(unicode(w) for w in words)
 
       words -= cls._FULL_TEXT_STOP_WORDS
       for word in list(words):
@@ -251,7 +251,7 @@
         filter.set_op(datastore_pb.Query_Filter.EQUAL)
         prop = filter.add_property()
         prop.set_name(SearchableEntity._FULL_TEXT_INDEX_PROPERTY)
-        prop.mutable_value().set_stringvalue(keyword)
+        prop.mutable_value().set_stringvalue(unicode(keyword).encode('utf-8'))
 
     return pb
 
--- a/thirdparty/google_appengine/google/appengine/ext/webapp/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/webapp/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -154,12 +154,9 @@
     if self.charset:
       argument_name = argument_name.encode(self.charset)
 
-    try:
-      param_value = self.params.getall(argument_name)
-    except KeyError:
-      return default_value
+    param_value = self.params.getall(argument_name)
 
-    for i in range(len(param_value)):
+    for i in xrange(len(param_value)):
       if isinstance(param_value[i], cgi.FieldStorage):
         param_value[i] = param_value[i].value
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/ext/zipserve/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Serve static files from a zipfile.
+
+This is a solution for apps that want to serve 1000s of small static
+files while staying withing the 1000 file limit.
+
+The simplest use case is driven purely from the handlers section in
+app.yaml, e.g.:
+
+  - url: /images/.*
+    script: $PYTHON_LIB/google/appengine/ext/zipserve
+
+This would invoke a main() within zipserve/__init__.py.  This code
+would then take the URL path, and look for a .zip file under the first
+component of the path, in this case "images.zip" in the app's working
+directory.  If found, it will then serve any matching paths below that
+from the zip file.  In other words, /images/foo/icon.gif would map to
+foo/icon.gif in the zip file images.zip.
+
+You can also customize the behavior by adding a custom line to your
+WSGIApplication() invocation:
+
+  def main():
+    app = webapp.WSGIApplication(
+            [('/', MainPage),
+             ('/static/(.*)', zipserve.make_zip_handler('staticfiles.zip')),
+            ])
+
+You can pass max_age=N to the make_zip_handler() call to override the
+expiration time in seconds, which defaults to 600.
+
+To customize the behavior even more, you can subclass ZipHandler and
+override the get() method, or override it and call ServeFromZipFile()
+directly.
+
+Note that by default, a Cache-control is added that makes these pages
+cacheable even if they require authentication.  If this is not what
+you want, override ZipHandler.SetCachingHeaders().
+"""
+
+
+import email.Utils
+import logging
+import mimetypes
+import time
+import zipfile
+
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import util
+
+
+def make_zip_handler(zipfilename, max_age=None, public=None):
+  """Factory function to construct a custom ZipHandler instance.
+
+  Args:
+    zipfilename: The filename of a zipfile.
+    max_age: Optional expiration time; defaults to ZipHandler.MAX_AGE.
+    public: Optional public flag; defaults to ZipHandler.PUBLIC.
+
+  Returns:
+    A ZipHandler subclass.
+  """
+  class CustomZipHandler(ZipHandler):
+    def get(self, name):
+      self.ServeFromZipFile(self.ZIPFILENAME, name)
+    ZIPFILENAME = zipfilename
+    if max_age is not None:
+      MAX_AGE = max_age
+    if public is not None:
+      PUBLIC = public
+
+  return CustomZipHandler
+
+
+class ZipHandler(webapp.RequestHandler):
+  """Request handler serving static files from zipfiles."""
+
+  zipfile_cache = {}
+
+  def get(self, prefix, name):
+    """GET request handler.
+
+    Typically the arguments are passed from the matching groups in the
+    URL pattern passed to WSGIApplication().
+
+    Args:
+      prefix: The zipfilename without the .zip suffix.
+      name: The name within the zipfile.
+    """
+    self.ServeFromZipFile(prefix + '.zip', name)
+
+  def ServeFromZipFile(self, zipfilename, name):
+    """Helper for the GET request handler.
+
+    This serves the contents of file 'name' from zipfile
+    'zipfilename', logging a message and returning a 404 response if
+    either the zipfile cannot be opened or the named file cannot be
+    read from it.
+
+    Args:
+      zipfilename: The name of the zipfile.
+      name: The name within the zipfile.
+    """
+    zipfile_object = self.zipfile_cache.get(zipfilename)
+    if zipfile_object is None:
+      try:
+        zipfile_object = zipfile.ZipFile(zipfilename)
+      except (IOError, RuntimeError), err:
+        logging.error('Can\'t open zipfile %s: %s', zipfilename, err)
+        zipfile_object = ''
+      self.zipfile_cache[zipfilename] = zipfile_object
+    if zipfile_object == '':
+      self.error(404)
+      self.response.out.write('Not found')
+      return
+    try:
+      data = zipfile_object.read(name)
+    except (KeyError, RuntimeError), err:
+      self.error(404)
+      self.response.out.write('Not found')
+      return
+    content_type, encoding = mimetypes.guess_type(name)
+    if content_type:
+      self.response.headers['Content-Type'] = content_type
+    self.SetCachingHeaders()
+    self.response.out.write(data)
+
+  MAX_AGE = 600
+
+  PUBLIC = True
+
+  def SetCachingHeaders(self):
+    """Helper to set the caching headers.
+
+    Override this to customize the headers beyond setting MAX_AGE.
+    """
+    max_age = self.MAX_AGE
+    self.response.headers['Expires'] = email.Utils.formatdate(
+        time.time() + max_age, usegmt=True)
+    cache_control = []
+    if self.PUBLIC:
+      cache_control.append('public')
+    cache_control.append('max-age=%d' % max_age)
+    self.response.headers['Cache-Control'] = ', '.join(cache_control)
+
+
+def main():
+  """Main program.
+
+  This is invoked when this package is referenced from app.yaml.
+  """
+  application = webapp.WSGIApplication([('/([^/]+)/(.*)', ZipHandler)])
+  util.run_wsgi_app(application)
+
+
+if __name__ == '__main__':
+  main()
--- a/thirdparty/google_appengine/google/appengine/runtime/__init__.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/runtime/__init__.py	Tue Sep 16 02:28:33 2008 +0000
@@ -14,3 +14,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+
+"""Define the DeadlineExceededError exception."""
+
+
+
+try:
+  BaseException
+except NameError:
+  BaseException = Exception
+
+
+class DeadlineExceededError(BaseException):
+  """Exception raised when the request reaches its overall time limit.
+
+  Not to be confused with runtime.apiproxy_errors.DeadlineExceededError.
+  That one is raised when individual API calls take too long.
+  """
--- a/thirdparty/google_appengine/google/appengine/tools/appcfg.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/tools/appcfg.py	Tue Sep 16 02:28:33 2008 +0000
@@ -1567,7 +1567,7 @@
                       dest="verbose", help="Print all logs.")
     parser.add_option("-s", "--server", action="store", dest="server",
                       default="appengine.google.com",
-                      metavar="SERVER", help="The server to upload to.")
+                      metavar="SERVER", help="The server to connect to.")
     parser.add_option("-e", "--email", action="store", dest="email",
                       metavar="EMAIL", default=None,
                       help="The username to use. Will prompt if omitted.")
--- a/thirdparty/google_appengine/google/appengine/tools/dev_appserver.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/tools/dev_appserver.py	Tue Sep 16 02:28:33 2008 +0000
@@ -31,11 +31,7 @@
 """
 
 
-import os
-os.environ['TZ'] = 'UTC'
-import time
-if hasattr(time, 'tzset'):
-  time.tzset()
+from google.appengine.tools import os_compat
 
 import __builtin__
 import BaseHTTPServer
@@ -44,6 +40,7 @@
 import cgi
 import cgitb
 import dummy_thread
+import email.Utils
 import errno
 import httplib
 import imp
@@ -52,6 +49,7 @@
 import logging
 import mimetools
 import mimetypes
+import os
 import pickle
 import pprint
 import random
@@ -64,10 +62,11 @@
 import mimetypes
 import socket
 import sys
+import time
+import traceback
+import types
 import urlparse
 import urllib
-import traceback
-import types
 
 import google
 from google.pyglib import gexcept
@@ -1142,6 +1141,9 @@
           of packages, this will be None, which implies to look at __init__.py.
         pathname: String containing the full path of the module on disk.
         description: Tuple returned by imp.find_module().
+      However, in the case of an import using a path hook (e.g. a zipfile),
+      source_file will be a PEP-302-style loader object, pathname will be None,
+      and description will be a tuple filled with None values.
 
     Raises:
       ImportError exception if the requested module was found, but importing
@@ -1150,9 +1152,17 @@
       CouldNotFindModuleError exception if the request module could not even
       be found for import.
     """
-    try:
-      source_file, pathname, description = self._imp.find_module(submodule, search_path)
-    except ImportError:
+    if search_path is None:
+      search_path = [None] + sys.path
+    for path_entry in search_path:
+      result = self.FindPathHook(submodule, submodule_fullname, path_entry)
+      if result is not None:
+        source_file, pathname, description = result
+        if description == (None, None, None):
+          return result
+        else:
+          break
+    else:
       self.log('Could not find module "%s"', submodule_fullname)
       raise CouldNotFindModuleError()
 
@@ -1173,6 +1183,57 @@
 
     return source_file, pathname, description
 
+  def FindPathHook(self, submodule, submodule_fullname, path_entry):
+    """Helper for FindModuleRestricted to find a module in a sys.path entry.
+
+    Args:
+      submodule:
+      submodule_fullname:
+      path_entry: A single sys.path entry, or None representing the builtins.
+
+    Returns:
+      Either None (if nothing was found), or a triple (source_file, path_name,
+      description).  See the doc string for FindModuleRestricted() for the
+      meaning of the latter.
+    """
+    if path_entry is None:
+      if submodule_fullname in sys.builtin_module_names:
+        try:
+          result = self._imp.find_module(submodule)
+        except ImportError:
+          pass
+        else:
+          source_file, pathname, description = result
+          suffix, mode, file_type = description
+          if file_type == self._imp.C_BUILTIN:
+            return result
+      return None
+
+
+    if path_entry in sys.path_importer_cache:
+      importer = sys.path_importer_cache[path_entry]
+    else:
+      importer = None
+      for hook in sys.path_hooks:
+        try:
+          importer = hook(path_entry)
+          break
+        except ImportError:
+          pass
+      sys.path_importer_cache[path_entry] = importer
+
+    if importer is None:
+      try:
+        return self._imp.find_module(submodule, [path_entry])
+      except ImportError:
+        pass
+    else:
+      loader = importer.find_module(submodule)
+      if loader is not None:
+        return (loader, None, (None, None, None))
+
+    return None
+
   @Trace
   def LoadModuleRestricted(self,
                            submodule_fullname,
@@ -1186,9 +1247,11 @@
     Args:
       submodule_fullname: The fully qualified name of the module to find (e.g.,
         'foo.bar').
-      source_file: File-like object that contains the module's source code.
+      source_file: File-like object that contains the module's source code,
+        or a PEP-302-style loader object.
       pathname: String containing the full path of the module on disk.
-      description: Tuple returned by imp.find_module().
+      description: Tuple returned by imp.find_module(), or (None, None, None)
+        in case source_file is a PEP-302-style loader object.
 
     Returns:
       The new module.
@@ -1197,6 +1260,9 @@
       ImportError exception of the specified module could not be loaded for
       whatever reason.
     """
+    if description == (None, None, None):
+      return source_file.load_module(submodule_fullname)
+
     try:
       try:
         return self._imp.load_module(submodule_fullname,
@@ -1317,7 +1383,8 @@
 
     Returns:
       Tuple (pathname, search_path, submodule) where:
-        pathname: String containing the full path of the module on disk.
+        pathname: String containing the full path of the module on disk,
+          or None if the module wasn't loaded from disk (e.g. from a zipfile).
         search_path: List of paths that belong to the found package's search
           path or None if found module is not a package.
         submodule: The relative name of the submodule that's being imported.
@@ -1359,6 +1426,8 @@
   def get_source(self, fullname):
     """See PEP 302 extensions."""
     full_path, search_path, submodule = self.GetModuleInfo(fullname)
+    if full_path is None:
+      return None
     source_file = open(full_path)
     try:
       return source_file.read()
@@ -1369,6 +1438,8 @@
   def get_code(self, fullname):
     """See PEP 302 extensions."""
     full_path, search_path, submodule = self.GetModuleInfo(fullname)
+    if full_path is None:
+      return None
     source_file = open(full_path)
     try:
       source_code = source_file.read()
@@ -1513,7 +1584,7 @@
       if exc_value:
         import_error_message += ': ' + str(exc_value)
 
-      logging.error('Encountered error loading module "%s": %s',
+      logging.exception('Encountered error loading module "%s": %s',
                     module_fullname, import_error_message)
       missing_inits = FindMissingInitFiles(cgi_path, module_fullname)
       if missing_inits:
@@ -1662,7 +1733,12 @@
     os.environ.clear()
     os.environ.update(env)
     before_path = sys.path[:]
-    os.chdir(os.path.dirname(cgi_path))
+    cgi_dir = os.path.normpath(os.path.dirname(cgi_path))
+    root_path = os.path.normpath(os.path.abspath(root_path))
+    if cgi_dir.startswith(root_path + os.sep):
+      os.chdir(cgi_dir)
+    else:
+      os.chdir(root_path)
 
     hook = HardenedModulesHook(sys.modules)
     sys.meta_path = [hook]
@@ -1848,8 +1924,12 @@
     return path
 
 
-class StaticFileMimeTypeMatcher(object):
-  """Computes mime type based on URLMap and file extension.
+class StaticFileConfigMatcher(object):
+  """Keeps track of file/directory specific application configuration.
+
+  Specifically:
+  - Computes mime type based on URLMap and file extension.
+  - Decides on cache expiration time based on URLMap and default expiration.
 
   To determine the mime type, we first see if there is any mime-type property
   on each URLMap entry. If non is specified, we use the mimetypes module to
@@ -1859,7 +1939,8 @@
 
   def __init__(self,
                url_map_list,
-               path_adjuster):
+               path_adjuster,
+               default_expiration):
     """Initializer.
 
     Args:
@@ -1867,13 +1948,19 @@
         If empty or None, then we always use the mime type chosen by the
         mimetypes module.
       path_adjuster: PathAdjuster object used to adjust application file paths.
+      default_expiration: String describing default expiration time for browser
+        based caching of static files.  If set to None this disallows any
+        browser caching of static content.
     """
+    if default_expiration is not None:
+      self._default_expiration = appinfo.ParseExpiration(default_expiration)
+    else:
+      self._default_expiration = None
+
     self._patterns = []
 
     if url_map_list:
       for entry in url_map_list:
-        if entry.mime_type is None:
-          continue
         handler_type = entry.GetHandlerType()
         if handler_type not in (appinfo.STATIC_FILES, appinfo.STATIC_DIR):
           continue
@@ -1892,7 +1979,14 @@
         except re.error, e:
           raise InvalidAppConfigError('regex does not compile: %s' % e)
 
-        self._patterns.append((path_re, entry.mime_type))
+        if self._default_expiration is None:
+          expiration = 0
+        elif entry.expiration is None:
+          expiration = self._default_expiration
+        else:
+          expiration = appinfo.ParseExpiration(entry.expiration)
+
+        self._patterns.append((path_re, entry.mime_type, expiration))
 
   def GetMimeType(self, path):
     """Returns the mime type that we should use when serving the specified file.
@@ -1904,14 +1998,32 @@
       String containing the mime type to use. Will be 'application/octet-stream'
       if we have no idea what it should be.
     """
-    for (path_re, mime_type) in self._patterns:
-      the_match = path_re.match(path)
-      if the_match:
-        return mime_type
+    for (path_re, mime_type, expiration) in self._patterns:
+      if mime_type is not None:
+        the_match = path_re.match(path)
+        if the_match:
+          return mime_type
 
     filename, extension = os.path.splitext(path)
     return mimetypes.types_map.get(extension, 'application/octet-stream')
 
+  def GetExpiration(self, path):
+    """Returns the cache expiration duration to be users for the given file.
+
+    Args:
+      path: String containing the file's path on disk.
+
+    Returns:
+      Integer number of seconds to be used for browser cache expiration time.
+    """
+    for (path_re, mime_type, expiration) in self._patterns:
+      the_match = path_re.match(path)
+      if the_match:
+        return expiration
+
+    return self._default_expiration or 0
+
+
 
 def ReadDataFile(data_path, openfile=file):
   """Reads a file on disk, returning a corresponding HTTP status and data.
@@ -1950,18 +2062,18 @@
 
   def __init__(self,
                path_adjuster,
-               static_file_mime_type_matcher,
+               static_file_config_matcher,
                read_data_file=ReadDataFile):
     """Initializer.
 
     Args:
       path_adjuster: Instance of PathAdjuster to use for finding absolute
         paths of data files on disk.
-      static_file_mime_type_matcher: StaticFileMimeTypeMatcher object.
+      static_file_config_matcher: StaticFileConfigMatcher object.
       read_data_file: Used for dependency injection.
     """
     self._path_adjuster = path_adjuster
-    self._static_file_mime_type_matcher = static_file_mime_type_matcher
+    self._static_file_config_matcher = static_file_config_matcher
     self._read_data_file = read_data_file
 
   def Dispatch(self,
@@ -1974,10 +2086,16 @@
     """Reads the file and returns the response status and data."""
     full_path = self._path_adjuster.AdjustPath(path)
     status, data = self._read_data_file(full_path)
-    content_type = self._static_file_mime_type_matcher.GetMimeType(full_path)
+    content_type = self._static_file_config_matcher.GetMimeType(full_path)
+    expiration = self._static_file_config_matcher.GetExpiration(full_path)
 
     outfile.write('Status: %d\r\n' % status)
     outfile.write('Content-type: %s\r\n' % content_type)
+    if expiration:
+      outfile.write('Expires: %s\r\n'
+                    % email.Utils.formatdate(time.time() + expiration,
+                                             usegmt=True))
+      outfile.write('Cache-Control: public, max-age=%i\r\n' % expiration)
     outfile.write('\r\n')
     outfile.write(data)
 
@@ -2065,7 +2183,7 @@
     """
     self._modules = modules
     self._default_modules = self._modules.copy()
-
+    self._save_path_hooks = sys.path_hooks[:]
     self._modification_times = {}
 
   @staticmethod
@@ -2132,6 +2250,7 @@
     """Clear modules so that when request is run they are reloaded."""
     self._modules.clear()
     self._modules.update(self._default_modules)
+    sys.path_hooks[:] = self._save_path_hooks
 
 
 def _ClearTemplateCache(module_dict=sys.modules):
@@ -2145,7 +2264,8 @@
     template_module.template_cache.clear()
 
 
-def CreateRequestHandler(root_path, login_url, require_indexes=False):
+def CreateRequestHandler(root_path, login_url, require_indexes=False,
+                         static_caching=True):
   """Creates a new BaseHTTPRequestHandler sub-class for use with the Python
   BaseHTTPServer module's HTTP server.
 
@@ -2158,6 +2278,7 @@
     root_path: Path to the root of the application running on the server.
     login_url: Relative URL which should be used for handling user logins.
     require_indexes: True if index.yaml is read-only gospel; default False.
+    static_caching: True if browser caching of static files should be allowed.
 
   Returns:
     Sub-class of BaseHTTPRequestHandler.
@@ -2169,6 +2290,8 @@
   else:
     index_yaml_updater = dev_appserver_index.IndexYamlUpdater(root_path)
 
+  application_config_cache = AppConfigCache()
+
   class DevAppServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
     """Dispatches URLs using patterns from a URLMatcher, which is created by
     loading an application's configuration file. Executes CGI scripts in the
@@ -2189,6 +2312,8 @@
     module_dict = application_module_dict
     module_manager = ModuleManager(application_module_dict)
 
+    config_cache = application_config_cache
+
     def __init__(self, *args, **kwargs):
       """Initializer.
 
@@ -2255,7 +2380,9 @@
         implicit_matcher = CreateImplicitMatcher(self.module_dict,
                                                  root_path,
                                                  login_url)
-        config, explicit_matcher = LoadAppConfig(root_path, self.module_dict)
+        config, explicit_matcher = LoadAppConfig(root_path, self.module_dict,
+                                                 cache=self.config_cache,
+                                                 static_caching=static_caching)
         env_dict['CURRENT_VERSION_ID'] = config.version + ".1"
         env_dict['APPLICATION_ID'] = config.application
         dispatcher = MatcherDispatcher(login_url,
@@ -2353,10 +2480,12 @@
 def CreateURLMatcherFromMaps(root_path,
                              url_map_list,
                              module_dict,
+                             default_expiration,
                              create_url_matcher=URLMatcher,
                              create_cgi_dispatcher=CGIDispatcher,
                              create_file_dispatcher=FileDispatcher,
-                             create_path_adjuster=PathAdjuster):
+                             create_path_adjuster=PathAdjuster,
+                             normpath=os.path.normpath):
   """Creates a URLMatcher instance from URLMap.
 
   Creates all of the correct URLDispatcher instances to handle the various
@@ -2370,6 +2499,9 @@
     module_dict: Dictionary in which application-loaded modules should be
       preserved between requests. This dictionary must be separate from the
       sys.modules dictionary.
+    default_expiration: String describing default expiration time for browser
+      based caching of static files.  If set to None this disallows any
+      browser caching of static content.
     create_url_matcher, create_cgi_dispatcher, create_file_dispatcher,
     create_path_adjuster: Used for dependency injection.
 
@@ -2380,7 +2512,7 @@
   path_adjuster = create_path_adjuster(root_path)
   cgi_dispatcher = create_cgi_dispatcher(module_dict, root_path, path_adjuster)
   file_dispatcher = create_file_dispatcher(path_adjuster,
-      StaticFileMimeTypeMatcher(url_map_list, path_adjuster))
+      StaticFileConfigMatcher(url_map_list, path_adjuster, default_expiration))
 
   for url_map in url_map_list:
     admin_only = url_map.login == appinfo.LOGIN_ADMIN
@@ -2406,7 +2538,8 @@
         backref = r'\\1'
       else:
         backref = r'\1'
-      path = os.path.normpath(path) + os.path.sep + backref
+      path = (normpath(path).replace('\\', '\\\\') +
+              os.path.sep + backref)
 
     url_matcher.AddURL(regex,
                        dispatcher,
@@ -2416,8 +2549,26 @@
   return url_matcher
 
 
+class AppConfigCache(object):
+  """Cache used by LoadAppConfig.
+
+  If given to LoadAppConfig instances of this class are used to cache contents
+  of the app config (app.yaml or app.yml) and the Matcher created from it.
+
+  Code outside LoadAppConfig should treat instances of this class as opaque
+  objects and not access its members.
+  """
+
+  path = None
+  mtime = None
+  config = None
+  matcher = None
+
+
 def LoadAppConfig(root_path,
                   module_dict,
+                  cache=None,
+                  static_caching=True,
                   read_app_config=ReadAppConfig,
                   create_matcher=CreateURLMatcherFromMaps):
   """Creates a Matcher instance for an application configuration file.
@@ -2430,22 +2581,45 @@
     module_dict: Dictionary in which application-loaded modules should be
       preserved between requests. This dictionary must be separate from the
       sys.modules dictionary.
-    read_url_map, create_matcher: Used for dependency injection.
+    cache: Instance of AppConfigCache or None.
+    static_caching: True if browser caching of static files should be allowed.
+    read_app_config, create_matcher: Used for dependency injection.
 
   Returns:
      tuple: (AppInfoExternal, URLMatcher)
   """
-
   for appinfo_path in [os.path.join(root_path, 'app.yaml'),
                        os.path.join(root_path, 'app.yml')]:
 
     if os.path.isfile(appinfo_path):
+      if cache is not None:
+        mtime = os.path.getmtime(appinfo_path)
+        if cache.path == appinfo_path and cache.mtime == mtime:
+          return (cache.config, cache.matcher)
+
+        cache.config = cache.matcher = cache.path = None
+        cache.mtime = mtime
+
       try:
         config = read_app_config(appinfo_path, appinfo.LoadSingleAppInfo)
 
+        if static_caching:
+          if config.default_expiration:
+            default_expiration = config.default_expiration
+          else:
+            default_expiration = '0'
+        else:
+          default_expiration = None
+
         matcher = create_matcher(root_path,
                                  config.handlers,
-                                 module_dict)
+                                 module_dict,
+                                 default_expiration)
+
+        if cache is not None:
+          cache.path = appinfo_path
+          cache.config = config
+          cache.matcher = matcher
 
         return (config, matcher)
       except gexcept.AbstractMethod:
@@ -2486,6 +2660,8 @@
   show_mail_body = config.get('show_mail_body', False)
   remove = config.get('remove', os.remove)
 
+  os.environ['APPLICATION_ID'] = app_id
+
   if clear_datastore:
     for path in (datastore_path, history_path):
       if os.path.lexists(path):
@@ -2616,6 +2792,7 @@
                  template_dir,
                  serve_address='',
                  require_indexes=False,
+                 static_caching=True,
                  python_path_list=sys.path):
   """Creates an new HTTPServer for an application.
 
@@ -2628,6 +2805,7 @@
       are stored.
     serve_address: Address on which the server should serve.
     require_indexes: True if index.yaml is read-only gospel; default False.
+    static_caching: True if browser caching of static files should be allowed.
     python_path_list: Used for dependency injection.
 
   Returns:
@@ -2641,7 +2819,7 @@
                             template_dir])
 
   handler_class = CreateRequestHandler(absolute_root_path, login_url,
-                                       require_indexes)
+                                       require_indexes, static_caching)
 
   if absolute_root_path not in python_path_list:
     python_path_list.insert(0, absolute_root_path)
--- a/thirdparty/google_appengine/google/appengine/tools/dev_appserver_main.py	Tue Sep 16 01:18:49 2008 +0000
+++ b/thirdparty/google_appengine/google/appengine/tools/dev_appserver_main.py	Tue Sep 16 02:28:33 2008 +0000
@@ -54,18 +54,17 @@
   --debug_imports            Enables debug logging for module imports, showing
                              search paths used for finding modules and any
                              errors encountered during the import process.
+  --disable_static_caching   Never allow the browser to cache static files.
+                             (Default enable if expiration set in app.yaml)
 """
 
 
 
-import os
-os.environ['TZ'] = 'UTC'
-import time
-if hasattr(time, 'tzset'):
-  time.tzset()
+from google.appengine.tools import os_compat
 
 import getopt
 import logging
+import os
 import sys
 import traceback
 import tempfile
@@ -95,6 +94,7 @@
 ARG_SMTP_PASSWORD = 'smtp_password'
 ARG_SMTP_PORT = 'smtp_port'
 ARG_SMTP_USER = 'smtp_user'
+ARG_STATIC_CACHING = 'static_caching'
 ARG_TEMPLATE_DIR = 'template_dir'
 
 
@@ -122,6 +122,7 @@
   ARG_ADDRESS: 'localhost',
   ARG_ADMIN_CONSOLE_SERVER: DEFAULT_ADMIN_CONSOLE_SERVER,
   ARG_ADMIN_CONSOLE_HOST: None,
+  ARG_STATIC_CACHING: True,
 }
 
 
@@ -167,6 +168,7 @@
         'debug',
         'debug_imports',
         'enable_sendmail',
+        'disable_static_caching',
         'show_mail_body',
         'help',
         'history_path=',
@@ -252,6 +254,9 @@
     if option == '--admin_console_host':
       option_dict[ARG_ADMIN_CONSOLE_HOST] = value
 
+    if option == '--disable_static_caching':
+      option_dict[ARG_STATIC_CACHING] = False
+
   return args, option_dict
 
 
@@ -290,6 +295,7 @@
   template_dir = option_dict[ARG_TEMPLATE_DIR]
   serve_address = option_dict[ARG_ADDRESS]
   require_indexes = option_dict[ARG_REQUIRE_INDEXES]
+  static_caching = option_dict[ARG_STATIC_CACHING]
 
   logging.basicConfig(
     level=log_level,
@@ -327,7 +333,8 @@
                                            port,
                                            template_dir,
                                            serve_address=serve_address,
-                                           require_indexes=require_indexes)
+                                           require_indexes=require_indexes,
+                                           static_caching=static_caching)
 
   logging.info('Running application %s on port %d: http://%s:%d',
                config.application, port, serve_address, port)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/tools/os_compat.py	Tue Sep 16 02:28:33 2008 +0000
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""OS cross-platform compatibility tweaks.
+
+This module will, on import, change some parts of the running evironment so
+that other modules do not need special handling when running on different
+operating systems, such as Linux/Mac OSX/Windows.
+
+Some of these changes must be done before other modules are imported, so
+always import this module first.
+"""
+
+
+import os
+os.environ['TZ'] = 'UTC'
+import time
+if hasattr(time, 'tzset'):
+  time.tzset()
+
+import __builtin__
+
+
+if 'WindowsError' in __builtin__.__dict__:
+  WindowsError = WindowsError
+else:
+  class WindowsError(Exception):
+    """A fake Windows Error exception which should never be thrown."""
+