App Engine Python SDK version 1.8.9

git-svn-id: http://googleappengine.googlecode.com/svn/trunk/python@410 80f5ef21-4148-0410-bacc-cfb02402ada8
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 54e774c..5db6916 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -3,6 +3,57 @@
 
 App Engine SDK - Release Notes
 
+Version 1.8.9
+
+All
+==============================
+- Renamed appcfg start to appcfg start_module_version and stop to
+  stop_module_version.
+- Fixed an issue with ID allocation collisions in the Datastore.
+  https://code.google.com/p/googleappengine/issues/detail?id=10134
+- Fixed an issue with Cloud Storage calls failing in the dev_appserver.
+    https://code.google.com/p/googleappengine/issues/detail?id=10181
+    https://code.google.com/p/googleappengine/issues/detail?id=10185
+- Fixed an issue with the dev_appserver not starting on Windows when using
+  versions of Python earlier than 2.7.2.
+    https://code.google.com/p/googleappengine/issues/detail?id=10363
+
+Python
+==============================
+- Deprecated start_module, start_module_async, stop_module, stop_module_async
+  get_modules_async, get_versions_async, get_default_version_async,
+  get_num_instances_async, get_hostname_async and added start_version,
+  start_version_async, stop_version, stop_version_async as replacements in the
+  Modules API.
+- Added verbose_name support for ComputedProperty in NDB.
+    https://code.google.com/p/appengine-ndb-experiment/issues/detail?id=239
+- Fixed an issue with deffered.defer not waiting for async operations.
+    https://code.google.com/p/appengine-ndb-experiment/issues/detail?id=238
+- Fixed an issue with LocalStructureProperty not handling None value in NDB.
+    https://code.google.com/p/appengine-ndb-experiment/issues/detail?id=233
+- Fixed an issue NDB not working with sqlite.
+    https://code.google.com/p/googleappengine/issues/detail?id=8381
+- Fixed an issue with debug not working with PyDev in 1.8.8.
+    https://code.google.com/p/googleappengine/issues/detail?id=10390
+- Fixed an issue with object properties not being retrieved in NDB.
+    https://code.google.com/p/googleappengine/issues/detail?id=10467
+
+PHP
+=============================
+- Renamed startModule, stopModule to startVersion, stopVersion in the Modules
+  API.
+- Added support for the ftp extension.
+- Added support for the zip extension.
+- Added support for the gethostname() function.
+- A proper "billing required" message is now given when users attempt to use
+  Sockets API on free apps.
+- Fixed an issue with header keys and values having whitespaces preventing
+  responses from being compressed.
+- Fixed an issue with content-type and metadata not being updated when renaming
+  Google Cloud Storage objects.
+- Fixed an issue with Cloud Storage rename requests failing when an object has
+  a space in its name.
+
 Version 1.8.8
 
 All
diff --git a/VERSION b/VERSION
index b2e2cd0..99302d0 100644
--- a/VERSION
+++ b/VERSION
@@ -1,5 +1,5 @@
-release: "1.8.8"
-timestamp: 1383722570
+release: "1.8.9"
+timestamp: 1386206330
 api_versions: ['1']
 supported_api_versions:
   python:
diff --git a/_php_runtime.py b/_php_runtime.py
index 566da19..483d3ed 100644
--- a/_php_runtime.py
+++ b/_php_runtime.py
@@ -95,6 +95,9 @@
     os.path.join(_DIR_PATH, 'lib', 'fancy_urllib'),
     os.path.join(_DIR_PATH, 'lib', 'ipaddr'),
     os.path.join(_DIR_PATH, 'lib', 'yaml-3.10'),
+    os.path.join(_DIR_PATH, 'lib', 'rsa'),
+    os.path.join(_DIR_PATH, 'lib', 'pyasn1'),
+    os.path.join(_DIR_PATH, 'lib', 'pyasn1_modules'),
     ]
 
 
@@ -108,6 +111,7 @@
 
 
     os.path.join(_DIR_PATH, 'lib', 'django-1.4'),
+    os.path.join(_DIR_PATH, 'lib', 'endpoints-1.0'),
     os.path.join(_DIR_PATH, 'lib', 'jinja2-2.6'),
     os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
     os.path.join(_DIR_PATH, 'lib', 'PyAMF-0.6.1'),
diff --git a/_python_runtime.py b/_python_runtime.py
index 566da19..483d3ed 100644
--- a/_python_runtime.py
+++ b/_python_runtime.py
@@ -95,6 +95,9 @@
     os.path.join(_DIR_PATH, 'lib', 'fancy_urllib'),
     os.path.join(_DIR_PATH, 'lib', 'ipaddr'),
     os.path.join(_DIR_PATH, 'lib', 'yaml-3.10'),
+    os.path.join(_DIR_PATH, 'lib', 'rsa'),
+    os.path.join(_DIR_PATH, 'lib', 'pyasn1'),
+    os.path.join(_DIR_PATH, 'lib', 'pyasn1_modules'),
     ]
 
 
@@ -108,6 +111,7 @@
 
 
     os.path.join(_DIR_PATH, 'lib', 'django-1.4'),
+    os.path.join(_DIR_PATH, 'lib', 'endpoints-1.0'),
     os.path.join(_DIR_PATH, 'lib', 'jinja2-2.6'),
     os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
     os.path.join(_DIR_PATH, 'lib', 'PyAMF-0.6.1'),
diff --git a/api_server.py b/api_server.py
index b15365d..2af168b 100644
--- a/api_server.py
+++ b/api_server.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/appcfg.py b/appcfg.py
index b15365d..2af168b 100644
--- a/appcfg.py
+++ b/appcfg.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/backends_conversion.py b/backends_conversion.py
index b15365d..2af168b 100644
--- a/backends_conversion.py
+++ b/backends_conversion.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/bulkload_client.py b/bulkload_client.py
index b15365d..2af168b 100644
--- a/bulkload_client.py
+++ b/bulkload_client.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/bulkloader.py b/bulkloader.py
index b15365d..2af168b 100644
--- a/bulkloader.py
+++ b/bulkloader.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/demos/php/guestbook/app.yaml b/demos/php/guestbook/app.yaml
index 42e8d26..9261f36 100644
--- a/demos/php/guestbook/app.yaml
+++ b/demos/php/guestbook/app.yaml
@@ -2,7 +2,6 @@
 version: 1
 runtime: php
 api_version: 1
-threadsafe: no
 
 handlers:
 - url: .*
diff --git a/demos/php/minishell/app.yaml b/demos/php/minishell/app.yaml
index dfe57b8..dcbfacd 100644
--- a/demos/php/minishell/app.yaml
+++ b/demos/php/minishell/app.yaml
@@ -2,7 +2,6 @@
 version: 1
 runtime: php
 api_version: 1
-threadsafe: false
 
 handlers:
 - url: /static
diff --git a/demos/php/sendpage/app.yaml b/demos/php/sendpage/app.yaml
new file mode 100644
index 0000000..81a8013
--- /dev/null
+++ b/demos/php/sendpage/app.yaml
@@ -0,0 +1,13 @@
+application: sendpage
+version: 1
+runtime: php
+api_version: 1
+
+handlers:
+- url: /sendpage
+  script: sendpage.php
+  login: admin
+
+- url: /.*
+  script: index.php
+  login: admin
\ No newline at end of file
diff --git a/demos/php/sendpage/index.php b/demos/php/sendpage/index.php
new file mode 100644
index 0000000..6e6abc9
--- /dev/null
+++ b/demos/php/sendpage/index.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * 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.
+ */
+require_once 'google/appengine/api/taskqueue/PushTask.php';
+require_once 'google/appengine/api/users/User.php';
+require_once 'google/appengine/api/users/UserService.php';
+
+use \google\appengine\api\taskqueue\PushTask;
+use \google\appengine\api\users\User;
+use \google\appengine\api\users\UserService;
+
+$user = UserService::getCurrentUser();
+$email = $user->getEmail();
+?>
+<html>
+  <head><title>Send Site</title></head>
+  <body>
+    <p>Enter the a URL.
+    <form method="POST">
+      URL: <input type="text" size="50" name="url">
+      <input
+        type="submit"
+        value="Send to <?php echo htmlentities($email, ENT_QUOTES) ?>">
+    </form>
+    <?php
+      if (isset($_POST['url'])) {
+        $task = new PushTask(
+            '/sendpage',
+            ['url' => $_POST['url'], 'email' => $email]);
+        $task->add();
+        echo "<b>Sending url!</b>";
+      }
+    ?>
+  </body>
+</html>
diff --git a/demos/php/sendpage/sendpage.php b/demos/php/sendpage/sendpage.php
new file mode 100644
index 0000000..f5d76db
--- /dev/null
+++ b/demos/php/sendpage/sendpage.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * 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.
+ */
+require_once 'google/appengine/api/mail/Message.php';
+
+use google\appengine\api\mail\Message;
+
+$url = $_POST['url'];
+$page_content = file_get_contents($url);
+
+$mail_options = [
+    "sender" => $_POST['email'],
+    "to" => $_POST['email'],
+    "subject" => "Content of $url",
+    "htmlBody" => $page_content
+];
+
+$message = new Message($mail_options);
+$message->send();
\ No newline at end of file
diff --git a/dev_appserver.py b/dev_appserver.py
index 566da19..483d3ed 100644
--- a/dev_appserver.py
+++ b/dev_appserver.py
@@ -95,6 +95,9 @@
     os.path.join(_DIR_PATH, 'lib', 'fancy_urllib'),
     os.path.join(_DIR_PATH, 'lib', 'ipaddr'),
     os.path.join(_DIR_PATH, 'lib', 'yaml-3.10'),
+    os.path.join(_DIR_PATH, 'lib', 'rsa'),
+    os.path.join(_DIR_PATH, 'lib', 'pyasn1'),
+    os.path.join(_DIR_PATH, 'lib', 'pyasn1_modules'),
     ]
 
 
@@ -108,6 +111,7 @@
 
 
     os.path.join(_DIR_PATH, 'lib', 'django-1.4'),
+    os.path.join(_DIR_PATH, 'lib', 'endpoints-1.0'),
     os.path.join(_DIR_PATH, 'lib', 'jinja2-2.6'),
     os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
     os.path.join(_DIR_PATH, 'lib', 'PyAMF-0.6.1'),
diff --git a/download_appstats.py b/download_appstats.py
index b15365d..2af168b 100644
--- a/download_appstats.py
+++ b/download_appstats.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/endpointscfg.py b/endpointscfg.py
index b15365d..2af168b 100644
--- a/endpointscfg.py
+++ b/endpointscfg.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/gen_protorpc.py b/gen_protorpc.py
index b15365d..2af168b 100644
--- a/gen_protorpc.py
+++ b/gen_protorpc.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/google/appengine/api/app_identity/app_identity_keybased_stub.py b/google/appengine/api/app_identity/app_identity_keybased_stub.py
new file mode 100644
index 0000000..fab7db6
--- /dev/null
+++ b/google/appengine/api/app_identity/app_identity_keybased_stub.py
@@ -0,0 +1,233 @@
+#!/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.
+#
+
+
+"""App identity stub service implementation.
+
+This service behaves the same as the production service, except using
+a private key specified when starting dev_appserver.py.
+"""
+
+from __future__ import with_statement
+
+
+
+
+
+
+
+
+
+
+import base64
+import json
+import os
+import threading
+import time
+import urllib
+
+from pyasn1.codec.der import decoder
+from pyasn1_modules.rfc2459 import Certificate
+import rsa
+
+from google.appengine.api import urlfetch
+from google.appengine.api.app_identity import app_identity_service_pb
+from google.appengine.api.app_identity import app_identity_stub
+from google.appengine.runtime import apiproxy_errors
+
+
+def BitStringToByteString(bs):
+  """Convert a pyasn1.type.univ.BitString object to a string of bytes."""
+  def BitsToInt(bits):
+    return sum(v * (2 ** (7 - j)) for j, v in enumerate(bits))
+  return str(bytearray([BitsToInt(bs[i:i + 8]) for i in range(0, len(bs), 8)]))
+
+
+class KeyBasedAppIdentityServiceStub(app_identity_stub.AppIdentityServiceStub):
+  """A stub for the AppIdentityService API for offline development.
+
+  Provides stub functions which allow a developer to test integration before
+  deployment.
+  """
+  THREADSAFE = True
+
+  def __init__(self, service_name='app_identity_service',
+               email_address=None, private_key_path=None):
+    """Constructor."""
+    super(KeyBasedAppIdentityServiceStub, self).__init__(service_name)
+    self.__x509_init_lock = threading.Lock()
+    self.__access_token_cache_lock = threading.Lock()
+    self.__default_gcs_bucket_name = (
+        app_identity_stub.APP_DEFAULT_GCS_BUCKET_NAME)
+    if email_address is None:
+      raise ValueError('Email address for service account must be specified.')
+    self.__email_address = email_address
+    if private_key_path is None:
+      raise ValueError('Path to the private key must be specified '
+                       'if an email address is specified.')
+    if not os.path.exists(private_key_path):
+      raise ValueError(private_key_path + ' not found.')
+    if private_key_path.endswith('.p12'):
+
+      raise ValueError(('Please convert .p12 format to .pem format: '
+                        'cat %s | openssl pkcs12 -nodes -nocerts -passin '
+                        'pass:notasecret | openssl rsa > %s') % (
+                            private_key_path,
+                            '%s.pem' % os.path.splitext(private_key_path)[0]))
+    self.__private_key = rsa.key.PrivateKey.load_pkcs1(
+        file(private_key_path, 'rb').read(), 'PEM')
+    self.__access_token_cache = {}
+    self.__x509 = None
+    self.__signing_key = None
+
+  def _PopulateX509(self):
+    with self.__x509_init_lock:
+      if not self.__x509:
+        url = ('https://www.googleapis.com/service_accounts/v1/metadata/x509/%s'
+               % urllib.unquote_plus(self.__email_address))
+        resp = urlfetch.fetch(
+            url=url,
+            validate_certificate=True,
+            method=urlfetch.GET)
+        if resp.status_code != 200:
+          raise apiproxy_errors.ApplicationError(
+              app_identity_service_pb.AppIdentityServiceError.UNKNOWN_ERROR,
+              'Unable to load X509 cert: %s Response code: %i, Content: %s' % (
+                  url, resp.status_code, resp.content))
+
+        msg = 'test'
+        sig = rsa.pkcs1.sign(msg, self.__private_key, 'SHA-256')
+
+
+
+
+        for signing_key, x509 in json.loads(resp.content).items():
+          der = rsa.pem.load_pem(x509, 'CERTIFICATE')
+          asn1_cert, _ = decoder.decode(der, asn1Spec=Certificate())
+
+          key_bitstring = (
+              asn1_cert['tbsCertificate']
+              ['subjectPublicKeyInfo']
+              ['subjectPublicKey'])
+          key_bytearray = BitStringToByteString(key_bitstring)
+
+          pub = rsa.PublicKey.load_pkcs1(key_bytearray, 'DER')
+          try:
+            if rsa.pkcs1.verify(msg, sig, pub):
+              self.__x509 = x509
+              self.__signing_key = signing_key
+              return
+          except rsa.pkcs1.VerificationError:
+            pass
+
+
+        raise apiproxy_errors.ApplicationError(
+            app_identity_service_pb.AppIdentityServiceError.UNKNOWN_ERROR,
+            'Unable to find matching X509 cert for private key: %s' % url)
+
+  def _Dynamic_SignForApp(self, request, response):
+    """Implementation of AppIdentityService::SignForApp."""
+    self._PopulateX509()
+    response.set_signature_bytes(rsa.pkcs1.sign(
+        request.bytes_to_sign(), self.__private_key, 'SHA-256'))
+    response.set_key_name(self.__signing_key)
+
+  def _Dynamic_GetPublicCertificatesForApp(self, request, response):
+    """Implementation of AppIdentityService::GetPublicCertificatesForApp."""
+    self._PopulateX509()
+    cert = response.add_public_certificate_list()
+    cert.set_key_name(self.__signing_key)
+    cert.set_x509_certificate_pem(self.__x509)
+
+  def _Dynamic_GetServiceAccountName(self, request, response):
+    """Implementation of AppIdentityService::GetServiceAccountName."""
+    response.set_service_account_name(self.__email_address)
+
+  def _Dynamic_GetDefaultGcsBucketName(self, unused_request, response):
+    """Implementation of AppIdentityService::GetDefaultGcsBucketName."""
+    response.set_default_gcs_bucket_name(self.__default_gcs_bucket_name)
+
+  def SetDefaultGcsBucketName(self, default_gcs_bucket_name):
+    if default_gcs_bucket_name:
+      self.__default_gcs_bucket_name = default_gcs_bucket_name
+    else:
+      self.__default_gcs_bucket_name = (
+          app_identity_stub.APP_DEFAULT_GCS_BUCKET_NAME)
+
+  def _Dynamic_GetAccessToken(self, request, response):
+    """Implementation of AppIdentityService::GetAccessToken.
+
+    This API requires internet access.
+
+    Raises:
+      apiproxy_errors.ApplicationError: If unexpected response from
+                                        Google server.
+    """
+    scope = ' '.join(request.scope_list())
+    with self.__access_token_cache_lock:
+      rv = self.__access_token_cache.get(scope, None)
+    now = int(time.time())
+
+
+
+    if not (rv and rv['expires'] > (now + 60)):
+
+      assertion_input = '%s.%s' % (
+          base64.urlsafe_b64encode(json.dumps({
+              'alg': 'RS256',
+              'typ': 'JWT'
+          }).encode('UTF-8')).rstrip('='),
+          base64.urlsafe_b64encode(json.dumps({
+              'iss': self.__email_address,
+              'scope': scope,
+              'aud': 'https://accounts.google.com/o/oauth2/token',
+              'exp': now + (60 * 60),
+              'iat': now
+          }).encode('UTF-8')).rstrip('='))
+
+
+      signature = base64.urlsafe_b64encode(rsa.pkcs1.sign(
+          assertion_input, self.__private_key, 'SHA-256')).rstrip('=')
+
+
+      message = urllib.urlencode({
+          'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
+          'assertion': '%s.%s' % (assertion_input, signature)
+      })
+
+      resp = urlfetch.fetch(
+          url='https://accounts.google.com/o/oauth2/token',
+          validate_certificate=True,
+          payload=message,
+          method=urlfetch.POST,
+          headers={'Content-Type': 'application/x-www-form-urlencoded'})
+
+      if resp.status_code != 200:
+        raise apiproxy_errors.ApplicationError(
+            app_identity_service_pb.AppIdentityServiceError.UNKNOWN_ERROR,
+            'Error getting access token. Response code: %i, Content: %s' % (
+                resp.status_code, resp.content))
+
+      rv = json.loads(resp.content)
+
+
+      rv['expires'] = now + int(rv.get('expires_in', '0'))
+      with self.__access_token_cache_lock:
+        self.__access_token_cache[scope] = rv
+
+    response.set_access_token(rv['access_token'])
+    response.set_expiration_time(rv['expires'])
diff --git a/google/appengine/api/app_identity/app_identity_service_pb.py b/google/appengine/api/app_identity/app_identity_service_pb.py
index 75337bf..b9427dc 100644
--- a/google/appengine/api/app_identity/app_identity_service_pb.py
+++ b/google/appengine/api/app_identity/app_identity_service_pb.py
@@ -1917,6 +1917,7 @@
 
   @classmethod
   def _MethodSignatures(cls):
+    """Returns a dict of {<method-name>: (<request-type>, <response-type>)}."""
     return {
       'SignForApp': (SignForAppRequest, SignForAppResponse),
       'GetPublicCertificatesForApp': (GetPublicCertificateForAppRequest, GetPublicCertificateForAppResponse),
@@ -1925,6 +1926,12 @@
       'GetDefaultGcsBucketName': (GetDefaultGcsBucketNameRequest, GetDefaultGcsBucketNameResponse),
       }
 
+  @classmethod
+  def _StreamMethodSignatures(cls):
+    """Returns a dict of {<method-name>: (<request-type>, <stream-type>, <response-type>)}."""
+    return {
+      }
+
   def __init__(self, *args, **kwargs):
     """Creates a Stubby RPC server.
 
diff --git a/google/appengine/api/app_identity/app_identity_stub.py b/google/appengine/api/app_identity/app_identity_stub.py
index 2ce6d81..1c204d8 100644
--- a/google/appengine/api/app_identity/app_identity_stub.py
+++ b/google/appengine/api/app_identity/app_identity_stub.py
@@ -155,3 +155,14 @@
     response.set_access_token('InvalidToken:%s:%s' % (token, time.time() % 100))
 
     response.set_expiration_time(int(time.time()) + 1800)
+
+  @staticmethod
+  def Create(email_address=None, private_key_path=None):
+    if email_address:
+      from google.appengine.api.app_identity import app_identity_keybased_stub
+
+      return app_identity_keybased_stub.KeyBasedAppIdentityServiceStub(
+          email_address=email_address,
+          private_key_path=private_key_path)
+    else:
+      return AppIdentityServiceStub()
diff --git a/google/appengine/api/appinfo.py b/google/appengine/api/appinfo.py
index 49824fa..6b433af 100644
--- a/google/appengine/api/appinfo.py
+++ b/google/appengine/api/appinfo.py
@@ -59,7 +59,7 @@
 
 
 _URL_REGEX = r'(?!\^)/.*|\..*|(\(.).*(?!\$).'
-_FILES_REGEX = r'(?!\^).*(?!\$).'
+_FILES_REGEX = r'.+'
 _URL_ROOT_REGEX = r'/.*'
 
 
@@ -1286,6 +1286,27 @@
   KEY_VALIDATOR = validation.Regex('[a-zA-Z_][a-zA-Z0-9_]*')
   VALUE_VALIDATOR = str
 
+  @classmethod
+  def Merge(cls, env_variables_one, env_variables_two):
+    """Merges to EnvironmentVariables instances.
+
+    Args:
+      env_variables_one: The first EnvironmentVariables instance or None.
+      env_variables_two: The second EnvironmentVariables instance or None.
+
+    Returns:
+      The merged EnvironmentVariables instance, or None if both input instances
+      are None or empty.
+
+    If a variable is specified by both instances, the value from
+    env_variables_two is used.
+    """
+
+    result_env_variables = (env_variables_one or {}).copy()
+    result_env_variables.update(env_variables_two or {})
+    return (EnvironmentVariables(**result_env_variables)
+            if result_env_variables else None)
+
 
 def NormalizeVmSettings(appyaml):
   """Normalize Vm settings.
@@ -1330,8 +1351,7 @@
       MANUAL_SCALING: validation.Optional(ManualScaling),
       VM: validation.Optional(bool),
       VM_SETTINGS: validation.Optional(VmSettings),
-
-
+      ENV_VARIABLES: validation.Optional(EnvironmentVariables),
 
 
   }
@@ -1389,6 +1409,11 @@
     one.vm_settings = VmSettings.Merge(one.vm_settings,
                                        two.vm_settings)
 
+
+
+    one.env_variables = EnvironmentVariables.Merge(one.env_variables,
+                                                   two.env_variables)
+
     return one
 
   @classmethod
diff --git a/google/appengine/api/blobstore/blobstore.py b/google/appengine/api/blobstore/blobstore.py
index 1c869e9..a435122 100644
--- a/google/appengine/api/blobstore/blobstore.py
+++ b/google/appengine/api/blobstore/blobstore.py
@@ -337,7 +337,9 @@
                           _get_result_hook, lambda rpc: rpc.response.url())
 
 
-def delete(blob_keys, rpc=None):
+
+
+def delete(blob_keys, rpc=None, _token=None):
   """Delete a blob from Blobstore.
 
   Args:
@@ -348,11 +350,16 @@
   Returns:
     None.
   """
-  rpc = delete_async(blob_keys, rpc)
+
+
+
+  rpc = delete_async(blob_keys, rpc, _token)
   return rpc.get_result()
 
 
-def delete_async(blob_keys, rpc=None):
+
+
+def delete_async(blob_keys, rpc=None, _token=None):
   """Delete a blob from Blobstore -- async version.
 
   Args:
@@ -363,11 +370,16 @@
   Returns:
     A UserRPC whose result will be None.
   """
+
+
+
   if isinstance(blob_keys, (basestring, BlobKey)):
     blob_keys = [blob_keys]
   request = blobstore_service_pb.DeleteBlobRequest()
   for blob_key in blob_keys:
     request.add_blob_key(str(blob_key))
+  if _token:
+    request.set_token(_token)
   response = api_base_pb.VoidProto()
 
   return _make_async_call(rpc, 'DeleteBlob', request, response,
diff --git a/google/appengine/api/blobstore/blobstore_service_pb.py b/google/appengine/api/blobstore/blobstore_service_pb.py
index dca96bd..7f3d309 100644
--- a/google/appengine/api/blobstore/blobstore_service_pb.py
+++ b/google/appengine/api/blobstore/blobstore_service_pb.py
@@ -428,6 +428,8 @@
   _STYLE_CONTENT_TYPE = """"""
   _PROTO_DESCRIPTOR_NAME = 'apphosting.CreateUploadURLResponse'
 class DeleteBlobRequest(ProtocolBuffer.ProtocolMessage):
+  has_token_ = 0
+  token_ = ""
 
   def __init__(self, contents=None):
     self.blob_key_ = []
@@ -448,16 +450,32 @@
   def clear_blob_key(self):
     self.blob_key_ = []
 
+  def token(self): return self.token_
+
+  def set_token(self, x):
+    self.has_token_ = 1
+    self.token_ = x
+
+  def clear_token(self):
+    if self.has_token_:
+      self.has_token_ = 0
+      self.token_ = ""
+
+  def has_token(self): return self.has_token_
+
 
   def MergeFrom(self, x):
     assert x is not self
     for i in xrange(x.blob_key_size()): self.add_blob_key(x.blob_key(i))
+    if (x.has_token()): self.set_token(x.token())
 
   def Equals(self, x):
     if x is self: return 1
     if len(self.blob_key_) != len(x.blob_key_): return 0
     for e1, e2 in zip(self.blob_key_, x.blob_key_):
       if e1 != e2: return 0
+    if self.has_token_ != x.has_token_: return 0
+    if self.has_token_ and self.token_ != x.token_: return 0
     return 1
 
   def IsInitialized(self, debug_strs=None):
@@ -468,26 +486,35 @@
     n = 0
     n += 1 * len(self.blob_key_)
     for i in xrange(len(self.blob_key_)): n += self.lengthString(len(self.blob_key_[i]))
+    if (self.has_token_): n += 1 + self.lengthString(len(self.token_))
     return n
 
   def ByteSizePartial(self):
     n = 0
     n += 1 * len(self.blob_key_)
     for i in xrange(len(self.blob_key_)): n += self.lengthString(len(self.blob_key_[i]))
+    if (self.has_token_): n += 1 + self.lengthString(len(self.token_))
     return n
 
   def Clear(self):
     self.clear_blob_key()
+    self.clear_token()
 
   def OutputUnchecked(self, out):
     for i in xrange(len(self.blob_key_)):
       out.putVarInt32(10)
       out.putPrefixedString(self.blob_key_[i])
+    if (self.has_token_):
+      out.putVarInt32(18)
+      out.putPrefixedString(self.token_)
 
   def OutputPartial(self, out):
     for i in xrange(len(self.blob_key_)):
       out.putVarInt32(10)
       out.putPrefixedString(self.blob_key_[i])
+    if (self.has_token_):
+      out.putVarInt32(18)
+      out.putPrefixedString(self.token_)
 
   def TryMerge(self, d):
     while d.avail() > 0:
@@ -495,6 +522,9 @@
       if tt == 10:
         self.add_blob_key(d.getPrefixedString())
         continue
+      if tt == 18:
+        self.set_token(d.getPrefixedString())
+        continue
 
 
       if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
@@ -509,6 +539,7 @@
       if printElemNumber: elm="(%d)" % cnt
       res+=prefix+("blob_key%s: %s\n" % (elm, self.DebugFormatString(e)))
       cnt+=1
+    if self.has_token_: res+=prefix+("token: %s\n" % self.DebugFormatString(self.token_))
     return res
 
 
@@ -516,16 +547,19 @@
     return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
 
   kblob_key = 1
+  ktoken = 2
 
   _TEXT = _BuildTagLookupTable({
     0: "ErrorCode",
     1: "blob_key",
-  }, 1)
+    2: "token",
+  }, 2)
 
   _TYPES = _BuildTagLookupTable({
     0: ProtocolBuffer.Encoder.NUMERIC,
     1: ProtocolBuffer.Encoder.STRING,
-  }, 1, ProtocolBuffer.Encoder.MAX_TYPE)
+    2: ProtocolBuffer.Encoder.STRING,
+  }, 2, ProtocolBuffer.Encoder.MAX_TYPE)
 
 
   _STYLE = """"""
diff --git a/google/appengine/api/datastore.py b/google/appengine/api/datastore.py
index 55e4712..7dc66cb 100644
--- a/google/appengine/api/datastore.py
+++ b/google/appengine/api/datastore.py
@@ -442,7 +442,7 @@
   if isinstance(request, datastore_pb.Query):
     conn._set_request_read_policy(request, config)
     conn._set_request_transaction(request)
-  rpc = conn.make_rpc_call(config, call, request, response)
+  rpc = conn._make_rpc_call(config, call, request, response)
   conn.check_rpc_success(rpc)
   return response
 
@@ -476,7 +476,7 @@
   config = None
   if deadline is not None:
     config = datastore_rpc.Configuration(deadline=deadline)
-  rpc = conn.create_rpc(config)
+  rpc = conn._create_rpc(config)
   rpc.callback = callback
   if read_policy is not None:
     rpc.read_policy = read_policy
diff --git a/google/appengine/api/datastore_types.py b/google/appengine/api/datastore_types.py
index 940bd97..1f2667d 100644
--- a/google/appengine/api/datastore_types.py
+++ b/google/appengine/api/datastore_types.py
@@ -52,14 +52,17 @@
 import time
 import urlparse
 from xml.sax import saxutils
-from google.appengine.datastore import datastore_pb
-from google.appengine.datastore import sortable_pb_encoder
-from google.appengine.api import datastore_errors
-from google.appengine.api import users
-from google.appengine.api import namespace_manager
-from google.net.proto import ProtocolBuffer
+
 from google.appengine.datastore import entity_pb
 
+from google.appengine.api import datastore_errors
+from google.appengine.api import namespace_manager
+from google.appengine.api import users
+from google.appengine.datastore import datastore_pb
+from google.appengine.datastore import datastore_pbs
+from google.appengine.datastore import entity_v4_pb
+from google.appengine.datastore import sortable_pb_encoder
+
 
 
 
@@ -2067,26 +2070,33 @@
   return type_(value_string)
 
 
-def ReferenceToKeyValue(reference):
-  """Converts a entity_pb.Reference into a comparable hashable "key" value.
+def ReferenceToKeyValue(key):
+  """Converts a key into a comparable hashable "key" value.
 
   Args:
-    reference: The entity_pb.Reference from which to construct the key value.
+    key: The entity_pb.Reference or entity_v4_pb.Key from which to construct
+        the key value.
 
   Returns:
-    A comparable and hashable representation of the given reference that is
-    compatible with one derived from a reference property value.
+    A comparable and hashable representation of the given key that is
+    compatible with one derived from a key property value.
   """
-  if isinstance(reference, entity_pb.Reference):
-    element_list = reference.path().element_list()
-  elif isinstance(reference, entity_pb.PropertyValue_ReferenceValue):
-    element_list = reference.pathelement_list()
+  if isinstance(key, entity_v4_pb.Key):
+    v4_key = key
+    key = entity_pb.Reference()
+    datastore_pbs.get_entity_converter().v4_to_v3_reference(v4_key, key)
+
+  if isinstance(key, entity_pb.Reference):
+    element_list = key.path().element_list()
+  elif isinstance(key, entity_pb.PropertyValue_ReferenceValue):
+    element_list = key.pathelement_list()
   else:
     raise datastore_errors.BadArgumentError(
-        "reference arg expected to be entity_pb.Reference (%r)" % (reference,))
+        "key arg expected to be entity_pb.Reference or entity_v4.Key (%r)"
+        % (key,))
 
   result = [entity_pb.PropertyValue.kReferenceValueGroup,
-            reference.app(), reference.name_space()]
+            key.app(), key.name_space()]
   for element in element_list:
     result.append(element.type())
     if element.has_name():
diff --git a/google/appengine/api/logservice/logservice_stub.py b/google/appengine/api/logservice/logservice_stub.py
index 043c175..1fbe9d9 100644
--- a/google/appengine/api/logservice/logservice_stub.py
+++ b/google/appengine/api/logservice/logservice_stub.py
@@ -17,6 +17,7 @@
 """Stub implementation for Log Service that uses sqlite."""
 
 import atexit
+import codecs
 import logging
 import time
 
@@ -215,7 +216,7 @@
   def _tuple_from_log_line(row_id, log_line):
     message = log_line.message()
     if isinstance(message, str):
-      message = message.decode('utf-8')
+      message = codecs.decode(message, 'utf-8', 'replace')
     return (row_id, log_line.timestamp_usec(), log_line.level(), message)
 
   @apiproxy_stub.Synchronized
diff --git a/google/appengine/api/mail.py b/google/appengine/api/mail.py
index b68f070..fca0ce4 100644
--- a/google/appengine/api/mail.py
+++ b/google/appengine/api/mail.py
@@ -347,9 +347,12 @@
     else returns attachments as is.
   """
   if len(attachments) == 2 and isinstance(attachments[0], basestring):
-    return attachments,
-  return attachments
-
+    attachments = attachments,
+  for attachment in attachments:
+    if isinstance(attachment, Attachment):
+      yield attachment
+    else:
+      yield Attachment(*attachment)
 
 def _parse_mime_message(mime_message):
   """Helper function converts a mime_message in to email.Message.Message.
@@ -536,6 +539,8 @@
                                'attachment',
                                filename=attachment.filename())
     mime_attachment.set_payload(attachment.data())
+    if attachment.has_contentid():
+      mime_attachment['content-id'] = attachment.contentid()
     result.attach(mime_attachment)
 
 
@@ -609,6 +614,124 @@
     return map(_decode_and_join_header, address_list)
 
 
+
+def wrapping(wrapped):
+
+
+
+
+  def wrapping_wrapper(wrapper):
+    try:
+      wrapper.__wrapped__ = wrapped
+      wrapper.__name__ = wrapped.__name__
+      wrapper.__doc__ = wrapped.__doc__
+      wrapper.__dict__.update(wrapped.__dict__)
+    except Exception:
+      pass
+    return wrapper
+  return wrapping_wrapper
+
+
+
+def _positional(max_pos_args):
+  """A decorator to declare that only the first N arguments may be positional.
+
+  Note that for methods, n includes 'self'.
+  """
+  def positional_decorator(wrapped):
+    @wrapping(wrapped)
+    def positional_wrapper(*args, **kwds):
+      if len(args) > max_pos_args:
+        plural_s = ''
+        if max_pos_args != 1:
+          plural_s = 's'
+        raise TypeError(
+            '%s() takes at most %d positional argument%s (%d given)' %
+            (wrapped.__name__, max_pos_args, plural_s, len(args)))
+      return wrapped(*args, **kwds)
+    return positional_wrapper
+  return positional_decorator
+
+
+class Attachment(object):
+  """Attachment object.
+
+  Subclasses tuple to retain compatibility with existing code. An Attachment
+  object is largely interchangeable with a (filename, payload) tuple.
+
+  Note that the behavior is a bit asymmetric with respect to unpacking and
+  equality comparison. An Attachment object without a content ID will be
+  equivalent to a (filename, payload) tuple. An Attachment with a content ID
+  will unpack to a (filename, payload) tuple, but will compare unequally to
+  that tuple.
+
+  Thus, the following comparison will succeed:
+
+      attachment = mail.Attachment('foo.jpg', 'data')
+      filename, payload = attachment
+      attachment == filename, payload
+
+  ...while the following will fail:
+
+      attachment = mail.Attachment('foo.jpg', 'data', content_id='<foo>')
+      filename, payload = attachment
+      attachment == filename, payload
+
+   The following comparison will pass though:
+
+      attachment = mail.Attachment('foo.jpg', 'data', content_id='<foo>')
+      attachment == (attachment.filename,
+                     attachment.payload,
+                     attachment.content_id)
+
+  Attributes:
+    filename: The name of the attachment.
+    payload: The attachment data.
+    content_id: Optional. The content-id for this attachment. Keyword-only.
+  """
+
+  @_positional(3)
+  def __init__(self, filename, payload, content_id=None):
+    """Constructor.
+
+    Arguments:
+      filename: The name of the attachment
+      payload: The attachment data.
+      content_id: Optional. The content-id for this attachment.
+    """
+    self.filename = filename
+    self.payload = payload
+    self.content_id = content_id
+
+  def __eq__(self, other):
+    self_tuple = (self.filename, self.payload, self.content_id)
+    if isinstance(other, Attachment):
+      other_tuple = (other.filename, other.payload, other.content_id)
+
+
+    elif not hasattr(other, '__len__'):
+      return NotImplemented
+    elif len(other) == 2:
+      other_tuple = other + (None,)
+    elif len(other) == 3:
+      other_tuple = other
+    else:
+      return NotImplemented
+    return self_tuple == other_tuple
+
+  def __hash__(self):
+    if self.content_id:
+      return hash((self.filename, self.payload, self.content_id))
+    else:
+      return hash((self.filename, self.payload))
+
+  def __ne__(self, other):
+    return not self == other
+
+  def __iter__(self):
+    return iter((self.filename, self.payload))
+
+
 class EncodedPayload(object):
   """Wrapper for a payload that contains encoding information.
 
@@ -858,16 +981,16 @@
       found_body = True
 
     if hasattr(self, 'attachments'):
-      for file_name, data in _attachment_sequence(self.attachments):
+      for attachment in _attachment_sequence(self.attachments):
 
 
-        _GetMimeType(file_name)
+        _GetMimeType(attachment.filename)
 
 
 
 
-        if isinstance(data, EncodedPayload):
-          data.decode()
+        if isinstance(attachment.payload, EncodedPayload):
+          attachment.payload.decode()
 
 
   def CheckInitialized(self):
@@ -927,12 +1050,14 @@
       message.set_htmlbody(_to_str(html))
 
     if hasattr(self, 'attachments'):
-      for file_name, data in _attachment_sequence(self.attachments):
-        if isinstance(data, EncodedPayload):
+      for attachment in _attachment_sequence(self.attachments):
+        if isinstance(attachment.payload, EncodedPayload):
           data = data.decode()
-        attachment = message.add_attachment()
-        attachment.set_filename(_to_str(file_name))
-        attachment.set_data(_to_str(data))
+        protoattachment = message.add_attachment()
+        protoattachment.set_filename(_to_str(attachment.filename))
+        protoattachment.set_data(_to_str(attachment.payload))
+        if attachment.content_id:
+          protoattachment.set_contentid(attachment.content_id)
     return message
 
   def to_mime_message(self):
@@ -985,10 +1110,9 @@
     self.send(*args, **kwds)
 
   def _check_attachment(self, attachment):
-    file_name, data = attachment
 
-    if not (isinstance(file_name, basestring) or
-            isinstance(data, basestring)):
+    if not (isinstance(attachment.filename, basestring) or
+            isinstance(attachment.payload, basestring)):
       raise TypeError()
 
   def _check_attachments(self, attachments):
@@ -1004,11 +1128,9 @@
     Raises:
       TypeError if values are not string type.
     """
-    if len(attachments) == 2 and isinstance(attachments[0], basestring):
-      self._check_attachment(attachments)
-    else:
-      for attachment in attachments:
-        self._check_attachment(attachment)
+    attachments = _attachment_sequence(attachments)
+    for attachment in attachments:
+      self._check_attachment(attachment)
 
   def __setattr__(self, attr, value):
     """Property setting access control.
@@ -1088,17 +1210,24 @@
                                   mime_message.get_charset()),
                                  mime_message['content-transfer-encoding'])
 
+        if 'content-id' in mime_message:
+          attachment = Attachment(filename,
+                                  payload,
+                                  content_id=mime_message['content-id'])
+        else:
+          attachment = Attachment(filename, payload)
+
         if filename:
 
           try:
             attachments = self.attachments
           except AttributeError:
-            self.attachments = [(filename, payload)]
+            self.attachments = [attachment]
           else:
             if isinstance(attachments[0], basestring):
               self.attachments = [attachments]
               attachments = self.attachments
-            attachments.append((filename, payload))
+            attachments.append(attachment)
         else:
           self._add_body(mime_message.get_content_type(), payload)
 
diff --git a/google/appengine/api/modules/modules.py b/google/appengine/api/modules/modules.py
index 3bc8b59..02b7eb4 100644
--- a/google/appengine/api/modules/modules.py
+++ b/google/appengine/api/modules/modules.py
@@ -17,27 +17,39 @@
 """Exposes methods to control modules and versions of an app."""
 
 __all__ = [
-            'Error',
-            'InvalidModuleError',
-            'InvalidVersionError',
-            'InvalidInstancesError',
-            'UnexpectedStateError',
-            'TransientError',
+    'Error',
+    'InvalidModuleError',
+    'InvalidVersionError',
+    'InvalidInstancesError',
+    'UnexpectedStateError',
+    'TransientError',
 
-            'get_current_module_name',
-            'get_current_version_name',
-            'get_current_instance_id',
-            'get_modules',
-            'get_versions',
-            'get_default_version',
-            'get_num_instances',
-            'set_num_instances',
-            'start_module',
-            'stop_module',
-            'get_hostname',
-           ]
+    'get_current_module_name',
+    'get_current_version_name',
+    'get_current_instance_id',
+    'get_modules',
+    'get_modules_async',
+    'get_versions',
+    'get_versions_async',
+    'get_default_version',
+    'get_default_version_async',
+    'get_num_instances',
+    'get_num_instances_async',
+    'set_num_instances',
+    'set_num_instances_async',
+    'start_module',
+    'start_module_async',
+    'start_version',
+    'start_version_async',
+    'stop_module',
+    'stop_module_async',
+    'stop_version',
+    'stop_version_async',
+    'get_hostname',
+    'get_hostname_async']
 
 
+import logging
 import os
 
 from google.appengine.api import apiproxy_stub_map
@@ -128,10 +140,15 @@
 }
 
 
-def _CheckAsyncResult(rpc, expected_application_errors):
+def _CheckAsyncResult(rpc,
+                      expected_application_errors,
+                      ignored_application_errors):
   try:
     rpc.check_success()
   except apiproxy_errors.ApplicationError, e:
+    if e.application_error in ignored_application_errors:
+      logging.info(ignored_application_errors.get(e.application_error))
+      return
     if e.application_error in expected_application_errors:
       mapped_error = _MODULE_SERVICE_ERROR_MAP.get(e.application_error)
       if mapped_error:
@@ -148,21 +165,35 @@
       the name of the module that is associated with the instance that calls
       this function.
   """
-  rpc = get_modules_async()
-  return rpc.get_result()
+  def _ResultHook(rpc):
+    _CheckAsyncResult(rpc, [], {})
+
+
+    return list(rpc.response.module_list())
+
+  request = modules_service_pb.GetModulesRequest()
+  response = modules_service_pb.GetModulesResponse()
+  return _MakeAsyncCall('GetModules',
+                        request,
+                        response,
+                        _ResultHook).get_result()
 
 
 def get_modules_async():
   """Returns a UserRPC whose result contains this application's module names.
 
-     Returns:
-       A UserRPC whose result contains a list of strings containing the names
-       of modules associated with this application. The 'default' module will be
-       included if it exists, as will the name of the module that is associated
-       with the instance that calls this function.
+  DEPRECATED. Please use get_modules instead.
+
+  Returns:
+    A UserRPC whose result contains a list of strings containing the names
+    of modules associated with this application. The 'default' module will be
+    included if it exists, as will the name of the module that is associated
+    with the instance that calls this function.
   """
+  logging.warning('The get_modules_async function is deprecated. Please '
+                  'use get_modules instead.')
   def _ResultHook(rpc):
-    _CheckAsyncResult(rpc, [])
+    _CheckAsyncResult(rpc, [], {})
 
 
     return list(rpc.response.module_list())
@@ -187,13 +218,29 @@
     InvalidModuleError if the given module isn't valid, TransientError if there
     is an issue fetching the information.
   """
-  rpc = get_versions_async(module)
-  return rpc.get_result()
+  def _ResultHook(rpc):
+    mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_MODULE,
+                     modules_service_pb.ModulesServiceError.TRANSIENT_ERROR]
+    _CheckAsyncResult(rpc, mapped_errors, {})
+
+
+    return list(rpc.response.version_list())
+
+  request = modules_service_pb.GetVersionsRequest()
+  if module:
+    request.set_module(module)
+  response = modules_service_pb.GetVersionsResponse()
+  return _MakeAsyncCall('GetVersions',
+                        request,
+                        response,
+                        _ResultHook).get_result()
 
 
 def get_versions_async(module=None):
   """Returns a UserRPC whose result contains list of versions for a module.
 
+  DEPRECATED. Please use get_versions instead.
+
   Args:
     module: Module to retrieve version for, if None then the current module will
       be used.
@@ -202,10 +249,12 @@
     Returns a UserRPC whose result contains the list of strings containing
     the names of versions associated with the specified module.
   """
+  logging.warning('The get_versions_async function is deprecated. Please '
+                  'use get_versions instead.')
   def _ResultHook(rpc):
     mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_MODULE,
                      modules_service_pb.ModulesServiceError.TRANSIENT_ERROR]
-    _CheckAsyncResult(rpc, mapped_errors)
+    _CheckAsyncResult(rpc, mapped_errors, {})
 
 
     return list(rpc.response.version_list())
@@ -231,14 +280,28 @@
     InvalidModuleError if the given module is not valid, InvalidVersionError if
     no default version could be found.
   """
-  rpc = get_default_version_async(module)
-  return rpc.get_result()
+  def _ResultHook(rpc):
+    mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_MODULE,
+                     modules_service_pb.ModulesServiceError.INVALID_VERSION]
+    _CheckAsyncResult(rpc, mapped_errors, {})
+    return rpc.response.version()
+
+  request = modules_service_pb.GetDefaultVersionRequest()
+  if module:
+    request.set_module(module)
+  response = modules_service_pb.GetDefaultVersionResponse()
+  return _MakeAsyncCall('GetDefaultVersion',
+                        request,
+                        response,
+                        _ResultHook).get_result()
 
 
 def get_default_version_async(
     module=None):
   """Returns a UserRPC whose result contains a module's default version.
 
+  DEPRECATED. Please use get_default_version instead.
+
   Args:
     module: Module to retrieve the default version for, if None then the current
       module will be used.
@@ -247,10 +310,12 @@
     Returns a UserRPC whose result contains a string holding the name of the
     default version of the specified module.
   """
+  logging.warning('The get_default_version_async function is deprecated. '
+                  'Please use get_default_version instead.')
   def _ResultHook(rpc):
     mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_MODULE,
                      modules_service_pb.ModulesServiceError.INVALID_VERSION]
-    _CheckAsyncResult(rpc, mapped_errors)
+    _CheckAsyncResult(rpc, mapped_errors, {})
     return rpc.response.version()
 
   request = modules_service_pb.GetDefaultVersionRequest()
@@ -281,14 +346,29 @@
   Raises:
     InvalidVersionError on invalid input.
   """
-  rpc = get_num_instances_async(module, version)
-  return rpc.get_result()
+  def _ResultHook(rpc):
+    mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_VERSION]
+    _CheckAsyncResult(rpc, mapped_errors, {})
+    return rpc.response.instances()
+
+  request = modules_service_pb.GetNumInstancesRequest()
+  if module:
+    request.set_module(module)
+  if version:
+    request.set_version(version)
+  response = modules_service_pb.GetNumInstancesResponse()
+  return _MakeAsyncCall('GetNumInstances',
+                        request,
+                        response,
+                        _ResultHook).get_result()
 
 
 def get_num_instances_async(
     module=None, version=None):
   """Returns a UserRPC whose result holds the number of instances for a version.
 
+  DEPRECATED. Please use get_num_instances instead.
+
   This is only valid for fixed modules, an error will be raised for
   automatically-scaled modules.  Support for automatically-scaled modules may be
   supported in the future.
@@ -303,9 +383,11 @@
   Returns:
     A UserRPC whose result holds the number of instances for a version.
   """
+  logging.warning('The get_num_instances_async function is deprecated. '
+                  'Please use get_num_instances instead.')
   def _ResultHook(rpc):
     mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_VERSION]
-    _CheckAsyncResult(rpc, mapped_errors)
+    _CheckAsyncResult(rpc, mapped_errors, {})
     return rpc.response.instances()
 
   request = modules_service_pb.GetNumInstancesRequest()
@@ -354,7 +436,7 @@
   def _ResultHook(rpc):
     mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_VERSION,
                      modules_service_pb.ModulesServiceError.TRANSIENT_ERROR]
-    _CheckAsyncResult(rpc, mapped_errors)
+    _CheckAsyncResult(rpc, mapped_errors, {})
 
   if not isinstance(instances, (long, int)):
     raise TypeError("'instances' arg must be of type long or int.")
@@ -368,8 +450,7 @@
   return _MakeAsyncCall('SetNumInstances', request, response, _ResultHook)
 
 
-def start_module(module,
-                 version):
+def start_version(module, version):
   """Start all instances for the given version of the module.
 
   Args:
@@ -378,15 +459,33 @@
 
   Raises:
     InvalidVersionError if the given module version is invalid.
-    UnexpectedStateError if the module is already started, or cannot be started.
     TransientError if there is a problem persisting the change.
   """
-  rpc = start_module_async(module, version)
+  rpc = start_version_async(module, version)
   rpc.get_result()
 
 
-def start_module_async(module,
-                       version):
+def start_module(module,
+                 version):
+  """Start all instances for the given version of the module.
+
+  DEPRECATED. Please use start_version instead.
+
+  Args:
+    module: String containing the name of the module to affect.
+    version: String containing the name of the version of the module to start.
+
+  Raises:
+    InvalidVersionError if the given module version is invalid.
+    TransientError if there is a problem persisting the change.
+  """
+  logging.warning('The start_module function is deprecated, please use the '
+                  'start_version function instead.')
+  start_version(module, version)
+
+
+def start_version_async(module,
+                        version):
   """Returns a UserRPC  to start all instances for the given module version.
 
   Args:
@@ -398,9 +497,13 @@
   """
   def _ResultHook(rpc):
     mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_VERSION,
-                     modules_service_pb.ModulesServiceError.TRANSIENT_ERROR,
-                     modules_service_pb.ModulesServiceError.UNEXPECTED_STATE]
-    _CheckAsyncResult(rpc, mapped_errors)
+                     modules_service_pb.ModulesServiceError.TRANSIENT_ERROR]
+    expected_errors = {
+        modules_service_pb.ModulesServiceError.UNEXPECTED_STATE:
+        'The specified module: %s, version: %s is already started.' % (module,
+                                                                       version)
+    }
+    _CheckAsyncResult(rpc, mapped_errors, expected_errors)
 
   request = modules_service_pb.StartModuleRequest()
   request.set_module(module)
@@ -409,10 +512,47 @@
   return _MakeAsyncCall('StartModule', request, response, _ResultHook)
 
 
+def start_module_async(module,
+                       version):
+  """Returns a UserRPC  to start all instances for the given module version.
+
+  DEPRECATED. Please use start_version_async instead.
+
+  Args:
+    module: String containing the name of the module to affect.
+    version: String containing the name of the version of the module to start.
+
+  Returns:
+    A UserRPC  to start all instances for the given module version.
+  """
+  logging.warning('The start_module_async function is deprecated, please use '
+                  'the start_version_async function isntead.')
+  return start_version_async(module, version)
+
+
+def stop_version(module=None,
+                 version=None):
+  """Stops all instances for the given version of the module.
+
+  Args:
+    module: The module to affect, if None the current module is used.
+    version: The version of the given module to affect, if None the current
+      version is used.
+
+  Raises:
+    InvalidVersionError if the given module version is invalid.
+    TransientError if there is a problem persisting the change.
+  """
+  rpc = stop_version_async(module, version)
+  rpc.get_result()
+
+
 def stop_module(module=None,
                 version=None):
   """Stops all instances for the given version of the module.
 
+  DEPRECATED. Please use stop_version instead.
+
   Args:
     module: The module to affect, if None the current module is used.
     version: The version of the given module to affect, if None the current
@@ -423,12 +563,13 @@
     UnexpectedStateError if the module is already stopped, or cannot be stopped.
     TransientError if there is a problem persisting the change.
   """
-  rpc = stop_module_async(module, version)
-  rpc.get_result()
+  logging.warning('The stop_module function is deprecated, please use '
+                  'the stop_version function isntead.')
+  stop_version(module, version)
 
 
-def stop_module_async(module=None,
-                      version=None):
+def stop_version_async(module=None,
+                       version=None):
   """Returns a UserRPC  to stop all instances for the given module version.
 
   Args:
@@ -441,9 +582,13 @@
   """
   def _ResultHook(rpc):
     mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_VERSION,
-                     modules_service_pb.ModulesServiceError.TRANSIENT_ERROR,
-                     modules_service_pb.ModulesServiceError.UNEXPECTED_STATE]
-    _CheckAsyncResult(rpc, mapped_errors)
+                     modules_service_pb.ModulesServiceError.TRANSIENT_ERROR]
+    expected_errors = {
+        modules_service_pb.ModulesServiceError.UNEXPECTED_STATE:
+        'The specified module: %s, version: %s is already stopped.' % (module,
+                                                                       version)
+    }
+    _CheckAsyncResult(rpc, mapped_errors, expected_errors)
 
   request = modules_service_pb.StopModuleRequest()
   if module:
@@ -454,6 +599,25 @@
   return _MakeAsyncCall('StopModule', request, response, _ResultHook)
 
 
+def stop_module_async(module=None,
+                      version=None):
+  """Returns a UserRPC  to stop all instances for the given module version.
+
+  DEPRECATED. Please use stop_version_async instead.
+
+  Args:
+    module: The module to affect, if None the current module is used.
+    version: The version of the given module to affect, if None the current
+      version is used.
+
+  Returns:
+    A UserRPC  to stop all instances for the given module version.
+  """
+  logging.warning('The stop_module_async function is deprecated. Please use '
+                  'the stop_version_async function instead.')
+  return stop_version_async(module, version)
+
+
 def get_hostname(module=None,
                  version=None, instance=None):
   """Returns a hostname to use to contact the module.
@@ -476,14 +640,35 @@
     InvalidInstancesError if the given instance value is invalid.
     TypeError if the given instance type is invalid.
   """
-  rpc = get_hostname_async(module, version, instance)
-  return rpc.get_result()
+  def _ResultHook(rpc):
+    mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_MODULE,
+                     modules_service_pb.ModulesServiceError.INVALID_INSTANCES]
+    _CheckAsyncResult(rpc, mapped_errors, [])
+    return rpc.response.hostname()
+
+  request = modules_service_pb.GetHostnameRequest()
+  if module:
+    request.set_module(module)
+  if version:
+    request.set_version(version)
+  if instance:
+    if not isinstance(instance, (basestring, long, int)):
+      raise TypeError(
+          "'instance' arg must be of type basestring, long or int.")
+    request.set_instance(str(instance))
+  response = modules_service_pb.GetHostnameResponse()
+  return _MakeAsyncCall('GetHostname',
+                        request,
+                        response,
+                        _ResultHook).get_result()
 
 
 def get_hostname_async(module=None,
                        version=None, instance=None):
   """Returns a UserRPC whose result contains the hostname to contact a module.
 
+  DEPRECATED. Please use get_hostname instead.
+
   Args:
     module: Name of module, if None, take module of the current instance.
     version: Name of version, if version is None then either use the version of
@@ -499,12 +684,14 @@
     E.g. 0.v1.module5.myapp.appspot.com
 
   Raises:
-    TypeError if the given instance type is invalid.
+    TypeError: If the given instance type is invalid.
   """
+  logging.warning('The get_hostname_async function is deprecated. Please '
+                  'use get_hostname instead.')
   def _ResultHook(rpc):
     mapped_errors = [modules_service_pb.ModulesServiceError.INVALID_MODULE,
                      modules_service_pb.ModulesServiceError.INVALID_INSTANCES]
-    _CheckAsyncResult(rpc, mapped_errors)
+    _CheckAsyncResult(rpc, mapped_errors, [])
     return rpc.response.hostname()
 
   request = modules_service_pb.GetHostnameRequest()
@@ -519,3 +706,4 @@
     request.set_instance(str(instance))
   response = modules_service_pb.GetHostnameResponse()
   return _MakeAsyncCall('GetHostname', request, response, _ResultHook)
+
diff --git a/google/appengine/api/modules/modules_stub.py b/google/appengine/api/modules/modules_stub.py
index a4f23eb..bc4c1be 100644
--- a/google/appengine/api/modules/modules_stub.py
+++ b/google/appengine/api/modules/modules_stub.py
@@ -24,6 +24,7 @@
 
 class ModulesServiceStub(apiproxy_stub.APIProxyStub):
 
+
   _ACCEPTS_REQUEST_ID = True
   THREADSAFE = True
 
@@ -98,13 +99,13 @@
     version = request.version()
     dispatcher = self.request_data.get_dispatcher()
     try:
-      dispatcher.start_module(module, version)
+      dispatcher.start_version(module, version)
     except (request_info.ModuleDoesNotExistError,
             request_info.VersionDoesNotExistError,
             request_info.NotSupportedWithAutoScalingError):
       raise apiproxy_errors.ApplicationError(
           modules_service_pb.ModulesServiceError.INVALID_VERSION)
-    except request_info.ModuleAlreadyStartedError:
+    except request_info.VersionAlreadyStartedError:
       raise apiproxy_errors.ApplicationError(
           modules_service_pb.ModulesServiceError.UNEXPECTED_STATE)
 
@@ -112,13 +113,13 @@
     try:
       module, version, dispatcher = self._GetModuleAndVersionFromRequest(
           request, request_id)
-      dispatcher.stop_module(module, version)
+      dispatcher.stop_version(module, version)
     except (request_info.ModuleDoesNotExistError,
             request_info.VersionDoesNotExistError,
             request_info.NotSupportedWithAutoScalingError):
       raise apiproxy_errors.ApplicationError(
           modules_service_pb.ModulesServiceError.INVALID_VERSION)
-    except request_info.ModuleAlreadyStoppedError:
+    except request_info.VersionAlreadyStoppedError:
       raise apiproxy_errors.ApplicationError(
           modules_service_pb.ModulesServiceError.UNEXPECTED_STATE)
 
diff --git a/google/appengine/api/namespace_manager/namespace_manager.py b/google/appengine/api/namespace_manager/namespace_manager.py
index fdbfd51..c6b46b3 100644
--- a/google/appengine/api/namespace_manager/namespace_manager.py
+++ b/google/appengine/api/namespace_manager/namespace_manager.py
@@ -81,7 +81,7 @@
 
 
 def get_namespace():
-  """Get the the current default namespace or ('') namespace if unset."""
+  """Get the current default namespace or ('') namespace if unset."""
   name = os.environ.get(_ENV_CURRENT_NAMESPACE, None)
   if name is None:
     name = _config.default_namespace_for_request()
diff --git a/google/appengine/api/prospective_search/prospective_search_pb.py b/google/appengine/api/prospective_search/prospective_search_pb.py
index fbb0d30..aa102eb 100644
--- a/google/appengine/api/prospective_search/prospective_search_pb.py
+++ b/google/appengine/api/prospective_search/prospective_search_pb.py
@@ -31,7 +31,8 @@
   _extension_runtime = False
   _ExtendableProtocolMessage = ProtocolBuffer.ProtocolMessage
 
-from google.appengine.datastore.entity_pb import EntityProto
+from google.appengine.datastore.entity_pb import *
+import google.appengine.datastore.entity_pb
 class SchemaEntry(ProtocolBuffer.ProtocolMessage):
 
 
diff --git a/google/appengine/api/queueinfo.py b/google/appengine/api/queueinfo.py
index 06b89c2..3be0cad 100644
--- a/google/appengine/api/queueinfo.py
+++ b/google/appengine/api/queueinfo.py
@@ -145,7 +145,6 @@
 _NAME_REGEX = r'^[A-Za-z0-9-]{0,499}$'
 _RATE_REGEX = r'^(0|[0-9]+(\.[0-9]*)?/[smhd])'
 _TOTAL_STORAGE_LIMIT_REGEX = r'^([0-9]+(\.[0-9]*)?[BKMGT]?)'
-_TASK_AGE_LIMIT_REGEX = r'^([0-9]+(\.[0-9]*(e-?[0-9]+))?[smhd])'
 _MODE_REGEX = r'(pull)|(push)'
 
 
@@ -190,7 +189,7 @@
   """Retry parameters for a single task queue."""
   ATTRIBUTES = {
       TASK_RETRY_LIMIT: validation.Optional(validation.TYPE_INT),
-      TASK_AGE_LIMIT: validation.Optional(_TASK_AGE_LIMIT_REGEX),
+      TASK_AGE_LIMIT: validation.Optional(validation.TimeValue()),
       MIN_BACKOFF_SECONDS: validation.Optional(validation.TYPE_FLOAT),
       MAX_BACKOFF_SECONDS: validation.Optional(validation.TYPE_FLOAT),
       MAX_DOUBLINGS: validation.Optional(validation.TYPE_INT),
diff --git a/google/appengine/api/request_info.py b/google/appengine/api/request_info.py
index 9cba5cc..cb20af4 100644
--- a/google/appengine/api/request_info.py
+++ b/google/appengine/api/request_info.py
@@ -53,12 +53,12 @@
   """The requested operation is not supported for auto-scaling modules."""
 
 
-class ModuleAlreadyStartedError(Error):
-  """The module is already started."""
+class VersionAlreadyStartedError(Error):
+  """The version is already started."""
 
 
-class ModuleAlreadyStoppedError(Error):
-  """The module is already stopped."""
+class VersionAlreadyStoppedError(Error):
+  """The version is already stopped."""
 
 
 class BackgroundThreadLimitReachedError(Error):
@@ -197,8 +197,8 @@
     """
     raise NotImplementedError()
 
-  def start_module(self, module, version):
-    """Starts a module.
+  def start_version(self, module, version):
+    """Starts a version.
 
     Args:
       module: A str containing the name of the module.
@@ -212,8 +212,8 @@
     """
     raise NotImplementedError()
 
-  def stop_module(self, module, version):
-    """Stops a module.
+  def stop_version(self, module, version):
+    """Stops a version.
 
     Args:
       module: A str containing the name of the module.
@@ -449,8 +449,8 @@
 
     raise NotSupportedWithAutoScalingError()
 
-  def start_module(self, module, version):
-    """Starts a module.
+  def start_version(self, module, version):
+    """Starts a version.
 
     Args:
       module: A str containing the name of the module.
@@ -469,8 +469,8 @@
 
     raise NotSupportedWithAutoScalingError()
 
-  def stop_module(self, module, version):
-    """Stops a module.
+  def stop_version(self, module, version):
+    """Stops a version.
 
     Args:
       module: A str containing the name of the module.
diff --git a/google/appengine/api/search/simple_search_stub.py b/google/appengine/api/search/simple_search_stub.py
index 3bdb9f7..5f5bf25 100644
--- a/google/appengine/api/search/simple_search_stub.py
+++ b/google/appengine/api/search/simple_search_stub.py
@@ -525,7 +525,7 @@
         else:
           default_value = sort_spec.default_value_numeric()
         val = expression_evaluator.ExpressionEvaluator(
-            scored_doc, self._inverted_index).ValueOf(
+            scored_doc, self._inverted_index, True).ValueOf(
                 sort_spec.sort_expression(), default_value=default_value)
         if isinstance(val, datetime.datetime):
           val = search_util.EpochTime(val)
diff --git a/google/appengine/api/search/stub/expression_evaluator.py b/google/appengine/api/search/stub/expression_evaluator.py
index 7fa0c60..649639d 100644
--- a/google/appengine/api/search/stub/expression_evaluator.py
+++ b/google/appengine/api/search/stub/expression_evaluator.py
@@ -70,12 +70,14 @@
 class ExpressionEvaluator(object):
   """Evaluates an expression on scored documents."""
 
-  def __init__(self, document, inverted_index):
+  def __init__(self, document, inverted_index, is_sort_expression=False):
     """Constructor.
 
     Args:
       document: The ScoredDocument to evaluate the expression for.
       inverted_index: The search index (used for snippeting).
+      is_sort_expression: The flag indicates if this is a sort expression. Some
+        operations (such as COUNT) are not supported in sort expressions.
     """
     self._doc = document
     self._doc_pb = document.document
@@ -95,6 +97,7 @@
         ExpressionParser.SNIPPET: self._Snippet,
         ExpressionParser.SWITCH: self._Unsupported('switch'),
         }
+    self._is_sort_expression = is_sort_expression
 
   @classmethod
   def _GetFieldValue(cls, field):
@@ -153,6 +156,10 @@
     if node.getType() != ExpressionParser.NAME:
       raise _ExpressionError(
           'The argument to count() must be a simple field name')
+    if self._is_sort_expression:
+      raise query_parser.QueryException(
+          'Failed to parse sort expression \'count(' + node.getText() +
+          ')\': count() is not supported in sort expressions')
     return search_util.GetFieldCountInDocument(
         self._doc_pb, query_parser.GetQueryNodeText(node))
 
diff --git a/google/appengine/api/validation.py b/google/appengine/api/validation.py
index bb031cc..5130230 100644
--- a/google/appengine/api/validation.py
+++ b/google/appengine/api/validation.py
@@ -99,8 +99,8 @@
     Validator instance that wraps the given value.
 
   Raises:
-    AttributeDefinitionError if validator is not one of the above described
-    types.
+    AttributeDefinitionError: if validator is not one of the above described
+      types.
   """
   if isinstance(validator, (str, unicode)):
     return Regex(validator, type(validator))
@@ -156,7 +156,7 @@
       Validator associated with key or attribute.
 
     Raises:
-      ValidationError if the requested key is illegal.
+      ValidationError: if the requested key is illegal.
     """
     raise NotImplementedError('Subclasses of ValidatedBase must '
                               'override GetValidator.')
@@ -170,7 +170,7 @@
       attributes: A dict of attributes/items to set.
 
     Raises:
-      ValidationError when no validated attribute exists on class.
+      ValidationError: when no validated attribute exists on class.
     """
     for key, value in attributes.iteritems():
       self.Set(key, value)
@@ -187,7 +187,7 @@
       value: The value to set
 
     Raises:
-      ValidationError when no validated attribute exists on class.
+      ValidationError: when no validated attribute exists on class.
     """
     raise NotImplementedError('Subclasses of ValidatedBase must override Set.')
 
@@ -257,8 +257,8 @@
     keyword arguments.
 
     Raises:
-      AttributeDefinitionError when class instance is missing ATTRIBUTE
-      definition or when ATTRIBUTE is of the wrong type.
+      AttributeDefinitionError: when class instance is missing ATTRIBUTE
+        definition or when ATTRIBUTE is of the wrong type.
     """
     if not isinstance(self.ATTRIBUTES, dict):
       raise AttributeDefinitionError(
@@ -354,8 +354,8 @@
       value: Attributes new value.
 
     Raises:
-      ValidationError when trying to assign to an attribute
-      that does not exist.
+      ValidationError: when trying to assign to an attribute
+        that does not exist.
     """
     value = self.GetValidator(key)(value, key)
     object.__setattr__(self, key, value)
@@ -496,7 +496,7 @@
       value: Items new value.
 
     Raises:
-      ValidationError when trying to assign to a value that does not exist.
+      ValidationError: when trying to assign to a value that does not exist.
     """
     dict.__setitem__(self, key, self.GetValidator(key)(value, key))
 
@@ -506,7 +506,7 @@
     See the documentation for setdefault on dict for usage details.
 
     Raises:
-      ValidationError if the specified key is illegal or the
+      ValidationError: if the specified key is illegal or the
       value invalid.
     """
     return dict.setdefault(self, key, self.GetValidator(key)(value, key))
@@ -517,8 +517,8 @@
     See the documentation for update on dict for usage details.
 
     Raises:
-      ValidationError if any of the specified keys are illegal or
-      values invalid.
+      ValidationError: if any of the specified keys are illegal or
+        values invalid.
     """
     if hasattr(other, 'keys') and callable(getattr(other, 'keys')):
       newother = {}
@@ -544,7 +544,7 @@
       value: The value to set
 
     Raises:
-      ValidationError when no validated attribute exists on class.
+      ValidationError: when no validated attribute exists on class.
     """
     self[key] = value
 
@@ -742,8 +742,8 @@
       """Set new alias on alias_map.
 
       Raises:
-        AttributeDefinitionError when option already exists or if alias is
-        not of type str..
+        AttributeDefinitionError: when option already exists or if alias is
+          not of type str.
       """
       if not isinstance(alias, str):
         raise AttributeDefinitionError(
@@ -789,7 +789,7 @@
       Original value for provided alias.
 
     Raises:
-      ValidationError when value is not one of predefined values.
+      ValidationError: when value is not one of predefined values.
     """
     if value is None:
       raise ValidationError('Value for options field must not be None.')
@@ -825,7 +825,7 @@
       validator: Optional validation condition.
 
     Raises:
-      AttributeDefinitionError if validator is not callable.
+      AttributeDefinitionError: if validator is not callable.
     """
     self.validator = AsValidator(validator)
     self.expected_type = self.validator.expected_type
@@ -887,7 +887,7 @@
       regex: Regular expression string to use for comparison.
 
     Raises:
-      AttributeDefinitionError if string_type is not a kind of string.
+      AttributeDefinitionError: if string_type is not a kind of string.
     """
     super(Regex, self).__init__(default)
     if (not issubclass(string_type, basestring) or
@@ -910,8 +910,8 @@
       key: Name of the field being validated.
 
     Raises:
-      ValidationError when value does not match regular expression or
-      when value does not match provided string type.
+      ValidationError: when value does not match regular expression or
+        when value does not match provided string type.
     """
     if issubclass(self.expected_type, str):
       cast_value = TYPE_STR(value)
@@ -925,7 +925,7 @@
 
 
 class _RegexStrValue(object):
-  """Simulates the regex object to support recomplation when necessary.
+  """Simulates the regex object to support recompilation when necessary.
 
   Used by the RegexStr class to dynamically build and recompile regular
   expression attributes of a validated object.  This object replaces the normal
@@ -1046,7 +1046,7 @@
     """Initialized regex validator.
 
     Raises:
-      AttributeDefinitionError if string_type is not a kind of string.
+      AttributeDefinitionError: if string_type is not a kind of string.
     """
     if default is not None:
       default = _RegexStrValue(self, default, None)
@@ -1132,7 +1132,7 @@
       key: Name of the field being validated.
 
     Raises:
-      ValidationError when value is out of range.  ValidationError when value
+      ValidationError: when value is out of range.  ValidationError when value
       is notd of the same range type.
     """
     cast_value = self._type_validator.Validate(value, key)
@@ -1174,8 +1174,8 @@
       key: Name of the field being validated.
 
     Raises:
-      ValidationError if value is None, not a list or one of its elements is the
-      wrong type.
+      ValidationError: if value is None, not a list or one of its elements is
+        the wrong type.
     """
     if not isinstance(value, list):
       raise ValidationError('Value \'%s\' for %s should be a sequence but '
@@ -1189,3 +1189,44 @@
             str(item), key, self.constructor.__name__))
 
     return value
+
+
+class TimeValue(Validator):
+  """Validates time values with units, such as 1h or 3.5d."""
+
+  _EXPECTED_SYNTAX = ('must be a non-negative number followed by a time unit, '
+                      'such as 1h or 3.5d')
+
+  def __init__(self):
+    super(TimeValue, self).__init__()
+    self.expected_type = str
+
+  def Validate(self, value, key):
+    """Validate a time value.
+
+    Args:
+      value: Value to validate.
+      key: Name of the field being validated.
+
+    Raises:
+      ValidationError: if value is not a time value with the expected format.
+    """
+    if not isinstance(value, basestring):
+      raise ValidationError("Value '%s' for %s is not a string (%s)"
+                            % (value, key, TimeValue._EXPECTED_SYNTAX))
+    if not value:
+      raise ValidationError("Value for %s is empty (%s)"
+                            % (key, TimeValue._EXPECTED_SYNTAX))
+    if value[-1] not in "smhd":
+      raise ValidationError("Value '%s' for %s must end with a time unit, "
+                            "one of s (seconds), m (minutes), h (hours), "
+                            "or d (days)" % (value, key))
+    try:
+      t = float(value[:-1])
+    except ValueError:
+      raise ValidationError("Value '%s' for %s is not a valid time value (%s)"
+                            % (value, key, TimeValue._EXPECTED_SYNTAX))
+    if t < 0:
+      raise ValidationError("Value '%s' for %s is negative (%s)"
+                            % (value, key, TimeValue._EXPECTED_SYNTAX))
+    return value
diff --git a/google/appengine/api/xmpp/__init__.py b/google/appengine/api/xmpp/__init__.py
index 316195c..c5c9e19 100644
--- a/google/appengine/api/xmpp/__init__.py
+++ b/google/appengine/api/xmpp/__init__.py
@@ -131,6 +131,14 @@
   """Error that indicates a send presence request has an invalid status."""
 
 
+class NondefaultModuleError(Error):
+  """Error that indicates the XMPP API was used from a non-default module."""
+
+  def __init__(self):
+    super(NondefaultModuleError, self).__init__(
+        'XMPP API does not support modules')
+
+
 def get_presence(jid, from_jid=None, get_show=False):
   """Gets the presence for a JID.
 
@@ -192,6 +200,9 @@
 
 
       raise InvalidJidError()
+    elif (e.application_error ==
+          xmpp_service_pb.XmppServiceError.NONDEFAULT_MODULE):
+      raise NondefaultModuleError()
     else:
       raise Error()
 
@@ -253,6 +264,9 @@
     if (e.application_error ==
         xmpp_service_pb.XmppServiceError.INVALID_JID):
       raise InvalidJidError()
+    elif (e.application_error ==
+          xmpp_service_pb.XmppServiceError.NONDEFAULT_MODULE):
+      raise NondefaultModuleError()
     else:
       raise Error()
 
@@ -338,6 +352,9 @@
     elif (e.application_error ==
           xmpp_service_pb.XmppServiceError.NO_BODY):
       raise NoBodyError()
+    elif (e.application_error ==
+          xmpp_service_pb.XmppServiceError.NONDEFAULT_MODULE):
+      raise NondefaultModuleError()
     raise Error()
 
   if single_jid:
@@ -414,12 +431,11 @@
     elif (e.application_error ==
           xmpp_service_pb.XmppServiceError.INVALID_SHOW):
       raise InvalidShowError()
+    elif (e.application_error ==
+          xmpp_service_pb.XmppServiceError.NONDEFAULT_MODULE):
+      raise NondefaultModuleError()
     raise Error()
 
-  return
-
-
-
 
 class Message(object):
   """Encapsulates an XMPP message received by the application."""
diff --git a/google/appengine/api/xmpp/xmpp_service_pb.py b/google/appengine/api/xmpp/xmpp_service_pb.py
index 616fc40..9d8521d 100644
--- a/google/appengine/api/xmpp/xmpp_service_pb.py
+++ b/google/appengine/api/xmpp/xmpp_service_pb.py
@@ -46,6 +46,7 @@
   INVALID_SHOW =    6
   EXCEEDED_MAX_SIZE =    7
   APPID_ALIAS_REQUIRED =    8
+  NONDEFAULT_MODULE =    9
 
   _ErrorCode_NAMES = {
     1: "UNSPECIFIED_ERROR",
@@ -56,6 +57,7 @@
     6: "INVALID_SHOW",
     7: "EXCEEDED_MAX_SIZE",
     8: "APPID_ALIAS_REQUIRED",
+    9: "NONDEFAULT_MODULE",
   }
 
   def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
diff --git a/google/appengine/datastore/datastore_pb.py b/google/appengine/datastore/datastore_pb.py
index 7fc28f4..5ba578f 100644
--- a/google/appengine/datastore/datastore_pb.py
+++ b/google/appengine/datastore/datastore_pb.py
@@ -3525,6 +3525,8 @@
   commitcost_ = None
   has_approximate_storage_delta_ = 0
   approximate_storage_delta_ = 0
+  has_id_sequence_updates_ = 0
+  id_sequence_updates_ = 0
 
   def __init__(self, contents=None):
     self.lazy_init_lock_ = thread.allocate_lock()
@@ -3614,6 +3616,19 @@
 
   def has_approximate_storage_delta(self): return self.has_approximate_storage_delta_
 
+  def id_sequence_updates(self): return self.id_sequence_updates_
+
+  def set_id_sequence_updates(self, x):
+    self.has_id_sequence_updates_ = 1
+    self.id_sequence_updates_ = x
+
+  def clear_id_sequence_updates(self):
+    if self.has_id_sequence_updates_:
+      self.has_id_sequence_updates_ = 0
+      self.id_sequence_updates_ = 0
+
+  def has_id_sequence_updates(self): return self.has_id_sequence_updates_
+
 
   def MergeFrom(self, x):
     assert x is not self
@@ -3623,6 +3638,7 @@
     if (x.has_entity_write_bytes()): self.set_entity_write_bytes(x.entity_write_bytes())
     if (x.has_commitcost()): self.mutable_commitcost().MergeFrom(x.commitcost())
     if (x.has_approximate_storage_delta()): self.set_approximate_storage_delta(x.approximate_storage_delta())
+    if (x.has_id_sequence_updates()): self.set_id_sequence_updates(x.id_sequence_updates())
 
   def Equals(self, x):
     if x is self: return 1
@@ -3638,6 +3654,8 @@
     if self.has_commitcost_ and self.commitcost_ != x.commitcost_: return 0
     if self.has_approximate_storage_delta_ != x.has_approximate_storage_delta_: return 0
     if self.has_approximate_storage_delta_ and self.approximate_storage_delta_ != x.approximate_storage_delta_: return 0
+    if self.has_id_sequence_updates_ != x.has_id_sequence_updates_: return 0
+    if self.has_id_sequence_updates_ and self.id_sequence_updates_ != x.id_sequence_updates_: return 0
     return 1
 
   def IsInitialized(self, debug_strs=None):
@@ -3653,6 +3671,7 @@
     if (self.has_entity_write_bytes_): n += 1 + self.lengthVarInt64(self.entity_write_bytes_)
     if (self.has_commitcost_): n += 2 + self.commitcost_.ByteSize()
     if (self.has_approximate_storage_delta_): n += 1 + self.lengthVarInt64(self.approximate_storage_delta_)
+    if (self.has_id_sequence_updates_): n += 1 + self.lengthVarInt64(self.id_sequence_updates_)
     return n
 
   def ByteSizePartial(self):
@@ -3663,6 +3682,7 @@
     if (self.has_entity_write_bytes_): n += 1 + self.lengthVarInt64(self.entity_write_bytes_)
     if (self.has_commitcost_): n += 2 + self.commitcost_.ByteSizePartial()
     if (self.has_approximate_storage_delta_): n += 1 + self.lengthVarInt64(self.approximate_storage_delta_)
+    if (self.has_id_sequence_updates_): n += 1 + self.lengthVarInt64(self.id_sequence_updates_)
     return n
 
   def Clear(self):
@@ -3672,6 +3692,7 @@
     self.clear_entity_write_bytes()
     self.clear_commitcost()
     self.clear_approximate_storage_delta()
+    self.clear_id_sequence_updates()
 
   def OutputUnchecked(self, out):
     if (self.has_index_writes_):
@@ -3693,6 +3714,9 @@
     if (self.has_approximate_storage_delta_):
       out.putVarInt32(64)
       out.putVarInt32(self.approximate_storage_delta_)
+    if (self.has_id_sequence_updates_):
+      out.putVarInt32(72)
+      out.putVarInt32(self.id_sequence_updates_)
 
   def OutputPartial(self, out):
     if (self.has_index_writes_):
@@ -3714,6 +3738,9 @@
     if (self.has_approximate_storage_delta_):
       out.putVarInt32(64)
       out.putVarInt32(self.approximate_storage_delta_)
+    if (self.has_id_sequence_updates_):
+      out.putVarInt32(72)
+      out.putVarInt32(self.id_sequence_updates_)
 
   def TryMerge(self, d):
     while d.avail() > 0:
@@ -3736,6 +3763,9 @@
       if tt == 64:
         self.set_approximate_storage_delta(d.getVarInt32())
         continue
+      if tt == 72:
+        self.set_id_sequence_updates(d.getVarInt32())
+        continue
 
 
       if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
@@ -3753,6 +3783,7 @@
       res+=self.commitcost_.__str__(prefix + "  ", printElemNumber)
       res+=prefix+"}\n"
     if self.has_approximate_storage_delta_: res+=prefix+("approximate_storage_delta: %s\n" % self.DebugFormatInt32(self.approximate_storage_delta_))
+    if self.has_id_sequence_updates_: res+=prefix+("id_sequence_updates: %s\n" % self.DebugFormatInt32(self.id_sequence_updates_))
     return res
 
 
@@ -3767,6 +3798,7 @@
   kCommitCostrequested_entity_puts = 6
   kCommitCostrequested_entity_deletes = 7
   kapproximate_storage_delta = 8
+  kid_sequence_updates = 9
 
   _TEXT = _BuildTagLookupTable({
     0: "ErrorCode",
@@ -3778,7 +3810,8 @@
     6: "requested_entity_puts",
     7: "requested_entity_deletes",
     8: "approximate_storage_delta",
-  }, 8)
+    9: "id_sequence_updates",
+  }, 9)
 
   _TYPES = _BuildTagLookupTable({
     0: ProtocolBuffer.Encoder.NUMERIC,
@@ -3790,7 +3823,8 @@
     6: ProtocolBuffer.Encoder.NUMERIC,
     7: ProtocolBuffer.Encoder.NUMERIC,
     8: ProtocolBuffer.Encoder.NUMERIC,
-  }, 8, ProtocolBuffer.Encoder.MAX_TYPE)
+    9: ProtocolBuffer.Encoder.NUMERIC,
+  }, 9, ProtocolBuffer.Encoder.MAX_TYPE)
 
 
   _STYLE = """"""
@@ -6816,8 +6850,11 @@
   start_ = 0
   has_end_ = 0
   end_ = 0
+  has_cost_ = 0
+  cost_ = None
 
   def __init__(self, contents=None):
+    self.lazy_init_lock_ = thread.allocate_lock()
     if contents is not None: self.MergeFromString(contents)
 
   def start(self): return self.start_
@@ -6846,11 +6883,31 @@
 
   def has_end(self): return self.has_end_
 
+  def cost(self):
+    if self.cost_ is None:
+      self.lazy_init_lock_.acquire()
+      try:
+        if self.cost_ is None: self.cost_ = Cost()
+      finally:
+        self.lazy_init_lock_.release()
+    return self.cost_
+
+  def mutable_cost(self): self.has_cost_ = 1; return self.cost()
+
+  def clear_cost(self):
+
+    if self.has_cost_:
+      self.has_cost_ = 0;
+      if self.cost_ is not None: self.cost_.Clear()
+
+  def has_cost(self): return self.has_cost_
+
 
   def MergeFrom(self, x):
     assert x is not self
     if (x.has_start()): self.set_start(x.start())
     if (x.has_end()): self.set_end(x.end())
+    if (x.has_cost()): self.mutable_cost().MergeFrom(x.cost())
 
   def Equals(self, x):
     if x is self: return 1
@@ -6858,6 +6915,8 @@
     if self.has_start_ and self.start_ != x.start_: return 0
     if self.has_end_ != x.has_end_: return 0
     if self.has_end_ and self.end_ != x.end_: return 0
+    if self.has_cost_ != x.has_cost_: return 0
+    if self.has_cost_ and self.cost_ != x.cost_: return 0
     return 1
 
   def IsInitialized(self, debug_strs=None):
@@ -6870,12 +6929,14 @@
       initialized = 0
       if debug_strs is not None:
         debug_strs.append('Required field: end not set.')
+    if (self.has_cost_ and not self.cost_.IsInitialized(debug_strs)): initialized = 0
     return initialized
 
   def ByteSize(self):
     n = 0
     n += self.lengthVarInt64(self.start_)
     n += self.lengthVarInt64(self.end_)
+    if (self.has_cost_): n += 1 + self.lengthString(self.cost_.ByteSize())
     return n + 2
 
   def ByteSizePartial(self):
@@ -6886,17 +6947,23 @@
     if (self.has_end_):
       n += 1
       n += self.lengthVarInt64(self.end_)
+    if (self.has_cost_): n += 1 + self.lengthString(self.cost_.ByteSizePartial())
     return n
 
   def Clear(self):
     self.clear_start()
     self.clear_end()
+    self.clear_cost()
 
   def OutputUnchecked(self, out):
     out.putVarInt32(8)
     out.putVarInt64(self.start_)
     out.putVarInt32(16)
     out.putVarInt64(self.end_)
+    if (self.has_cost_):
+      out.putVarInt32(26)
+      out.putVarInt32(self.cost_.ByteSize())
+      self.cost_.OutputUnchecked(out)
 
   def OutputPartial(self, out):
     if (self.has_start_):
@@ -6905,6 +6972,10 @@
     if (self.has_end_):
       out.putVarInt32(16)
       out.putVarInt64(self.end_)
+    if (self.has_cost_):
+      out.putVarInt32(26)
+      out.putVarInt32(self.cost_.ByteSizePartial())
+      self.cost_.OutputPartial(out)
 
   def TryMerge(self, d):
     while d.avail() > 0:
@@ -6915,6 +6986,12 @@
       if tt == 16:
         self.set_end(d.getVarInt64())
         continue
+      if tt == 26:
+        length = d.getVarInt32()
+        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+        d.skip(length)
+        self.mutable_cost().TryMerge(tmp)
+        continue
 
 
       if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
@@ -6925,6 +7002,10 @@
     res=""
     if self.has_start_: res+=prefix+("start: %s\n" % self.DebugFormatInt64(self.start_))
     if self.has_end_: res+=prefix+("end: %s\n" % self.DebugFormatInt64(self.end_))
+    if self.has_cost_:
+      res+=prefix+"cost <\n"
+      res+=self.cost_.__str__(prefix + "  ", printElemNumber)
+      res+=prefix+">\n"
     return res
 
 
@@ -6933,18 +7014,21 @@
 
   kstart = 1
   kend = 2
+  kcost = 3
 
   _TEXT = _BuildTagLookupTable({
     0: "ErrorCode",
     1: "start",
     2: "end",
-  }, 2)
+    3: "cost",
+  }, 3)
 
   _TYPES = _BuildTagLookupTable({
     0: ProtocolBuffer.Encoder.NUMERIC,
     1: ProtocolBuffer.Encoder.NUMERIC,
     2: ProtocolBuffer.Encoder.NUMERIC,
-  }, 2, ProtocolBuffer.Encoder.MAX_TYPE)
+    3: ProtocolBuffer.Encoder.STRING,
+  }, 3, ProtocolBuffer.Encoder.MAX_TYPE)
 
 
   _STYLE = """"""
diff --git a/google/appengine/datastore/datastore_pbs.py b/google/appengine/datastore/datastore_pbs.py
index 9bb3fbf..e44b891 100644
--- a/google/appengine/datastore/datastore_pbs.py
+++ b/google/appengine/datastore/datastore_pbs.py
@@ -123,6 +123,20 @@
   return '[%s]' % ', '.join(path_element_strings)
 
 
+def is_complete_v4_key(v4_key):
+  """Returns True if a key specifies an ID or name, False otherwise.
+
+  Args:
+    v4_key: an entity_v4_pb.Key
+
+  Returns:
+    True if the key specifies an ID or name, False otherwise.
+  """
+  assert len(v4_key.path_element_list()) >= 1
+  last_element = v4_key.path_element(len(v4_key.path_element_list()) - 1)
+  return last_element.has_id() or last_element.has_name()
+
+
 def is_valid_utf8(s):
   try:
     s.decode('utf-8')
@@ -248,9 +262,7 @@
       v4_key = v4_entity.key()
       self.v4_to_v3_reference(v4_key, v3_entity.mutable_key())
       v3_ref = v3_entity.key()
-      if (self.__v3_reference_has_id_or_name(v3_ref)
-          or v3_ref.path().element_size() > 1):
-        self.v3_reference_to_group(v3_ref, v3_entity.mutable_entity_group())
+      self.v3_reference_to_group(v3_ref, v3_entity.mutable_entity_group())
     else:
 
 
diff --git a/google/appengine/datastore/datastore_query.py b/google/appengine/datastore/datastore_query.py
index 35f8785..d60edb0 100644
--- a/google/appengine/datastore/datastore_query.py
+++ b/google/appengine/datastore/datastore_query.py
@@ -2515,9 +2515,9 @@
     Returns:
       A UserRPC object that can be used to fetch the result of the RPC.
     """
-    return self._batch_shared.conn.make_rpc_call(config, name, req,
-                                                 datastore_pb.QueryResult(),
-                                                 self.__query_result_hook)
+    return self._batch_shared.conn._make_rpc_call(config, name, req,
+                                                  datastore_pb.QueryResult(),
+                                                  self.__query_result_hook)
 
   _need_index_header = 'The suggested index for this query is:'
 
diff --git a/google/appengine/datastore/datastore_rpc.py b/google/appengine/datastore/datastore_rpc.py
index 196ef26..b85d3ee 100644
--- a/google/appengine/datastore/datastore_rpc.py
+++ b/google/appengine/datastore/datastore_rpc.py
@@ -51,6 +51,7 @@
 
 
 import collections
+import copy
 import functools
 import logging
 
@@ -69,6 +70,7 @@
 from google.appengine.datastore import datastore_pb
 from google.appengine.datastore import datastore_pbs
 from google.appengine.datastore import datastore_v4_pb
+from google.appengine.datastore import entity_v4_pb
 from google.appengine.runtime import apiproxy_errors
 
 
@@ -78,6 +80,10 @@
 _MAX_ID_BATCH_SIZE = 1000 * 1000 * 1000
 
 
+_DATASTORE_V3 = 'datastore_v3'
+_DATASTORE_V4 = 'datastore_v4'
+
+
 
 def _positional(max_pos_args):
   """A decorator to declare that only the first N arguments may be positional.
@@ -141,10 +147,22 @@
     """Turn an entity_pb.Reference into a user-level key."""
     raise NotImplementedError
 
+  def pb_v4_to_key(self, pb):
+    """Turn an entity_v4_pb.Key into a user-level key."""
+    v3_ref = entity_pb.Reference()
+    datastore_pbs.get_entity_converter().v4_to_v3_reference(pb, v3_ref)
+    return self.pb_to_key(v3_ref)
+
   def pb_to_entity(self, pb):
     """Turn an entity_pb.EntityProto into a user-level entity."""
     raise NotImplementedError
 
+  def pb_v4_to_entity(self, pb):
+    """Turn an entity_v4_pb.Entity into a user-level entity."""
+    v3_entity = entity_pb.EntityProto()
+    datastore_pbs.get_entity_converter().v4_to_v3_entity(pb, v3_entity)
+    return self.pb_to_entity(v3_entity)
+
   def pb_to_index(self, pb):
     """Turn an entity_pb.CompositeIndex into a user-level Index
     representation."""
@@ -161,10 +179,24 @@
     """Turn a user-level key into an entity_pb.Reference."""
     raise NotImplementedError
 
+  def key_to_pb_v4(self, key):
+    """Turn a user-level key into an entity_v4_pb.Key."""
+    v3_ref = self.key_to_pb(key)
+    v4_key = entity_v4_pb.Key()
+    datastore_pbs.get_entity_converter().v3_to_v4_key(v3_ref, v4_key)
+    return v4_key
+
   def entity_to_pb(self, entity):
     """Turn a user-level entity into an entity_pb.EntityProto."""
     raise NotImplementedError
 
+  def entity_to_pb_v4(self, entity):
+    """Turn a user-level entity into an entity_v4_pb.Key."""
+    v3_entity = self.entity_to_pb(entity)
+    v4_entity = entity_v4_pb.Entity()
+    datastore_pbs.get_entity_converter().v3_to_v4_entity(v3_entity, v4_entity)
+    return v4_entity
+
   def new_key_pb(self):
     """Create a new, empty entity_pb.Reference."""
     return entity_pb.Reference()
@@ -716,6 +748,26 @@
         'max_delete_keys should be a positive integer')
     return value
 
+
+class _StubRpc(object):
+  """A stub RPC implementation.
+
+  Returns a hard-coded result provided at construction time.
+  """
+
+  def __init__(self, result):
+    self.__result = result
+
+  def wait(self):
+    pass
+
+  def check_success(self):
+    pass
+
+  def get_result(self):
+    return self.__result
+
+
 class MultiRpc(object):
   """A wrapper around multiple UserRPC objects.
 
@@ -961,8 +1013,10 @@
   MASTER_SLAVE_DATASTORE = 1
   HIGH_REPLICATION_DATASTORE = 2
 
+  __SUPPORTED_VERSIONS = frozenset((_DATASTORE_V3, _DATASTORE_V4))
+
   @_positional(1)
-  def __init__(self, adapter=None, config=None):
+  def __init__(self, adapter=None, config=None, _api_version=_DATASTORE_V3):
     """Constructor.
 
     All arguments should be specified as keyword arguments.
@@ -976,16 +1030,21 @@
       adapter = IdentityAdapter()
     if not isinstance(adapter, AbstractAdapter):
       raise datastore_errors.BadArgumentError(
-        'invalid adapter argument (%r)' % (adapter,))
+          'invalid adapter argument (%r)' % (adapter,))
     self.__adapter = adapter
 
     if config is None:
       config = Configuration()
     elif not Configuration.is_configuration(config):
       raise datastore_errors.BadArgumentError(
-        'invalid config argument (%r)' % (config,))
+          'invalid config argument (%r)' % (config,))
     self.__config = config
 
+    if _api_version not in self.__SUPPORTED_VERSIONS:
+      raise datastore_errors.BadArgumentError(
+          'unsupported API version (%s)' % (_api_version,))
+    self._api_version = _api_version
+
     self.__pending_rpcs = set()
 
 
@@ -1097,9 +1156,11 @@
 
 
 
-  def create_rpc(self, config=None, service_name='datastore_v3'):
+  def _create_rpc(self, config=None, service_name=None):
     """Create an RPC object using the configuration parameters.
 
+    Internal only.
+
     Args:
       config: Optional Configuration object.
       service_name: Optional datastore service name.
@@ -1120,6 +1181,13 @@
     deadline = Configuration.deadline(config, self.__config)
     on_completion = Configuration.on_completion(config, self.__config)
     callback = None
+    if service_name is None:
+
+
+
+
+
+      service_name = self._api_version
     if on_completion is not None:
 
 
@@ -1128,20 +1196,25 @@
     rpc = apiproxy_stub_map.UserRPC(service_name, deadline, callback)
     return rpc
 
+
+  create_rpc = _create_rpc
+
   def _set_request_read_policy(self, request, config=None):
     """Set the read policy on a request.
 
     This takes the read policy from the config argument or the
-    configuration's default configuration, and if it is
-    EVENTUAL_CONSISTENCY, sets the failover_ms field in the protobuf.
+    configuration's default configuration, and sets the request's read
+    options.
 
     Args:
-      request: A protobuf with a failover_ms field.
+      request: A read request protobuf.
       config: Optional Configuration object.
+
+    Returns:
+      True if the read policy specifies a read current request, False if it
+        specifies an eventually consistent request, None if it does
+        not specify a read consistency.
     """
-    if not (hasattr(request, 'set_failover_ms') and hasattr(request, 'strong')):
-      raise datastore_errors.BadRequestError(
-          'read_policy is only supported on read operations.')
 
     if isinstance(config, apiproxy_stub_map.UserRPC):
       read_policy = getattr(config, 'read_policy', None)
@@ -1152,14 +1225,33 @@
     if read_policy is None:
       read_policy = self.__config.read_policy
 
-    if read_policy == Configuration.APPLY_ALL_JOBS_CONSISTENCY:
-      request.set_strong(True)
-    elif read_policy == Configuration.EVENTUAL_CONSISTENCY:
-      request.set_strong(False)
+    if hasattr(request, 'set_failover_ms') and hasattr(request, 'strong'):
+
+      if read_policy == Configuration.APPLY_ALL_JOBS_CONSISTENCY:
+        request.set_strong(True)
+        return True
+      elif read_policy == Configuration.EVENTUAL_CONSISTENCY:
+        request.set_strong(False)
 
 
 
-      request.set_failover_ms(-1)
+        request.set_failover_ms(-1)
+        return False
+      else:
+        return None
+    elif hasattr(request, 'read_options'):
+
+
+
+      if read_policy == Configuration.EVENTUAL_CONSISTENCY:
+        request.mutable_read_options().set_read_consistency(
+            datastore_v4_pb.ReadOptions.EVENTUAL)
+        return False
+      else:
+        return None
+    else:
+      raise datastore_errors.BadRequestError(
+          'read_policy is only supported on read operations.')
 
   def _set_request_transaction(self, request):
     """Set the current transaction on a request.
@@ -1171,15 +1263,17 @@
       request: A protobuf with a transaction field.
 
     Returns:
-      A datastore_pb.Transaction object or None.
+      An object representing a transaction or None.
     """
     return None
 
-  def make_rpc_call(self, config, method, request, response,
-                    get_result_hook=None, user_data=None,
-                    service_name='datastore_v3'):
+  def _make_rpc_call(self, config, method, request, response,
+                     get_result_hook=None, user_data=None,
+                     service_name=None):
     """Make an RPC call.
 
+    Internal only.
+
     Except for the added config argument, this is a thin wrapper
     around UserRPC.make_call().
 
@@ -1204,11 +1298,14 @@
     if isinstance(config, apiproxy_stub_map.UserRPC):
       rpc = config
     else:
-      rpc = self.create_rpc(config, service_name)
+      rpc = self._create_rpc(config, service_name)
     rpc.make_call(method, request, response, get_result_hook, user_data)
     self._add_pending(rpc)
     return rpc
 
+
+  make_rpc_call = _make_rpc_call
+
   def check_rpc_success(self, rpc):
     """Check for RPC success and translate exceptions.
 
@@ -1257,10 +1354,31 @@
         config, self.__config) or self.DEFAULT_MAX_ENTITY_GROUPS_PER_RPC
 
   def _extract_entity_group(self, value):
-    """Internal helper: extracts the entity group from a key or entity."""
-    if isinstance(value, entity_pb.EntityProto):
+    """Internal helper: extracts the entity group from a key or entity.
+
+    Supports both v3 and v4 protobufs.
+
+    Args:
+      value: an entity_pb.{Reference, EntityProto} or
+          entity_v4_pb.{Key, Entity}.
+
+    Returns:
+      A tuple consisting of:
+        - kind
+        - name, id, or ('new', unique id)
+    """
+    if (isinstance(value, entity_v4_pb.Entity)
+        or isinstance(value, entity_pb.EntityProto)):
       value = value.key()
-    return value.path().element(0)
+    if isinstance(value, entity_v4_pb.Key):
+      elem = value.path_element(0)
+      kind = elem.kind()
+    else:
+      elem = value.path().element(0)
+      kind = elem.type()
+
+
+    return (kind, elem.id() or elem.name() or ('new', id(elem)))
 
   def _map_and_group(self, values, map_fn, group_fn):
     """Internal helper: map values to keys and group by key. Here key is any
@@ -1283,25 +1401,6 @@
       indexed_key_groups[group_fn(key)].append((key, index))
     return indexed_key_groups.values()
 
-  def __group_indexed_pbs_by_entity_group(self, values, to_ref):
-    """Internal helper: group pbs by entity group.
-
-    Args:
-      values: The values to be grouped by entity group.
-      to_ref: A function that translates a value to a Reference pb.
-
-    Returns:
-      A list where each element is a list of (pb, index) pairs.  Here index is
-      the location of the value from which pb was derived in the original list.
-    """
-    def get_entity_group(ref):
-      eg = self._extract_entity_group(ref)
-
-
-      return (eg.type(), eg.id() or eg.name() or ('new', id(eg)))
-
-    return self._map_and_group(values, to_ref, get_entity_group)
-
   def __create_result_index_pairs(self, indexes):
     """Internal helper: build a function that ties an index with each result.
 
@@ -1310,7 +1409,6 @@
         that the result at location y in the result list needs to be at location
         x in the list of results returned to the user.
     """
-
     def create_result_index_pairs(results):
       return zip(results, indexes)
     return create_result_index_pairs
@@ -1396,9 +1494,12 @@
         size += incr_size
     yield (pbs, pb_indexes)
 
-  def _get_base_size(self, base_req):
-    """Internal helper: return request size in bytes."""
-    return base_req.ByteSize()
+  def __force(self, req):
+    """Configure a request to force mutations."""
+    if isinstance(req, datastore_v4_pb.CommitRequest):
+      req.mutable_mutation().set_force(True)
+    else:
+      req.set_force(True)
 
   def get(self, keys):
     """Synchronous Get operation.
@@ -1427,10 +1528,15 @@
       A MultiRpc object.
     """
 
-    def make_get_call(req, pbs, extra_hook=None):
+    def make_get_call(base_req, pbs, extra_hook=None):
+      req = copy.deepcopy(base_req)
       req.key_list().extend(pbs)
-      self._set_request_transaction(req)
-      resp = datastore_pb.GetResponse()
+      if self._api_version == _DATASTORE_V4:
+        method = 'Lookup'
+        resp = datastore_v4_pb.LookupResponse()
+      else:
+        method = 'Get'
+        resp = datastore_pb.GetResponse()
 
 
 
@@ -1438,51 +1544,54 @@
 
 
 
-      return self.make_rpc_call(config, 'Get', req, resp,
-                                get_result_hook=self.__get_hook,
-                                user_data=(config, pbs, extra_hook))
+      user_data = config, pbs, extra_hook
+      return self._make_rpc_call(config, method, req, resp,
+                                 get_result_hook=self.__get_hook,
+                                 user_data=user_data,
+                                 service_name=self._api_version)
 
-    base_req = datastore_pb.GetRequest()
-    base_req.set_allow_deferred(True)
-    self._set_request_read_policy(base_req, config)
+    if self._api_version == _DATASTORE_V4:
+      base_req = datastore_v4_pb.LookupRequest()
+      key_to_pb = self.__adapter.key_to_pb_v4
+    else:
+      base_req = datastore_pb.GetRequest()
+      base_req.set_allow_deferred(True)
+      key_to_pb = self.__adapter.key_to_pb
+    is_read_current = self._set_request_read_policy(base_req, config)
+    txn = self._set_request_transaction(base_req)
 
 
     if isinstance(config, apiproxy_stub_map.UserRPC) or len(keys) <= 1:
-      pbs = [self.__adapter.key_to_pb(key) for key in keys]
+      pbs = [key_to_pb(key) for key in keys]
       return make_get_call(base_req, pbs, extra_hook)
 
-    base_size = self._get_base_size(base_req)
     max_count = (Configuration.max_get_keys(config, self.__config) or
                  self.MAX_GET_KEYS)
 
-    if base_req.has_strong():
-      is_read_current = base_req.strong()
-    else:
+    indexed_keys_by_entity_group = self._map_and_group(
+        keys, key_to_pb, self._extract_entity_group)
+
+    if is_read_current is None:
       is_read_current = (self.get_datastore_type() ==
                          BaseConnection.HIGH_REPLICATION_DATASTORE)
 
-    indexed_keys_by_entity_group = self.__group_indexed_pbs_by_entity_group(
-        keys, self.__adapter.key_to_pb)
 
 
 
-
-    if is_read_current and not base_req.has_transaction():
+    if is_read_current and txn is None:
       max_egs_per_rpc = self.__get_max_entity_groups_per_rpc(config)
     else:
       max_egs_per_rpc = None
 
 
 
-    pbsgen = self._generate_pb_lists(
-        indexed_keys_by_entity_group, base_size, max_count, max_egs_per_rpc,
-        config)
+    pbsgen = self._generate_pb_lists(indexed_keys_by_entity_group,
+                                     base_req.ByteSize(), max_count,
+                                     max_egs_per_rpc, config)
 
     rpcs = []
     for pbs, indexes in pbsgen:
-      req = datastore_pb.GetRequest()
-      req.CopyFrom(base_req)
-      rpcs.append(make_get_call(req, pbs,
+      rpcs.append(make_get_call(base_req, pbs,
                                 self.__create_result_index_pairs(indexes)))
     return MultiRpc(rpcs, self.__sort_result_index_pairs(extra_hook))
 
@@ -1492,9 +1601,9 @@
 
 
 
-    config, refs_from_request, extra_hook = rpc.user_data
+    config, keys_from_request, extra_hook = rpc.user_data
 
-    if rpc.response.in_order():
+    if self._api_version == _DATASTORE_V3 and rpc.response.in_order():
 
 
       entities = []
@@ -1513,16 +1622,20 @@
 
 
 
-      deferred_req = datastore_pb.GetRequest()
-      deferred_req.CopyFrom(rpc.request)
-      while current_get_response.deferred_size() > 0:
+      if self._api_version == _DATASTORE_V4:
+        method = 'Lookup'
+        deferred_resp = datastore_v4_pb.LookupResponse()
+      else:
+        method = 'Get'
+        deferred_resp = datastore_pb.GetResponse()
+      deferred_req = copy.deepcopy(rpc.request)
+      while current_get_response.deferred_list():
         deferred_req.clear_key()
         deferred_req.key_list().extend(current_get_response.deferred_list())
-
-        deferred_rpc = self.make_rpc_call(config,
-                                          'Get',
-                                          deferred_req,
-                                          datastore_pb.GetResponse())
+        deferred_resp.Clear()
+        deferred_rpc = self._make_rpc_call(config, method,
+                                           deferred_req, deferred_resp,
+                                           service_name=self._api_version)
         deferred_rpc.get_result()
         current_get_response = deferred_rpc.response
 
@@ -1532,8 +1645,8 @@
 
 
 
-      entities = [result_dict.get(datastore_types.ReferenceToKeyValue(ref_pb))
-                  for ref_pb in refs_from_request]
+      entities = [result_dict.get(datastore_types.ReferenceToKeyValue(pb))
+                  for pb in keys_from_request]
 
 
 
@@ -1543,27 +1656,34 @@
     return entities
 
   def __add_get_response_entities_to_dict(self, get_response, result_dict):
-    """Converts entities from the GetResponse and adds them to the dict.
+    """Converts entities from the get response and adds them to the dict.
 
     The Key for the dict will be calculated via
     datastore_types.ReferenceToKeyValue.  There will be no entry for entities
     that were not found.
 
     Args:
-      get_response: A datastore_pb.GetResponse.
+      get_response: A datastore_pb.GetResponse or
+          datastore_v4_pb.LookupResponse.
       result_dict: The dict to add results to.
     """
-    for entity_result in get_response.entity_list():
+    if isinstance(get_response, datastore_v4_pb.LookupResponse):
+      for result in get_response.found_list():
+        v4_key = result.entity().key()
+        entity = self.__adapter.pb_v4_to_entity(result.entity())
+        result_dict[datastore_types.ReferenceToKeyValue(v4_key)] = entity
+    else:
+      for entity_result in get_response.entity_list():
 
-      if entity_result.has_entity():
+        if entity_result.has_entity():
 
 
 
 
-        reference_pb = entity_result.entity().key()
-        hashable_key = datastore_types.ReferenceToKeyValue(reference_pb)
-        entity = self.__adapter.pb_to_entity(entity_result.entity())
-        result_dict[hashable_key] = entity
+          reference_pb = entity_result.entity().key()
+          hashable_key = datastore_types.ReferenceToKeyValue(reference_pb)
+          entity = self.__adapter.pb_to_entity(entity_result.entity())
+          result_dict[hashable_key] = entity
 
   def get_indexes(self):
     """Synchronous get indexes operation.
@@ -1587,8 +1707,10 @@
     req = api_base_pb.StringProto()
     req.set_value(datastore_types.ResolveAppId(_app))
     resp = datastore_pb.CompositeIndices()
-    return self.make_rpc_call(config, 'GetIndices', req, resp,
-                                self.__get_indexes_hook, extra_hook)
+    return self._make_rpc_call(config, 'GetIndices', req, resp,
+                               get_result_hook=self.__get_indexes_hook,
+                               user_data=extra_hook,
+                               service_name=_DATASTORE_V3)
 
   def __get_indexes_hook(self, rpc):
     """Internal method used as get_result_hook for Get operation."""
@@ -1631,54 +1753,88 @@
     *not* patch up those entities with the complete key.
     """
 
-    def make_put_call(req, pbs, user_data=None):
-      req.entity_list().extend(pbs)
-      self._set_request_transaction(req)
-      resp = datastore_pb.PutResponse()
-      return self.make_rpc_call(config, 'Put', req, resp,
-                                self.__put_hook, user_data)
+    def make_put_call(base_req, pbs, user_data=None):
+      req = copy.deepcopy(base_req)
+      if self._api_version == _DATASTORE_V4:
+        mutation = req.mutable_mutation()
+        for entity in pbs:
+          if datastore_pbs.is_complete_v4_key(entity.key()):
+            mutation.upsert_list().append(entity)
+          else:
+            mutation.insert_auto_id_list().append(entity)
+        method = 'Commit'
+        resp = datastore_v4_pb.CommitResponse()
+      else:
+        req.entity_list().extend(pbs)
+        method = 'Put'
+        resp = datastore_pb.PutResponse()
+      user_data = pbs, user_data
+      return self._make_rpc_call(config, method, req, resp,
+                                 get_result_hook=self.__put_hook,
+                                 user_data=user_data,
+                                 service_name=self._api_version)
 
 
-    base_req = datastore_pb.PutRequest()
+    if self._api_version == _DATASTORE_V4:
+      base_req = datastore_v4_pb.CommitRequest()
+      base_req.set_mode(datastore_v4_pb.CommitRequest.NON_TRANSACTIONAL)
+      entity_to_pb = self.__adapter.entity_to_pb_v4
+    else:
+      base_req = datastore_pb.PutRequest()
+      entity_to_pb = self.__adapter.entity_to_pb
+    self._set_request_transaction(base_req)
     if Configuration.force_writes(config, self.__config):
-      base_req.set_force(True)
+      self.__force(base_req)
 
 
     if isinstance(config, apiproxy_stub_map.UserRPC) or len(entities) <= 1:
-      pbs = [self.__adapter.entity_to_pb(entity) for entity in entities]
+      pbs = [entity_to_pb(entity) for entity in entities]
       return make_put_call(base_req, pbs, extra_hook)
 
-    base_size = self._get_base_size(base_req)
     max_count = (Configuration.max_put_entities(config, self.__config) or
                  self.MAX_PUT_ENTITIES)
-    rpcs = []
-    indexed_entities_by_entity_group = self.__group_indexed_pbs_by_entity_group(
-        entities, self.__adapter.entity_to_pb)
     if not base_req.has_transaction():
       max_egs_per_rpc = self.__get_max_entity_groups_per_rpc(config)
     else:
       max_egs_per_rpc = None
 
-    pbsgen = self._generate_pb_lists(
-        indexed_entities_by_entity_group, base_size, max_count, max_egs_per_rpc,
-        config)
+    indexed_entities_by_entity_group = self._map_and_group(
+        entities, entity_to_pb, self._extract_entity_group)
 
+
+
+    pbsgen = self._generate_pb_lists(indexed_entities_by_entity_group,
+                                     base_req.ByteSize(), max_count,
+                                     max_egs_per_rpc, config)
+
+    rpcs = []
     for pbs, indexes in pbsgen:
-      req = datastore_pb.PutRequest()
-      req.CopyFrom(base_req)
-      rpcs.append(make_put_call(req, pbs,
+      rpcs.append(make_put_call(base_req, pbs,
                                 self.__create_result_index_pairs(indexes)))
     return MultiRpc(rpcs, self.__sort_result_index_pairs(extra_hook))
 
   def __put_hook(self, rpc):
     """Internal method used as get_result_hook for Put operation."""
     self.check_rpc_success(rpc)
-    keys = [self.__adapter.pb_to_key(pb)
-            for pb in rpc.response.key_list()]
+    entities_from_request, extra_hook = rpc.user_data
+
+    if isinstance(rpc.response, datastore_v4_pb.CommitResponse):
+      keys = []
+      i = 0
+      for entity in entities_from_request:
+        if datastore_pbs.is_complete_v4_key(entity.key()):
+          keys.append(entity.key())
+        else:
+          keys.append(rpc.response.mutation_result().insert_auto_id_key(i))
+          i += 1
+      keys = [self.__adapter.pb_v4_to_key(key) for key in keys]
+    else:
+      keys = [self.__adapter.pb_to_key(key) for key in rpc.response.key_list()]
 
 
-    if rpc.user_data is not None:
-      keys = rpc.user_data(keys)
+
+    if extra_hook is not None:
+      keys = extra_hook(keys)
     return keys
 
   def delete(self, keys):
@@ -1705,43 +1861,57 @@
       A MultiRpc object.
     """
 
-    def make_delete_call(req, pbs, user_data=None):
-      req.key_list().extend(pbs)
-      self._set_request_transaction(req)
-      resp = datastore_pb.DeleteResponse()
-      return self.make_rpc_call(config, 'Delete', req, resp,
-                                self.__delete_hook, user_data)
+    def make_delete_call(base_req, pbs, user_data=None):
+      req = copy.deepcopy(base_req)
+      if self._api_version == _DATASTORE_V4:
+        req.mutable_mutation().delete_list().extend(pbs)
+        method = 'Commit'
+        resp = datastore_v4_pb.CommitResponse()
+      else:
+        req.key_list().extend(pbs)
+        method = 'Delete'
+        resp = datastore_pb.DeleteResponse()
+      return self._make_rpc_call(config, method, req, resp,
+                                 get_result_hook=self.__delete_hook,
+                                 user_data=user_data,
+                                 service_name=self._api_version)
 
 
-    base_req = datastore_pb.DeleteRequest()
+    if self._api_version == _DATASTORE_V4:
+      base_req = datastore_v4_pb.CommitRequest()
+      base_req.set_mode(datastore_v4_pb.CommitRequest.NON_TRANSACTIONAL)
+      key_to_pb = self.__adapter.key_to_pb_v4
+    else:
+      base_req = datastore_pb.DeleteRequest()
+      key_to_pb = self.__adapter.key_to_pb
+    self._set_request_transaction(base_req)
     if Configuration.force_writes(config, self.__config):
-      base_req.set_force(True)
+      self.__force(base_req)
 
 
     if isinstance(config, apiproxy_stub_map.UserRPC) or len(keys) <= 1:
-      pbs = [self.__adapter.key_to_pb(key) for key in keys]
+      pbs = [key_to_pb(key) for key in keys]
       return make_delete_call(base_req, pbs, extra_hook)
 
-    base_size = self._get_base_size(base_req)
     max_count = (Configuration.max_delete_keys(config, self.__config) or
                  self.MAX_DELETE_KEYS)
     if not base_req.has_transaction():
       max_egs_per_rpc = self.__get_max_entity_groups_per_rpc(config)
     else:
       max_egs_per_rpc = None
-    indexed_keys_by_entity_group = self.__group_indexed_pbs_by_entity_group(
-        keys, self.__adapter.key_to_pb)
+
+    indexed_keys_by_entity_group = self._map_and_group(
+        keys, key_to_pb, self._extract_entity_group)
 
 
 
-    pbsgen = self._generate_pb_lists(
-        indexed_keys_by_entity_group, base_size, max_count, max_egs_per_rpc,
-        config)
+    pbsgen = self._generate_pb_lists(indexed_keys_by_entity_group,
+                                     base_req.ByteSize(), max_count,
+                                     max_egs_per_rpc, config)
+
     rpcs = []
     for pbs, _ in pbsgen:
-      req = datastore_pb.DeleteRequest()
-      req.CopyFrom(base_req)
-      rpcs.append(make_delete_call(req, pbs))
+      rpcs.append(make_delete_call(base_req, pbs))
     return MultiRpc(rpcs, extra_hook)
 
   def __delete_hook(self, rpc):
@@ -1764,7 +1934,7 @@
       app: Application ID.
 
     Returns:
-      A datastore_pb.Transaction object.
+      An object representing a transaction or None.
     """
     return self.async_begin_transaction(None, app).get_result()
 
@@ -1783,19 +1953,30 @@
       raise datastore_errors.BadArgumentError(
         'begin_transaction requires an application id argument (%r)' %
         (app,))
-    req = datastore_pb.BeginTransactionRequest()
-    req.set_app(app)
-    if (TransactionOptions.xg(config, self.__config)):
-      req.set_allow_multiple_eg(True)
-    resp = datastore_pb.Transaction()
-    rpc = self.make_rpc_call(config, 'BeginTransaction', req, resp,
-                             self.__begin_transaction_hook)
-    return rpc
+
+    if self._api_version == _DATASTORE_V4:
+      req = datastore_v4_pb.BeginTransactionRequest()
+      if TransactionOptions.xg(config, self.config):
+        req.set_cross_group(True)
+      resp = datastore_v4_pb.BeginTransactionResponse()
+    else:
+      req = datastore_pb.BeginTransactionRequest()
+      req.set_app(app)
+      if (TransactionOptions.xg(config, self.__config)):
+        req.set_allow_multiple_eg(True)
+      resp = datastore_pb.Transaction()
+
+    return self._make_rpc_call(config, 'BeginTransaction', req, resp,
+                               get_result_hook=self.__begin_transaction_hook,
+                               service_name=self._api_version)
 
   def __begin_transaction_hook(self, rpc):
     """Internal method used as get_result_hook for BeginTransaction."""
     self.check_rpc_success(rpc)
-    return rpc.response
+    if isinstance(rpc.response, datastore_v4_pb.BeginTransactionResponse):
+      return rpc.response.transaction()
+    else:
+      return rpc.response
 
 
 class Connection(BaseConnection):
@@ -1806,7 +1987,7 @@
   """
 
   @_positional(1)
-  def __init__(self, adapter=None, config=None):
+  def __init__(self, adapter=None, config=None, _api_version=_DATASTORE_V3):
     """Constructor.
 
     All arguments should be specified as keyword arguments.
@@ -1816,7 +1997,8 @@
         default IdentityAdapter.
       config: Optional Configuration object.
     """
-    super(Connection, self).__init__(adapter=adapter, config=config)
+    super(Connection, self).__init__(adapter=adapter, config=config,
+                                     _api_version=_api_version)
     self.__adapter = self.adapter
     self.__config = self.config
 
@@ -1834,7 +2016,8 @@
         with this connection's config.
     """
     config = self.__config.merge(config)
-    return TransactionalConnection(adapter=self.__adapter, config=config)
+    return TransactionalConnection(adapter=self.__adapter, config=config,
+                                   _api_version=self._api_version)
 
 
 
@@ -1896,8 +2079,10 @@
     if max is not None:
       req.set_max(max)
     resp = datastore_pb.AllocateIdsResponse()
-    rpc = self.make_rpc_call(config, 'AllocateIds', req, resp,
-                             self.__allocate_ids_hook, extra_hook)
+    rpc = self._make_rpc_call(config, 'AllocateIds', req, resp,
+                              get_result_hook=self.__allocate_ids_hook,
+                              user_data=extra_hook,
+                              service_name=_DATASTORE_V3)
     return rpc
 
   def __allocate_ids_hook(self, rpc):
@@ -1939,8 +2124,7 @@
       if key.path().element_size() == 1:
         return 'root_idkey'
       else:
-        eg = self._extract_entity_group(key)
-        return (eg.type(), eg.id() or eg.name())
+        return self._extract_entity_group(key)
 
     keys_by_idkey = self._map_and_group(keys, self.__adapter.key_to_pb,
                                         to_id_key)
@@ -1955,9 +2139,10 @@
         datastore_pbs.get_entity_converter().v3_to_v4_key(key,
                                                           req.add_reserve())
       resp = datastore_v4_pb.AllocateIdsResponse()
-      rpcs.append(self.make_rpc_call(config, 'AllocateIds', req, resp,
-                                     self.__reserve_keys_hook, extra_hook,
-                                     'datastore_v4'))
+      rpcs.append(self._make_rpc_call(config, 'AllocateIds', req, resp,
+                                      get_result_hook=self.__reserve_keys_hook,
+                                      user_data=extra_hook,
+                                      service_name=_DATASTORE_V4))
     return MultiRpc(rpcs)
 
   def __reserve_keys_hook(self, rpc):
@@ -2056,7 +2241,8 @@
 
   @_positional(1)
   def __init__(self,
-               adapter=None, config=None, transaction=None, entity_group=None):
+               adapter=None, config=None, transaction=None, entity_group=None,
+               _api_version=_DATASTORE_V3):
     """Constructor.
 
     All arguments should be specified as keyword arguments.
@@ -2069,24 +2255,29 @@
       entity_group: Deprecated, do not use.
     """
     super(TransactionalConnection, self).__init__(adapter=adapter,
-                                                  config=config)
+                                                  config=config,
+                                                  _api_version=_api_version)
     self.__adapter = self.adapter
+    self.__config = self.config
     if transaction is None:
       app = TransactionOptions.app(self.config)
       app = datastore_types.ResolveAppId(TransactionOptions.app(self.config))
       self.__transaction_rpc = self.async_begin_transaction(None, app)
     else:
-      if not isinstance(transaction, datastore_pb.Transaction):
+      if self._api_version == _DATASTORE_V4:
+        txn_class = str
+      else:
+        txn_class = datastore_pb.Transaction
+      if not isinstance(transaction, txn_class):
         raise datastore_errors.BadArgumentError(
           'Invalid transaction (%r)' % (transaction,))
       self.__transaction = transaction
       self.__transaction_rpc = None
     self.__finished = False
 
-  def _get_base_size(self, base_req):
-    """Internal helper: return size in bytes plus room for transaction."""
-    return (super(TransactionalConnection, self)._get_base_size(base_req) +
-            self.transaction.lengthString(self.transaction.ByteSize()) + 1)
+
+    self.__pending_v4_upserts = {}
+    self.__pending_v4_deletes = {}
 
   @property
   def finished(self):
@@ -2110,13 +2301,18 @@
       request: A protobuf with a transaction field.
 
     Returns:
-      A datastore_pb.Transaction object or None.
+      An object representing a transaction or None.
     """
     if self.__finished:
       raise datastore_errors.BadRequestError(
           'Cannot start a new operation in a finished transaction.')
     transaction = self.transaction
-    request.mutable_transaction().CopyFrom(transaction)
+    if self._api_version == _DATASTORE_V4:
+      request.mutable_read_options().set_transaction(transaction)
+
+      request.mutable_read_options().clear_read_consistency()
+    else:
+      request.mutable_transaction().CopyFrom(transaction)
     return transaction
 
   def _end_transaction(self):
@@ -2127,7 +2323,7 @@
     can be started using this connection.
 
     Returns:
-      A datastore_pb.Transaction object or None.
+      An object representing a transaction or None.
 
     Raises:
       datastore_errors.BadRequestError if the transaction is already
@@ -2147,6 +2343,133 @@
 
 
 
+  def async_put(self, config, entities, extra_hook=None):
+    """Transactional asynchronous Put operation.
+
+    Args:
+      config: A Configuration object or None.  Defaults are taken from
+        the connection's default configuration.
+      entities: An iterable of user-level entity objects.
+      extra_hook: Optional function to be called on the result once the
+        RPC has completed.
+
+     Returns:
+      A MultiRpc object.
+
+    NOTE: If any of the entities has an incomplete key, this will
+    *not* patch up those entities with the complete key.
+    """
+    if self._api_version != _DATASTORE_V4:
+
+      return super(TransactionalConnection, self).async_put(
+          config, entities, extra_hook)
+
+    v4_entities = [self.adapter.entity_to_pb_v4(entity)
+                   for entity in entities]
+
+
+    v4_req = datastore_v4_pb.AllocateIdsRequest()
+    for v4_entity in v4_entities:
+      if not datastore_pbs.is_complete_v4_key(v4_entity.key()):
+        v4_req.allocate_list().append(v4_entity.key())
+
+    user_data = v4_entities, extra_hook
+
+    if not v4_req.allocate_list():
+
+      return _StubRpc(self.__v4_build_put_result([], user_data))
+
+    return self._make_rpc_call(config, 'AllocateIds', v4_req,
+                              datastore_v4_pb.AllocateIdsResponse(),
+                              get_result_hook=self.__v4_put_allocate_ids_hook,
+                              user_data=user_data,
+                              service_name=_DATASTORE_V4)
+
+  def __v4_put_allocate_ids_hook(self, rpc):
+    """Internal method used as get_result_hook for AllocateIds call."""
+    self.check_rpc_success(rpc)
+    v4_resp = rpc.response
+    return self.__v4_build_put_result(list(v4_resp.allocated_list()),
+                                      rpc.user_data)
+
+  def __v4_build_put_result(self, v4_allocated_keys, user_data):
+    """Internal method that builds the result of a put operation.
+
+    Converts the results from a v4 AllocateIds operation to a list of user-level
+    key objects.
+
+    Args:
+      v4_allocated_keys: a list of datastore_v4_pb.Keys that have been allocated
+      user_data: a tuple consisting of:
+        - a list of datastore_v4_pb.Entity objects
+        - an optional extra_hook
+    """
+    v4_entities, extra_hook = user_data
+    keys = []
+    idx = 0
+    for v4_entity in v4_entities:
+
+
+
+
+
+
+      v4_entity = copy.deepcopy(v4_entity)
+      if not datastore_pbs.is_complete_v4_key(v4_entity.key()):
+        v4_entity.key().CopyFrom(v4_allocated_keys[idx])
+        idx += 1
+      hashable_key = datastore_types.ReferenceToKeyValue(v4_entity.key())
+
+      self.__pending_v4_deletes.pop(hashable_key, None)
+
+
+      self.__pending_v4_upserts[hashable_key] = v4_entity
+      keys.append(self.adapter.pb_v4_to_key(copy.deepcopy(v4_entity.key())))
+
+
+    if extra_hook:
+      keys = extra_hook(keys)
+    return keys
+
+
+
+  def async_delete(self, config, keys, extra_hook=None):
+    """Transactional asynchronous Delete operation.
+
+    Args:
+      config: A Configuration object or None.  Defaults are taken from
+        the connection's default configuration.
+      keys: An iterable of user-level key objects.
+      extra_hook: Optional function to be called once the RPC has completed.
+
+    Returns:
+      A MultiRpc object.
+    """
+    if self._api_version != _DATASTORE_V4:
+
+      return super(TransactionalConnection, self).async_delete(config,
+                                                               keys,
+                                                               extra_hook)
+
+    v4_keys = [self.__adapter.key_to_pb_v4(key) for key in keys]
+
+    for key in v4_keys:
+      hashable_key = datastore_types.ReferenceToKeyValue(key)
+
+      self.__pending_v4_upserts.pop(hashable_key, None)
+
+
+      self.__pending_v4_deletes[hashable_key] = key
+
+
+    return _StubRpc(self.__v4_delete_hook(extra_hook))
+
+  def __v4_delete_hook(self, extra_hook):
+    if extra_hook:
+      extra_hook(None)
+
+
+
   def commit(self):
     """Synchronous Commit operation.
 
@@ -2156,7 +2479,7 @@
     """
 
 
-    rpc = self.create_rpc()
+    rpc = self._create_rpc(service_name=self._api_version)
     rpc = self.async_commit(rpc)
     if rpc is None:
       return True
@@ -2175,10 +2498,30 @@
     transaction = self._end_transaction()
     if transaction is None:
       return None
-    resp = datastore_pb.CommitResponse()
-    rpc = self.make_rpc_call(config, 'Commit', transaction, resp,
-                             self.__commit_hook)
-    return rpc
+
+    if self._api_version == _DATASTORE_V4:
+      req = datastore_v4_pb.CommitRequest()
+      req.set_transaction(transaction)
+      if Configuration.force_writes(config, self.__config):
+        self.__force(req)
+
+
+      mutation = req.mutable_mutation()
+      mutation.upsert_list().extend(self.__pending_v4_upserts.itervalues())
+      mutation.delete_list().extend(self.__pending_v4_deletes.itervalues())
+
+
+      self.__pending_v4_upserts.clear()
+      self.__pending_v4_deletes.clear()
+
+      resp = datastore_v4_pb.CommitResponse()
+    else:
+      req = transaction
+      resp = datastore_pb.CommitResponse()
+
+    return self._make_rpc_call(config, 'Commit', req, resp,
+                               get_result_hook=self.__commit_hook,
+                               service_name=self._api_version)
 
   def __commit_hook(self, rpc):
     """Internal method used as get_result_hook for Commit."""
@@ -2214,10 +2557,18 @@
     transaction = self._end_transaction()
     if transaction is None:
       return None
-    resp = api_base_pb.VoidProto()
-    rpc = self.make_rpc_call(config, 'Rollback', transaction, resp,
-                             self.__rollback_hook)
-    return rpc
+
+    if self._api_version == _DATASTORE_V4:
+      req = datastore_v4_pb.RollbackRequest()
+      req.set_transaction(transaction)
+      resp = datastore_v4_pb.RollbackResponse()
+    else:
+      req = transaction
+      resp = api_base_pb.VoidProto()
+
+    return self._make_rpc_call(config, 'Rollback', req, resp,
+                               get_result_hook=self.__rollback_hook,
+                               service_name=self._api_version)
 
   def __rollback_hook(self, rpc):
     """Internal method used as get_result_hook for Rollback."""
diff --git a/google/appengine/datastore/datastore_sqlite_stub.py b/google/appengine/datastore/datastore_sqlite_stub.py
index d61a0d4..db8ab95 100644
--- a/google/appengine/datastore/datastore_sqlite_stub.py
+++ b/google/appengine/datastore/datastore_sqlite_stub.py
@@ -1336,8 +1336,9 @@
         else:
           db_cursor = _DedupingEntityGenerator(conn.execute(sql_stmt, params))
         dsquery = datastore_stub_util._MakeQuery(query, filters, orders)
-        cursor = datastore_stub_util.IteratorCursor(
-            query, dsquery, orders, index_list, db_cursor)
+        cursor = datastore_stub_util.ListCursor(
+            query, dsquery, orders, index_list,
+            [r for r in db_cursor])
       finally:
         self._ReleaseConnection(conn)
     return cursor
diff --git a/google/appengine/datastore/datastore_stub_util.py b/google/appengine/datastore/datastore_stub_util.py
index b0f439f..3a76744 100644
--- a/google/appengine/datastore/datastore_stub_util.py
+++ b/google/appengine/datastore/datastore_stub_util.py
@@ -37,16 +37,19 @@
   import md5
   _MD5_FUNC = md5.new
 
+import atexit
 import collections
 import itertools
 import logging
 import os
 import random
 import struct
-import time
 import threading
+import time
 import weakref
-import atexit
+
+from google.net.proto import ProtocolBuffer
+from google.appengine.datastore import entity_pb
 
 from google.appengine.api import api_base_pb
 from google.appengine.api import apiproxy_stub_map
@@ -55,13 +58,12 @@
 from google.appengine.api import datastore_types
 from google.appengine.api.taskqueue import taskqueue_service_pb
 from google.appengine.datastore import datastore_index
-from google.appengine.datastore import datastore_stub_index
 from google.appengine.datastore import datastore_pb
 from google.appengine.datastore import datastore_pbs
 from google.appengine.datastore import datastore_query
+from google.appengine.datastore import datastore_stub_index
 from google.appengine.datastore import datastore_v4_pb
 from google.appengine.runtime import apiproxy_errors
-from google.appengine.datastore import entity_pb
 
 
 
@@ -1122,139 +1124,6 @@
       position.set_start_inclusive(False)
 
 
-class IteratorCursor(BaseCursor):
-  """A query cursor over an entity iterator."""
-
-  def __init__(self, query, dsquery, orders, index_list, results):
-    """Constructor.
-
-    Args:
-      query: the query request proto
-      dsquery: a datastore_query.Query over query.
-      orders: the orders of query as returned by _GuessOrders.
-      index_list: A list of indexes used by the query.
-      results: iterator over entity_pb.EntityProto
-    """
-    super(IteratorCursor, self).__init__(query, dsquery, orders, index_list)
-
-    self.__last_result = None
-    self.__next_result = None
-    self.__results = results
-    self.__distincts = set()
-    self.__done = False
-
-
-    if query.has_end_compiled_cursor():
-      if query.end_compiled_cursor().position_list():
-        self.__end_cursor = self._DecodeCompiledCursor(
-            query.end_compiled_cursor())
-      else:
-        self.__done = True
-    else:
-      self.__end_cursor = None
-
-    if query.has_compiled_cursor() and query.compiled_cursor().position_list():
-      start_cursor = self._DecodeCompiledCursor(query.compiled_cursor())
-      self.__last_result = start_cursor[0]
-      try:
-        self._Advance()
-        while self._IsBeforeCursor(self.__next_result, start_cursor):
-          self._Advance()
-      except StopIteration:
-        pass
-
-
-    self.__offset = 0
-    self.__limit = None
-    if query.has_limit():
-      limit = query.limit()
-      if query.offset():
-        limit += query.offset()
-      if limit >= 0:
-        self.__limit = limit
-
-  def _Done(self):
-    self.__done = True
-    self.__next_result = None
-    raise StopIteration
-
-  def _Advance(self):
-    """Advance to next result (handles end cursor, ignores limit)."""
-    if self.__done:
-      raise StopIteration
-    try:
-      while True:
-        self.__next_result = self.__results.next()
-        if not self.group_by:
-          break
-        next_group = _GetGroupByKey(self.__next_result, self.group_by)
-        if next_group not in self.__distincts:
-          self.__distincts.add(next_group)
-          break
-    except StopIteration:
-      self._Done()
-    if (self.__end_cursor and
-        not self._IsBeforeCursor(self.__next_result, self.__end_cursor)):
-      self._Done()
-
-  def _GetNext(self):
-    """Ensures next result is fetched."""
-    if self.__limit is not None and self.__offset >= self.__limit:
-      self._Done()
-    if self.__next_result is None:
-      self._Advance()
-
-  def _Next(self):
-    """Returns and consumes next result."""
-    self._GetNext()
-    self.__last_result = self.__next_result
-    self.__next_result = None
-    self.__offset += 1
-    return self.__last_result
-
-  def PopulateQueryResult(self, result, count, offset,
-                          compile=False, first_result=False):
-    """Populates a QueryResult with this cursor and the given number of results.
-
-    Args:
-      result: datastore_pb.QueryResult
-      count: integer of how many results to return
-      offset: integer of how many results to skip
-      compile: boolean, whether we are compiling this query
-      first_result: whether the query result is the first for this query
-    """
-    Check(offset >= 0, 'Offset must be >= 0')
-    skipped = 0
-    try:
-      limited_offset = min(offset, _MAX_QUERY_OFFSET)
-      while skipped < limited_offset:
-        self._Next()
-        skipped += 1
-
-
-
-
-
-
-
-      if skipped == offset:
-        if count > _MAXIMUM_RESULTS:
-          count = _MAXIMUM_RESULTS
-        while count > 0:
-          result.result_list().append(LoadEntity(self._Next(), self.keys_only,
-                                                 self.property_names))
-          count -= 1
-
-      self._GetNext()
-    except StopIteration:
-      pass
-
-    result.set_more_results(not self.__done)
-    result.set_skipped_results(skipped)
-    self._PopulateResultMetadata(result, compile,
-                                 first_result, self.__last_result)
-
-
 class ListCursor(BaseCursor):
   """A query cursor over a list of entities.
 
@@ -3287,7 +3156,10 @@
       v3_compiled_cursor: a datastore_pb.CompiledCursor to populate
     """
     v3_compiled_cursor.Clear()
-    v3_compiled_cursor.ParseFromString(v4_cursor)
+    try:
+      v3_compiled_cursor.ParseFromString(v4_cursor)
+    except ProtocolBuffer.ProtocolBufferDecodeError:
+      raise datastore_pbs.InvalidConversionError('Invalid query cursor.')
 
   def v3_to_v4_compiled_cursor(self, v3_compiled_cursor):
     """Converts a v3 CompiledCursor to a v4 cursor string.
@@ -3552,7 +3424,10 @@
       v4_query_handle: a string representing a v4 query handle
       v3_cursor: a datastore_pb.Cursor to populate
     """
-    v3_cursor.ParseFromString(v4_query_handle)
+    try:
+      v3_cursor.ParseFromString(v4_query_handle)
+    except ProtocolBuffer.ProtocolBufferDecodeError:
+      raise datastore_pbs.InvalidConversionError('Invalid query handle.')
     return v3_cursor
 
   def _v3_to_v4_query_handle(self, v3_cursor):
@@ -3573,7 +3448,10 @@
       v4_txn: a string representing a v4 transaction
       v3_txn: a datastore_pb.Transaction to populate
     """
-    v3_txn.ParseFromString(v4_txn)
+    try:
+      v3_txn.ParseFromString(v4_txn)
+    except ProtocolBuffer.ProtocolBufferDecodeError:
+      raise datastore_pbs.InvalidConversionError('Invalid transaction.')
     return v3_txn
 
   def _v3_to_v4_txn(self, v3_txn):
@@ -3605,35 +3483,6 @@
     v3_req.set_allow_multiple_eg(v4_req.cross_group())
     return v3_req
 
-  def v3_to_v4_begin_transaction_req(self, v3_req):
-    """Converts a v3 BeginTransactionRequest to a v4 BeginTransactionRequest.
-
-    Args:
-      v3_req: a datastore_pb.BeginTransactionRequest
-
-    Returns:
-      a datastore_v4_pb.BeginTransactionRequest
-    """
-    v4_req = datastore_v4_pb.BeginTransactionRequest()
-
-    if v3_req.has_allow_multiple_eg():
-      v4_req.set_cross_group(v3_req.allow_multiple_eg())
-
-    return v4_req
-
-  def v4_begin_transaction_resp_to_v3_txn(self, v4_resp):
-    """Converts a v4 BeginTransactionResponse to a v3 Transaction.
-
-    Args:
-      v4_resp: datastore_v4_pb.BeginTransactionResponse
-
-    Returns:
-      a a datastore_pb.Transaction
-    """
-    v3_txn = datastore_pb.Transaction()
-    self.v4_to_v3_txn(v4_resp.transaction(), v3_txn)
-    return v3_txn
-
   def v3_to_v4_begin_transaction_resp(self, v3_resp):
     """Converts a v3 Transaction to a v4 BeginTransactionResponse.
 
@@ -3663,19 +3512,6 @@
     self.v4_to_v3_txn(v4_req.transaction(), v3_txn)
     return v3_txn
 
-  def v3_to_v4_rollback_req(self, v3_req):
-    """Converts a v3 Transaction to a v4 RollbackRequest.
-
-    Args:
-      v3_req: datastore_pb.Transaction
-
-    Returns:
-      a a datastore_v4_pb.RollbackRequest
-    """
-    v4_req = datastore_v4_pb.RollbackRequest()
-    v4_req.set_transaction(self._v3_to_v4_txn(v3_req))
-    return v4_req
-
 
 
 
@@ -3755,6 +3591,10 @@
     if v3_req.has_count():
       v4_req.set_suggested_batch_size(v3_req.count())
 
+    datastore_pbs.check_conversion(
+        not (v3_req.has_transaction() and v3_req.has_failover_ms()),
+        'Cannot set failover and transaction handle.')
+
 
     if v3_req.has_transaction():
       v4_req.mutable_read_options().set_transaction(
@@ -3869,58 +3709,6 @@
 
     return v3_req
 
-  def v3_to_v4_lookup_req(self, v3_req):
-    """Converts a v3 GetRequest to a v4 LookupRequest.
-
-    Args:
-      v3_req: a datastore_pb.GetRequest
-
-    Returns:
-      a datastore_v4_pb.LookupRequest
-    """
-    v4_req = datastore_v4_pb.LookupRequest()
-    datastore_pbs.check_conversion(v3_req.allow_deferred(),
-                                   'allow_deferred must be true')
-
-
-    if v3_req.has_transaction():
-      v4_req.mutable_read_options().set_transaction(
-          self._v3_to_v4_txn(v3_req.transaction()))
-    elif v3_req.strong():
-      v4_req.mutable_read_options().set_read_consistency(
-          datastore_v4_pb.ReadOptions.STRONG)
-    elif v3_req.has_failover_ms():
-      v4_req.mutable_read_options().set_read_consistency(
-          datastore_v4_pb.ReadOptions.EVENTUAL)
-
-    for v3_ref in v3_req.key_list():
-      self._entity_converter.v3_to_v4_key(v3_ref, v4_req.add_key())
-
-    return v4_req
-
-  def v4_to_v3_get_resp(self, v4_resp):
-    """Converts a v4 LookupResponse to a v3 GetResponse.
-
-    Args:
-      v4_resp: a datastore_v4_pb.LookupResponse
-
-    Returns:
-      a datastore_pb.GetResponse
-    """
-    v3_resp = datastore_pb.GetResponse()
-
-    for v4_key in v4_resp.deferred_list():
-      self._entity_converter.v4_to_v3_reference(v4_key, v3_resp.add_deferred())
-    for v4_found in v4_resp.found_list():
-      self._entity_converter.v4_to_v3_entity(
-          v4_found.entity(), v3_resp.add_entity().mutable_entity())
-    for v4_missing in v4_resp.missing_list():
-      self._entity_converter.v4_to_v3_reference(
-          v4_missing.entity().key(),
-          v3_resp.add_entity().mutable_key())
-
-    return v3_resp
-
   def v3_to_v4_lookup_resp(self, v3_resp):
     """Converts a v3 GetResponse to a v4 LookupResponse.
 
@@ -4465,3 +4253,4 @@
   prop_copy.MergeFrom(prop)
   prop_copy.set_multiple(False)
   return prop_copy
+
diff --git a/google/appengine/datastore/datastore_v4_pb.py b/google/appengine/datastore/datastore_v4_pb.py
index bee6c3f..cd70a37 100644
--- a/google/appengine/datastore/datastore_v4_pb.py
+++ b/google/appengine/datastore/datastore_v4_pb.py
@@ -177,801 +177,7 @@
   _STYLE_CONTENT_TYPE = """"""
   _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.Error'
   _SERIALIZED_DESCRIPTOR = array.array('B')
-  _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KHWFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVycm9yc3oJRXJyb3JDb2RliwGSAQtCQURfUkVRVUVTVJgBAYwBiwGSARZDT05DVVJSRU5UX1RSQU5TQUNUSU9OmAECjAGLAZIBDklOVEVSTkFMX0VSUk9SmAEDjAGLAZIBCk5FRURfSU5ERViYAQSMAYsBkgEHVElNRU9VVJgBBYwBiwGSARFQRVJNSVNTSU9OX0RFTklFRJgBBowBiwGSAQ5CSUdUQUJMRV9FUlJPUpgBB4wBiwGSARxDT01NSVRURURfQlVUX1NUSUxMX0FQUExZSU5HmAEIjAGLAZIBE0NBUEFCSUxJVFlfRElTQUJMRUSYAQmMAYsBkgEVVFJZX0FMVEVSTkFURV9CQUNLRU5EmAEKjAGLAZIBEVNBRkVfVElNRV9UT09fT0xEmAELjAF0ugH4LQonYXBwaG9zdGluZy9kYXRhc3RvcmUvZGF0YXN0b3JlX3Y0LnByb3RvEhdhcHBob3N0aW5nLmRhdGFzdG9yZS52NBokYXBwaG9zdGluZy9kYXRhc3RvcmUvZW50aXR5X3Y0LnByb3RvIosCCgVFcnJvciKBAgoJRXJyb3JDb2RlEg8KC0JBRF9SRVFVRVNUEAESGgoWQ09OQ1VSUkVOVF9UUkFOU0FDVElPThACEhIKDklOVEVSTkFMX0VSUk9SEAMSDgoKTkVFRF9JTkRFWBAEEgsKB1RJTUVPVVQQBRIVChFQRVJNSVNTSU9OX0RFTklFRBAGEhIKDkJJR1RBQkxFX0VSUk9SEAcSIAocQ09NTUlUVEVEX0JVVF9TVElMTF9BUFBMWUlORxAIEhcKE0NBUEFCSUxJVFlfRElTQUJMRUQQCRIZChVUUllfQUxURVJOQVRFX0JBQ0tFTkQQChIVChFTQUZFX1RJTUVfVE9PX09MRBALIpMCCghNdXRhdGlvbhIvCgZ1cHNlcnQYASADKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHkSLwoGdXBkYXRlGAIgAygLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5Ei8KBmluc2VydBgDIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eRI3Cg5pbnNlcnRfYXV0b19pZBgEIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eRIsCgZkZWxldGUYBSADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkSDQoFZm9yY2UYBiABKAgi4QEKDk11dGF0aW9uUmVzdWx0EhUKDWluZGV4X3VwZGF0ZXMYASACKAUSOAoSaW5zZXJ0X2F1dG9faWRfa2V5GAIgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5EhYKDnVwc2VydF92ZXJzaW9uGAMgAygDEhYKDnVwZGF0ZV92ZXJzaW9uGAQgAygDEhYKDmluc2VydF92ZXJzaW9uGAUgAygDEh4KFmluc2VydF9hdXRvX2lkX3ZlcnNpb24YBiADKAMSFgoOZGVsZXRlX3ZlcnNpb24YByADKAMihgEKDEVudGl0eVJlc3VsdBIvCgZlbnRpdHkYASACKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHkSDwoHdmVyc2lvbhgCIAEoAyI0CgpSZXN1bHRUeXBlEggKBEZVTEwQARIOCgpQUk9KRUNUSU9OEAISDAoIS0VZX09OTFkQAyLxAgoFUXVlcnkSPwoKcHJvamVjdGlvbhgCIAMoCzIrLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5RXhwcmVzc2lvbhI1CgRraW5kGAMgAygLMicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2luZEV4cHJlc3Npb24SLwoGZmlsdGVyGAQgASgLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRmlsdGVyEjUKBW9yZGVyGAUgAygLMiYuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlPcmRlchI8Cghncm91cF9ieRgGIAMoCzIqLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5UmVmZXJlbmNlEhQKDHN0YXJ0X2N1cnNvchgHIAEoDBISCgplbmRfY3Vyc29yGAggASgMEhEKBm9mZnNldBgKIAEoBToBMBINCgVsaW1pdBgLIAEoBSIeCg5LaW5kRXhwcmVzc2lvbhIMCgRuYW1lGAEgAigJIiEKEVByb3BlcnR5UmVmZXJlbmNlEgwKBG5hbWUYAiACKAki0wEKElByb3BlcnR5RXhwcmVzc2lvbhI8Cghwcm9wZXJ0eRgBIAIoCzIqLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5UmVmZXJlbmNlEl0KFGFnZ3JlZ2F0aW9uX2Z1bmN0aW9uGAIgASgOMj8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlFeHByZXNzaW9uLkFnZ3JlZ2F0aW9uRnVuY3Rpb24iIAoTQWdncmVnYXRpb25GdW5jdGlvbhIJCgVGSVJTVBABIskBCg1Qcm9wZXJ0eU9yZGVyEjwKCHByb3BlcnR5GAEgAigLMiouYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlSZWZlcmVuY2USTgoJZGlyZWN0aW9uGAIgASgOMjAuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlPcmRlci5EaXJlY3Rpb246CUFTQ0VORElORyIqCglEaXJlY3Rpb24SDQoJQVNDRU5ESU5HEAESDgoKREVTQ0VORElORxACIo4BCgZGaWx0ZXISQgoQY29tcG9zaXRlX2ZpbHRlchgBIAEoCzIoLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbXBvc2l0ZUZpbHRlchJACg9wcm9wZXJ0eV9maWx0ZXIYAiABKAsyJy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUZpbHRlciKcAQoPQ29tcG9zaXRlRmlsdGVyEkMKCG9wZXJhdG9yGAEgAigOMjEuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tcG9zaXRlRmlsdGVyLk9wZXJhdG9yEi8KBmZpbHRlchgCIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkZpbHRlciITCghPcGVyYXRvchIHCgNBTkQQASK+AgoOUHJvcGVydHlGaWx0ZXISPAoIcHJvcGVydHkYASACKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRJCCghvcGVyYXRvchgCIAIoDjIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5RmlsdGVyLk9wZXJhdG9yEi0KBXZhbHVlGAMgAigLMh4uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuVmFsdWUiewoIT3BlcmF0b3ISDQoJTEVTU19USEFOEAESFgoSTEVTU19USEFOX09SX0VRVUFMEAISEAoMR1JFQVRFUl9USEFOEAMSGQoVR1JFQVRFUl9USEFOX09SX0VRVUFMEAQSCQoFRVFVQUwQBRIQCgxIQVNfQU5DRVNUT1IQCyKwAQoIR3FsUXVlcnkSFAoMcXVlcnlfc3RyaW5nGAEgAigJEhwKDWFsbG93X2xpdGVyYWwYAiABKAg6BWZhbHNlEjYKCG5hbWVfYXJnGAMgAygLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuR3FsUXVlcnlBcmcSOAoKbnVtYmVyX2FyZxgEIAMoCzIkLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkdxbFF1ZXJ5QXJnIloKC0dxbFF1ZXJ5QXJnEgwKBG5hbWUYASABKAkSLQoFdmFsdWUYAiABKAsyHi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5WYWx1ZRIOCgZjdXJzb3IYAyABKAwi9wIKEFF1ZXJ5UmVzdWx0QmF0Y2gSTAoSZW50aXR5X3Jlc3VsdF90eXBlGAEgAigOMjAuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5UmVzdWx0LlJlc3VsdFR5cGUSPAoNZW50aXR5X3Jlc3VsdBgCIAMoCzIlLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eVJlc3VsdBISCgplbmRfY3Vyc29yGAQgASgMEk8KDG1vcmVfcmVzdWx0cxgFIAIoDjI5LmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlF1ZXJ5UmVzdWx0QmF0Y2guTW9yZVJlc3VsdHNUeXBlEhoKD3NraXBwZWRfcmVzdWx0cxgGIAEoBToBMCJWCg9Nb3JlUmVzdWx0c1R5cGUSEAoMTk9UX0ZJTklTSEVEEAESHAoYTU9SRV9SRVNVTFRTX0FGVEVSX0xJTUlUEAISEwoPTk9fTU9SRV9SRVNVTFRTEAMitQEKC1JlYWRPcHRpb25zElcKEHJlYWRfY29uc2lzdGVuY3kYASABKA4yNC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5SZWFkT3B0aW9ucy5SZWFkQ29uc2lzdGVuY3k6B0RFRkFVTFQSEwoLdHJhbnNhY3Rpb24YAiABKAwiOAoPUmVhZENvbnNpc3RlbmN5EgsKB0RFRkFVTFQQABIKCgZTVFJPTkcQARIMCghFVkVOVFVBTBACInYKDUxvb2t1cFJlcXVlc3QSOgoMcmVhZF9vcHRpb25zGAEgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUmVhZE9wdGlvbnMSKQoDa2V5GAMgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5Iq4BCg5Mb29rdXBSZXNwb25zZRI0CgVmb3VuZBgBIAMoCzIlLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eVJlc3VsdBI2CgdtaXNzaW5nGAIgAygLMiUuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5UmVzdWx0Ei4KCGRlZmVycmVkGAMgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5IqsCCg9SdW5RdWVyeVJlcXVlc3QSOgoMcmVhZF9vcHRpb25zGAEgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUmVhZE9wdGlvbnMSOgoMcGFydGl0aW9uX2lkGAIgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUGFydGl0aW9uSWQSLQoFcXVlcnkYAyABKAsyHi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeRI0CglncWxfcXVlcnkYByABKAsyIS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HcWxRdWVyeRIdChVtaW5fc2FmZV90aW1lX3NlY29uZHMYBCABKAMSHAoUc3VnZ2VzdGVkX2JhdGNoX3NpemUYBSABKAUiYgoQUnVuUXVlcnlSZXNwb25zZRI4CgViYXRjaBgBIAIoCzIpLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlF1ZXJ5UmVzdWx0QmF0Y2gSFAoMcXVlcnlfaGFuZGxlGAIgASgMIiwKFENvbnRpbnVlUXVlcnlSZXF1ZXN0EhQKDHF1ZXJ5X2hhbmRsZRgBIAIoDCJRChVDb250aW51ZVF1ZXJ5UmVzcG9uc2USOAoFYmF0Y2gYASACKAsyKS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeVJlc3VsdEJhdGNoIlMKF0JlZ2luVHJhbnNhY3Rpb25SZXF1ZXN0EhoKC2Nyb3NzX2dyb3VwGAEgASgIOgVmYWxzZRIcCg1jcm9zc19yZXF1ZXN0GAIgASgIOgVmYWxzZSIvChhCZWdpblRyYW5zYWN0aW9uUmVzcG9uc2USEwoLdHJhbnNhY3Rpb24YASACKAwiJgoPUm9sbGJhY2tSZXF1ZXN0EhMKC3RyYW5zYWN0aW9uGAEgAigMIhIKEFJvbGxiYWNrUmVzcG9uc2Ui1QEKDUNvbW1pdFJlcXVlc3QSEwoLdHJhbnNhY3Rpb24YASABKAwSMwoIbXV0YXRpb24YAiABKAsyIS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5NdXRhdGlvbhJICgRtb2RlGAQgASgOMisuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tbWl0UmVxdWVzdC5Nb2RlOg1UUkFOU0FDVElPTkFMIjAKBE1vZGUSEQoNVFJBTlNBQ1RJT05BTBABEhUKEU5PTl9UUkFOU0FDVElPTkFMEAIiUgoOQ29tbWl0UmVzcG9uc2USQAoPbXV0YXRpb25fcmVzdWx0GAEgASgLMicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTXV0YXRpb25SZXN1bHQicwoSQWxsb2NhdGVJZHNSZXF1ZXN0Ei4KCGFsbG9jYXRlGAEgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5Ei0KB3Jlc2VydmUYAiADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkiRgoTQWxsb2NhdGVJZHNSZXNwb25zZRIvCglhbGxvY2F0ZWQYASADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkiQwoMV3JpdGVSZXF1ZXN0EjMKCG11dGF0aW9uGAEgAigLMiEuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTXV0YXRpb24yogcKEkRhdGFzdG9yZVY0U2VydmljZRJ5ChBCZWdpblRyYW5zYWN0aW9uEjAuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQmVnaW5UcmFuc2FjdGlvblJlcXVlc3QaMS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5CZWdpblRyYW5zYWN0aW9uUmVzcG9uc2UiABJhCghSb2xsYmFjaxIoLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJvbGxiYWNrUmVxdWVzdBopLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJvbGxiYWNrUmVzcG9uc2UiABJbCgZDb21taXQSJi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21taXRSZXF1ZXN0GicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tbWl0UmVzcG9uc2UiABJhCghSdW5RdWVyeRIoLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJ1blF1ZXJ5UmVxdWVzdBopLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJ1blF1ZXJ5UmVzcG9uc2UiABJwCg1Db250aW51ZVF1ZXJ5Ei0uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29udGludWVRdWVyeVJlcXVlc3QaLi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db250aW51ZVF1ZXJ5UmVzcG9uc2UiABJbCgZMb29rdXASJi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Mb29rdXBSZXF1ZXN0GicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTG9va3VwUmVzcG9uc2UiABJqCgtBbGxvY2F0ZUlkcxIrLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkFsbG9jYXRlSWRzUmVxdWVzdBosLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkFsbG9jYXRlSWRzUmVzcG9uc2UiABJYCgNHZXQSJi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Mb29rdXBSZXF1ZXN0GicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTG9va3VwUmVzcG9uc2UiABJZCgVXcml0ZRIlLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LldyaXRlUmVxdWVzdBonLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbW1pdFJlc3BvbnNlIgBCIQofY29tLmdvb2dsZS5hcHBob3N0aW5nLmRhdGFzdG9yZQ=="))
-  if _net_proto___parse__python is not None:
-    _net_proto___parse__python.RegisterType(
-        _SERIALIZED_DESCRIPTOR.tostring())
-
-class Mutation(ProtocolBuffer.ProtocolMessage):
-  has_force_ = 0
-  force_ = 0
-
-  def __init__(self, contents=None):
-    self.upsert_ = []
-    self.update_ = []
-    self.insert_ = []
-    self.insert_auto_id_ = []
-    self.delete_ = []
-    if contents is not None: self.MergeFromString(contents)
-
-  def upsert_size(self): return len(self.upsert_)
-  def upsert_list(self): return self.upsert_
-
-  def upsert(self, i):
-    return self.upsert_[i]
-
-  def mutable_upsert(self, i):
-    return self.upsert_[i]
-
-  def add_upsert(self):
-    x = google.appengine.datastore.entity_v4_pb.Entity()
-    self.upsert_.append(x)
-    return x
-
-  def clear_upsert(self):
-    self.upsert_ = []
-  def update_size(self): return len(self.update_)
-  def update_list(self): return self.update_
-
-  def update(self, i):
-    return self.update_[i]
-
-  def mutable_update(self, i):
-    return self.update_[i]
-
-  def add_update(self):
-    x = google.appengine.datastore.entity_v4_pb.Entity()
-    self.update_.append(x)
-    return x
-
-  def clear_update(self):
-    self.update_ = []
-  def insert_size(self): return len(self.insert_)
-  def insert_list(self): return self.insert_
-
-  def insert(self, i):
-    return self.insert_[i]
-
-  def mutable_insert(self, i):
-    return self.insert_[i]
-
-  def add_insert(self):
-    x = google.appengine.datastore.entity_v4_pb.Entity()
-    self.insert_.append(x)
-    return x
-
-  def clear_insert(self):
-    self.insert_ = []
-  def insert_auto_id_size(self): return len(self.insert_auto_id_)
-  def insert_auto_id_list(self): return self.insert_auto_id_
-
-  def insert_auto_id(self, i):
-    return self.insert_auto_id_[i]
-
-  def mutable_insert_auto_id(self, i):
-    return self.insert_auto_id_[i]
-
-  def add_insert_auto_id(self):
-    x = google.appengine.datastore.entity_v4_pb.Entity()
-    self.insert_auto_id_.append(x)
-    return x
-
-  def clear_insert_auto_id(self):
-    self.insert_auto_id_ = []
-  def delete_size(self): return len(self.delete_)
-  def delete_list(self): return self.delete_
-
-  def delete(self, i):
-    return self.delete_[i]
-
-  def mutable_delete(self, i):
-    return self.delete_[i]
-
-  def add_delete(self):
-    x = google.appengine.datastore.entity_v4_pb.Key()
-    self.delete_.append(x)
-    return x
-
-  def clear_delete(self):
-    self.delete_ = []
-  def force(self): return self.force_
-
-  def set_force(self, x):
-    self.has_force_ = 1
-    self.force_ = x
-
-  def clear_force(self):
-    if self.has_force_:
-      self.has_force_ = 0
-      self.force_ = 0
-
-  def has_force(self): return self.has_force_
-
-
-  def MergeFrom(self, x):
-    assert x is not self
-    for i in xrange(x.upsert_size()): self.add_upsert().CopyFrom(x.upsert(i))
-    for i in xrange(x.update_size()): self.add_update().CopyFrom(x.update(i))
-    for i in xrange(x.insert_size()): self.add_insert().CopyFrom(x.insert(i))
-    for i in xrange(x.insert_auto_id_size()): self.add_insert_auto_id().CopyFrom(x.insert_auto_id(i))
-    for i in xrange(x.delete_size()): self.add_delete().CopyFrom(x.delete(i))
-    if (x.has_force()): self.set_force(x.force())
-
-  if _net_proto___parse__python is not None:
-    def _CMergeFromString(self, s):
-      _net_proto___parse__python.MergeFromString(self, 'apphosting.datastore.v4.Mutation', s)
-
-  if _net_proto___parse__python is not None:
-    def _CEncode(self):
-      return _net_proto___parse__python.Encode(self, 'apphosting.datastore.v4.Mutation')
-
-  if _net_proto___parse__python is not None:
-    def _CEncodePartial(self):
-      return _net_proto___parse__python.EncodePartial(self, 'apphosting.datastore.v4.Mutation')
-
-  if _net_proto___parse__python is not None:
-    def _CToASCII(self, output_format):
-      return _net_proto___parse__python.ToASCII(self, 'apphosting.datastore.v4.Mutation', output_format)
-
-
-  if _net_proto___parse__python is not None:
-    def ParseASCII(self, s):
-      _net_proto___parse__python.ParseASCII(self, 'apphosting.datastore.v4.Mutation', s)
-
-
-  if _net_proto___parse__python is not None:
-    def ParseASCIIIgnoreUnknown(self, s):
-      _net_proto___parse__python.ParseASCIIIgnoreUnknown(self, 'apphosting.datastore.v4.Mutation', s)
-
-
-  def Equals(self, x):
-    if x is self: return 1
-    if len(self.upsert_) != len(x.upsert_): return 0
-    for e1, e2 in zip(self.upsert_, x.upsert_):
-      if e1 != e2: return 0
-    if len(self.update_) != len(x.update_): return 0
-    for e1, e2 in zip(self.update_, x.update_):
-      if e1 != e2: return 0
-    if len(self.insert_) != len(x.insert_): return 0
-    for e1, e2 in zip(self.insert_, x.insert_):
-      if e1 != e2: return 0
-    if len(self.insert_auto_id_) != len(x.insert_auto_id_): return 0
-    for e1, e2 in zip(self.insert_auto_id_, x.insert_auto_id_):
-      if e1 != e2: return 0
-    if len(self.delete_) != len(x.delete_): return 0
-    for e1, e2 in zip(self.delete_, x.delete_):
-      if e1 != e2: return 0
-    if self.has_force_ != x.has_force_: return 0
-    if self.has_force_ and self.force_ != x.force_: return 0
-    return 1
-
-  def IsInitialized(self, debug_strs=None):
-    initialized = 1
-    for p in self.upsert_:
-      if not p.IsInitialized(debug_strs): initialized=0
-    for p in self.update_:
-      if not p.IsInitialized(debug_strs): initialized=0
-    for p in self.insert_:
-      if not p.IsInitialized(debug_strs): initialized=0
-    for p in self.insert_auto_id_:
-      if not p.IsInitialized(debug_strs): initialized=0
-    for p in self.delete_:
-      if not p.IsInitialized(debug_strs): initialized=0
-    return initialized
-
-  def ByteSize(self):
-    n = 0
-    n += 1 * len(self.upsert_)
-    for i in xrange(len(self.upsert_)): n += self.lengthString(self.upsert_[i].ByteSize())
-    n += 1 * len(self.update_)
-    for i in xrange(len(self.update_)): n += self.lengthString(self.update_[i].ByteSize())
-    n += 1 * len(self.insert_)
-    for i in xrange(len(self.insert_)): n += self.lengthString(self.insert_[i].ByteSize())
-    n += 1 * len(self.insert_auto_id_)
-    for i in xrange(len(self.insert_auto_id_)): n += self.lengthString(self.insert_auto_id_[i].ByteSize())
-    n += 1 * len(self.delete_)
-    for i in xrange(len(self.delete_)): n += self.lengthString(self.delete_[i].ByteSize())
-    if (self.has_force_): n += 2
-    return n
-
-  def ByteSizePartial(self):
-    n = 0
-    n += 1 * len(self.upsert_)
-    for i in xrange(len(self.upsert_)): n += self.lengthString(self.upsert_[i].ByteSizePartial())
-    n += 1 * len(self.update_)
-    for i in xrange(len(self.update_)): n += self.lengthString(self.update_[i].ByteSizePartial())
-    n += 1 * len(self.insert_)
-    for i in xrange(len(self.insert_)): n += self.lengthString(self.insert_[i].ByteSizePartial())
-    n += 1 * len(self.insert_auto_id_)
-    for i in xrange(len(self.insert_auto_id_)): n += self.lengthString(self.insert_auto_id_[i].ByteSizePartial())
-    n += 1 * len(self.delete_)
-    for i in xrange(len(self.delete_)): n += self.lengthString(self.delete_[i].ByteSizePartial())
-    if (self.has_force_): n += 2
-    return n
-
-  def Clear(self):
-    self.clear_upsert()
-    self.clear_update()
-    self.clear_insert()
-    self.clear_insert_auto_id()
-    self.clear_delete()
-    self.clear_force()
-
-  def OutputUnchecked(self, out):
-    for i in xrange(len(self.upsert_)):
-      out.putVarInt32(10)
-      out.putVarInt32(self.upsert_[i].ByteSize())
-      self.upsert_[i].OutputUnchecked(out)
-    for i in xrange(len(self.update_)):
-      out.putVarInt32(18)
-      out.putVarInt32(self.update_[i].ByteSize())
-      self.update_[i].OutputUnchecked(out)
-    for i in xrange(len(self.insert_)):
-      out.putVarInt32(26)
-      out.putVarInt32(self.insert_[i].ByteSize())
-      self.insert_[i].OutputUnchecked(out)
-    for i in xrange(len(self.insert_auto_id_)):
-      out.putVarInt32(34)
-      out.putVarInt32(self.insert_auto_id_[i].ByteSize())
-      self.insert_auto_id_[i].OutputUnchecked(out)
-    for i in xrange(len(self.delete_)):
-      out.putVarInt32(42)
-      out.putVarInt32(self.delete_[i].ByteSize())
-      self.delete_[i].OutputUnchecked(out)
-    if (self.has_force_):
-      out.putVarInt32(48)
-      out.putBoolean(self.force_)
-
-  def OutputPartial(self, out):
-    for i in xrange(len(self.upsert_)):
-      out.putVarInt32(10)
-      out.putVarInt32(self.upsert_[i].ByteSizePartial())
-      self.upsert_[i].OutputPartial(out)
-    for i in xrange(len(self.update_)):
-      out.putVarInt32(18)
-      out.putVarInt32(self.update_[i].ByteSizePartial())
-      self.update_[i].OutputPartial(out)
-    for i in xrange(len(self.insert_)):
-      out.putVarInt32(26)
-      out.putVarInt32(self.insert_[i].ByteSizePartial())
-      self.insert_[i].OutputPartial(out)
-    for i in xrange(len(self.insert_auto_id_)):
-      out.putVarInt32(34)
-      out.putVarInt32(self.insert_auto_id_[i].ByteSizePartial())
-      self.insert_auto_id_[i].OutputPartial(out)
-    for i in xrange(len(self.delete_)):
-      out.putVarInt32(42)
-      out.putVarInt32(self.delete_[i].ByteSizePartial())
-      self.delete_[i].OutputPartial(out)
-    if (self.has_force_):
-      out.putVarInt32(48)
-      out.putBoolean(self.force_)
-
-  def TryMerge(self, d):
-    while d.avail() > 0:
-      tt = d.getVarInt32()
-      if tt == 10:
-        length = d.getVarInt32()
-        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
-        d.skip(length)
-        self.add_upsert().TryMerge(tmp)
-        continue
-      if tt == 18:
-        length = d.getVarInt32()
-        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
-        d.skip(length)
-        self.add_update().TryMerge(tmp)
-        continue
-      if tt == 26:
-        length = d.getVarInt32()
-        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
-        d.skip(length)
-        self.add_insert().TryMerge(tmp)
-        continue
-      if tt == 34:
-        length = d.getVarInt32()
-        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
-        d.skip(length)
-        self.add_insert_auto_id().TryMerge(tmp)
-        continue
-      if tt == 42:
-        length = d.getVarInt32()
-        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
-        d.skip(length)
-        self.add_delete().TryMerge(tmp)
-        continue
-      if tt == 48:
-        self.set_force(d.getBoolean())
-        continue
-
-
-      if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
-      d.skipData(tt)
-
-
-  def __str__(self, prefix="", printElemNumber=0):
-    res=""
-    cnt=0
-    for e in self.upsert_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("upsert%s <\n" % elm)
-      res+=e.__str__(prefix + "  ", printElemNumber)
-      res+=prefix+">\n"
-      cnt+=1
-    cnt=0
-    for e in self.update_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("update%s <\n" % elm)
-      res+=e.__str__(prefix + "  ", printElemNumber)
-      res+=prefix+">\n"
-      cnt+=1
-    cnt=0
-    for e in self.insert_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("insert%s <\n" % elm)
-      res+=e.__str__(prefix + "  ", printElemNumber)
-      res+=prefix+">\n"
-      cnt+=1
-    cnt=0
-    for e in self.insert_auto_id_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("insert_auto_id%s <\n" % elm)
-      res+=e.__str__(prefix + "  ", printElemNumber)
-      res+=prefix+">\n"
-      cnt+=1
-    cnt=0
-    for e in self.delete_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("delete%s <\n" % elm)
-      res+=e.__str__(prefix + "  ", printElemNumber)
-      res+=prefix+">\n"
-      cnt+=1
-    if self.has_force_: res+=prefix+("force: %s\n" % self.DebugFormatBool(self.force_))
-    return res
-
-
-  def _BuildTagLookupTable(sparse, maxtag, default=None):
-    return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
-
-  kupsert = 1
-  kupdate = 2
-  kinsert = 3
-  kinsert_auto_id = 4
-  kdelete = 5
-  kforce = 6
-
-  _TEXT = _BuildTagLookupTable({
-    0: "ErrorCode",
-    1: "upsert",
-    2: "update",
-    3: "insert",
-    4: "insert_auto_id",
-    5: "delete",
-    6: "force",
-  }, 6)
-
-  _TYPES = _BuildTagLookupTable({
-    0: ProtocolBuffer.Encoder.NUMERIC,
-    1: ProtocolBuffer.Encoder.STRING,
-    2: ProtocolBuffer.Encoder.STRING,
-    3: ProtocolBuffer.Encoder.STRING,
-    4: ProtocolBuffer.Encoder.STRING,
-    5: ProtocolBuffer.Encoder.STRING,
-    6: ProtocolBuffer.Encoder.NUMERIC,
-  }, 6, ProtocolBuffer.Encoder.MAX_TYPE)
-
-
-  _STYLE = """"""
-  _STYLE_CONTENT_TYPE = """"""
-  _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.Mutation'
-  _SERIALIZED_DESCRIPTOR = array.array('B')
-  _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KIGFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lk11dGF0aW9uExoGdXBzZXJ0IAEoAjALOANKHmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eaMBqgEFY3R5cGWyAQZwcm90bzKkARQTGgZ1cGRhdGUgAigCMAs4A0oeYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5owGqAQVjdHlwZbIBBnByb3RvMqQBFBMaBmluc2VydCADKAIwCzgDSh5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHmjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoOaW5zZXJ0X2F1dG9faWQgBCgCMAs4A0oeYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5owGqAQVjdHlwZbIBBnByb3RvMqQBFBMaBmRlbGV0ZSAFKAIwCzgDShthcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXmjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoFZm9yY2UgBigAMAg4ARTCAR1hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FcnJvcg=="))
-  if _net_proto___parse__python is not None:
-    _net_proto___parse__python.RegisterType(
-        _SERIALIZED_DESCRIPTOR.tostring())
-
-class MutationResult(ProtocolBuffer.ProtocolMessage):
-  has_index_updates_ = 0
-  index_updates_ = 0
-
-  def __init__(self, contents=None):
-    self.insert_auto_id_key_ = []
-    self.upsert_version_ = []
-    self.update_version_ = []
-    self.insert_version_ = []
-    self.insert_auto_id_version_ = []
-    self.delete_version_ = []
-    if contents is not None: self.MergeFromString(contents)
-
-  def index_updates(self): return self.index_updates_
-
-  def set_index_updates(self, x):
-    self.has_index_updates_ = 1
-    self.index_updates_ = x
-
-  def clear_index_updates(self):
-    if self.has_index_updates_:
-      self.has_index_updates_ = 0
-      self.index_updates_ = 0
-
-  def has_index_updates(self): return self.has_index_updates_
-
-  def insert_auto_id_key_size(self): return len(self.insert_auto_id_key_)
-  def insert_auto_id_key_list(self): return self.insert_auto_id_key_
-
-  def insert_auto_id_key(self, i):
-    return self.insert_auto_id_key_[i]
-
-  def mutable_insert_auto_id_key(self, i):
-    return self.insert_auto_id_key_[i]
-
-  def add_insert_auto_id_key(self):
-    x = google.appengine.datastore.entity_v4_pb.Key()
-    self.insert_auto_id_key_.append(x)
-    return x
-
-  def clear_insert_auto_id_key(self):
-    self.insert_auto_id_key_ = []
-  def upsert_version_size(self): return len(self.upsert_version_)
-  def upsert_version_list(self): return self.upsert_version_
-
-  def upsert_version(self, i):
-    return self.upsert_version_[i]
-
-  def set_upsert_version(self, i, x):
-    self.upsert_version_[i] = x
-
-  def add_upsert_version(self, x):
-    self.upsert_version_.append(x)
-
-  def clear_upsert_version(self):
-    self.upsert_version_ = []
-
-  def update_version_size(self): return len(self.update_version_)
-  def update_version_list(self): return self.update_version_
-
-  def update_version(self, i):
-    return self.update_version_[i]
-
-  def set_update_version(self, i, x):
-    self.update_version_[i] = x
-
-  def add_update_version(self, x):
-    self.update_version_.append(x)
-
-  def clear_update_version(self):
-    self.update_version_ = []
-
-  def insert_version_size(self): return len(self.insert_version_)
-  def insert_version_list(self): return self.insert_version_
-
-  def insert_version(self, i):
-    return self.insert_version_[i]
-
-  def set_insert_version(self, i, x):
-    self.insert_version_[i] = x
-
-  def add_insert_version(self, x):
-    self.insert_version_.append(x)
-
-  def clear_insert_version(self):
-    self.insert_version_ = []
-
-  def insert_auto_id_version_size(self): return len(self.insert_auto_id_version_)
-  def insert_auto_id_version_list(self): return self.insert_auto_id_version_
-
-  def insert_auto_id_version(self, i):
-    return self.insert_auto_id_version_[i]
-
-  def set_insert_auto_id_version(self, i, x):
-    self.insert_auto_id_version_[i] = x
-
-  def add_insert_auto_id_version(self, x):
-    self.insert_auto_id_version_.append(x)
-
-  def clear_insert_auto_id_version(self):
-    self.insert_auto_id_version_ = []
-
-  def delete_version_size(self): return len(self.delete_version_)
-  def delete_version_list(self): return self.delete_version_
-
-  def delete_version(self, i):
-    return self.delete_version_[i]
-
-  def set_delete_version(self, i, x):
-    self.delete_version_[i] = x
-
-  def add_delete_version(self, x):
-    self.delete_version_.append(x)
-
-  def clear_delete_version(self):
-    self.delete_version_ = []
-
-
-  def MergeFrom(self, x):
-    assert x is not self
-    if (x.has_index_updates()): self.set_index_updates(x.index_updates())
-    for i in xrange(x.insert_auto_id_key_size()): self.add_insert_auto_id_key().CopyFrom(x.insert_auto_id_key(i))
-    for i in xrange(x.upsert_version_size()): self.add_upsert_version(x.upsert_version(i))
-    for i in xrange(x.update_version_size()): self.add_update_version(x.update_version(i))
-    for i in xrange(x.insert_version_size()): self.add_insert_version(x.insert_version(i))
-    for i in xrange(x.insert_auto_id_version_size()): self.add_insert_auto_id_version(x.insert_auto_id_version(i))
-    for i in xrange(x.delete_version_size()): self.add_delete_version(x.delete_version(i))
-
-  if _net_proto___parse__python is not None:
-    def _CMergeFromString(self, s):
-      _net_proto___parse__python.MergeFromString(self, 'apphosting.datastore.v4.MutationResult', s)
-
-  if _net_proto___parse__python is not None:
-    def _CEncode(self):
-      return _net_proto___parse__python.Encode(self, 'apphosting.datastore.v4.MutationResult')
-
-  if _net_proto___parse__python is not None:
-    def _CEncodePartial(self):
-      return _net_proto___parse__python.EncodePartial(self, 'apphosting.datastore.v4.MutationResult')
-
-  if _net_proto___parse__python is not None:
-    def _CToASCII(self, output_format):
-      return _net_proto___parse__python.ToASCII(self, 'apphosting.datastore.v4.MutationResult', output_format)
-
-
-  if _net_proto___parse__python is not None:
-    def ParseASCII(self, s):
-      _net_proto___parse__python.ParseASCII(self, 'apphosting.datastore.v4.MutationResult', s)
-
-
-  if _net_proto___parse__python is not None:
-    def ParseASCIIIgnoreUnknown(self, s):
-      _net_proto___parse__python.ParseASCIIIgnoreUnknown(self, 'apphosting.datastore.v4.MutationResult', s)
-
-
-  def Equals(self, x):
-    if x is self: return 1
-    if self.has_index_updates_ != x.has_index_updates_: return 0
-    if self.has_index_updates_ and self.index_updates_ != x.index_updates_: return 0
-    if len(self.insert_auto_id_key_) != len(x.insert_auto_id_key_): return 0
-    for e1, e2 in zip(self.insert_auto_id_key_, x.insert_auto_id_key_):
-      if e1 != e2: return 0
-    if len(self.upsert_version_) != len(x.upsert_version_): return 0
-    for e1, e2 in zip(self.upsert_version_, x.upsert_version_):
-      if e1 != e2: return 0
-    if len(self.update_version_) != len(x.update_version_): return 0
-    for e1, e2 in zip(self.update_version_, x.update_version_):
-      if e1 != e2: return 0
-    if len(self.insert_version_) != len(x.insert_version_): return 0
-    for e1, e2 in zip(self.insert_version_, x.insert_version_):
-      if e1 != e2: return 0
-    if len(self.insert_auto_id_version_) != len(x.insert_auto_id_version_): return 0
-    for e1, e2 in zip(self.insert_auto_id_version_, x.insert_auto_id_version_):
-      if e1 != e2: return 0
-    if len(self.delete_version_) != len(x.delete_version_): return 0
-    for e1, e2 in zip(self.delete_version_, x.delete_version_):
-      if e1 != e2: return 0
-    return 1
-
-  def IsInitialized(self, debug_strs=None):
-    initialized = 1
-    if (not self.has_index_updates_):
-      initialized = 0
-      if debug_strs is not None:
-        debug_strs.append('Required field: index_updates not set.')
-    for p in self.insert_auto_id_key_:
-      if not p.IsInitialized(debug_strs): initialized=0
-    return initialized
-
-  def ByteSize(self):
-    n = 0
-    n += self.lengthVarInt64(self.index_updates_)
-    n += 1 * len(self.insert_auto_id_key_)
-    for i in xrange(len(self.insert_auto_id_key_)): n += self.lengthString(self.insert_auto_id_key_[i].ByteSize())
-    n += 1 * len(self.upsert_version_)
-    for i in xrange(len(self.upsert_version_)): n += self.lengthVarInt64(self.upsert_version_[i])
-    n += 1 * len(self.update_version_)
-    for i in xrange(len(self.update_version_)): n += self.lengthVarInt64(self.update_version_[i])
-    n += 1 * len(self.insert_version_)
-    for i in xrange(len(self.insert_version_)): n += self.lengthVarInt64(self.insert_version_[i])
-    n += 1 * len(self.insert_auto_id_version_)
-    for i in xrange(len(self.insert_auto_id_version_)): n += self.lengthVarInt64(self.insert_auto_id_version_[i])
-    n += 1 * len(self.delete_version_)
-    for i in xrange(len(self.delete_version_)): n += self.lengthVarInt64(self.delete_version_[i])
-    return n + 1
-
-  def ByteSizePartial(self):
-    n = 0
-    if (self.has_index_updates_):
-      n += 1
-      n += self.lengthVarInt64(self.index_updates_)
-    n += 1 * len(self.insert_auto_id_key_)
-    for i in xrange(len(self.insert_auto_id_key_)): n += self.lengthString(self.insert_auto_id_key_[i].ByteSizePartial())
-    n += 1 * len(self.upsert_version_)
-    for i in xrange(len(self.upsert_version_)): n += self.lengthVarInt64(self.upsert_version_[i])
-    n += 1 * len(self.update_version_)
-    for i in xrange(len(self.update_version_)): n += self.lengthVarInt64(self.update_version_[i])
-    n += 1 * len(self.insert_version_)
-    for i in xrange(len(self.insert_version_)): n += self.lengthVarInt64(self.insert_version_[i])
-    n += 1 * len(self.insert_auto_id_version_)
-    for i in xrange(len(self.insert_auto_id_version_)): n += self.lengthVarInt64(self.insert_auto_id_version_[i])
-    n += 1 * len(self.delete_version_)
-    for i in xrange(len(self.delete_version_)): n += self.lengthVarInt64(self.delete_version_[i])
-    return n
-
-  def Clear(self):
-    self.clear_index_updates()
-    self.clear_insert_auto_id_key()
-    self.clear_upsert_version()
-    self.clear_update_version()
-    self.clear_insert_version()
-    self.clear_insert_auto_id_version()
-    self.clear_delete_version()
-
-  def OutputUnchecked(self, out):
-    out.putVarInt32(8)
-    out.putVarInt32(self.index_updates_)
-    for i in xrange(len(self.insert_auto_id_key_)):
-      out.putVarInt32(18)
-      out.putVarInt32(self.insert_auto_id_key_[i].ByteSize())
-      self.insert_auto_id_key_[i].OutputUnchecked(out)
-    for i in xrange(len(self.upsert_version_)):
-      out.putVarInt32(24)
-      out.putVarInt64(self.upsert_version_[i])
-    for i in xrange(len(self.update_version_)):
-      out.putVarInt32(32)
-      out.putVarInt64(self.update_version_[i])
-    for i in xrange(len(self.insert_version_)):
-      out.putVarInt32(40)
-      out.putVarInt64(self.insert_version_[i])
-    for i in xrange(len(self.insert_auto_id_version_)):
-      out.putVarInt32(48)
-      out.putVarInt64(self.insert_auto_id_version_[i])
-    for i in xrange(len(self.delete_version_)):
-      out.putVarInt32(56)
-      out.putVarInt64(self.delete_version_[i])
-
-  def OutputPartial(self, out):
-    if (self.has_index_updates_):
-      out.putVarInt32(8)
-      out.putVarInt32(self.index_updates_)
-    for i in xrange(len(self.insert_auto_id_key_)):
-      out.putVarInt32(18)
-      out.putVarInt32(self.insert_auto_id_key_[i].ByteSizePartial())
-      self.insert_auto_id_key_[i].OutputPartial(out)
-    for i in xrange(len(self.upsert_version_)):
-      out.putVarInt32(24)
-      out.putVarInt64(self.upsert_version_[i])
-    for i in xrange(len(self.update_version_)):
-      out.putVarInt32(32)
-      out.putVarInt64(self.update_version_[i])
-    for i in xrange(len(self.insert_version_)):
-      out.putVarInt32(40)
-      out.putVarInt64(self.insert_version_[i])
-    for i in xrange(len(self.insert_auto_id_version_)):
-      out.putVarInt32(48)
-      out.putVarInt64(self.insert_auto_id_version_[i])
-    for i in xrange(len(self.delete_version_)):
-      out.putVarInt32(56)
-      out.putVarInt64(self.delete_version_[i])
-
-  def TryMerge(self, d):
-    while d.avail() > 0:
-      tt = d.getVarInt32()
-      if tt == 8:
-        self.set_index_updates(d.getVarInt32())
-        continue
-      if tt == 18:
-        length = d.getVarInt32()
-        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
-        d.skip(length)
-        self.add_insert_auto_id_key().TryMerge(tmp)
-        continue
-      if tt == 24:
-        self.add_upsert_version(d.getVarInt64())
-        continue
-      if tt == 32:
-        self.add_update_version(d.getVarInt64())
-        continue
-      if tt == 40:
-        self.add_insert_version(d.getVarInt64())
-        continue
-      if tt == 48:
-        self.add_insert_auto_id_version(d.getVarInt64())
-        continue
-      if tt == 56:
-        self.add_delete_version(d.getVarInt64())
-        continue
-
-
-      if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
-      d.skipData(tt)
-
-
-  def __str__(self, prefix="", printElemNumber=0):
-    res=""
-    if self.has_index_updates_: res+=prefix+("index_updates: %s\n" % self.DebugFormatInt32(self.index_updates_))
-    cnt=0
-    for e in self.insert_auto_id_key_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("insert_auto_id_key%s <\n" % elm)
-      res+=e.__str__(prefix + "  ", printElemNumber)
-      res+=prefix+">\n"
-      cnt+=1
-    cnt=0
-    for e in self.upsert_version_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("upsert_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
-      cnt+=1
-    cnt=0
-    for e in self.update_version_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("update_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
-      cnt+=1
-    cnt=0
-    for e in self.insert_version_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("insert_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
-      cnt+=1
-    cnt=0
-    for e in self.insert_auto_id_version_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("insert_auto_id_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
-      cnt+=1
-    cnt=0
-    for e in self.delete_version_:
-      elm=""
-      if printElemNumber: elm="(%d)" % cnt
-      res+=prefix+("delete_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
-      cnt+=1
-    return res
-
-
-  def _BuildTagLookupTable(sparse, maxtag, default=None):
-    return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
-
-  kindex_updates = 1
-  kinsert_auto_id_key = 2
-  kupsert_version = 3
-  kupdate_version = 4
-  kinsert_version = 5
-  kinsert_auto_id_version = 6
-  kdelete_version = 7
-
-  _TEXT = _BuildTagLookupTable({
-    0: "ErrorCode",
-    1: "index_updates",
-    2: "insert_auto_id_key",
-    3: "upsert_version",
-    4: "update_version",
-    5: "insert_version",
-    6: "insert_auto_id_version",
-    7: "delete_version",
-  }, 7)
-
-  _TYPES = _BuildTagLookupTable({
-    0: ProtocolBuffer.Encoder.NUMERIC,
-    1: ProtocolBuffer.Encoder.NUMERIC,
-    2: ProtocolBuffer.Encoder.STRING,
-    3: ProtocolBuffer.Encoder.NUMERIC,
-    4: ProtocolBuffer.Encoder.NUMERIC,
-    5: ProtocolBuffer.Encoder.NUMERIC,
-    6: ProtocolBuffer.Encoder.NUMERIC,
-    7: ProtocolBuffer.Encoder.NUMERIC,
-  }, 7, ProtocolBuffer.Encoder.MAX_TYPE)
-
-
-  _STYLE = """"""
-  _STYLE_CONTENT_TYPE = """"""
-  _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.MutationResult'
-  _SERIALIZED_DESCRIPTOR = array.array('B')
-  _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KJmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lk11dGF0aW9uUmVzdWx0ExoNaW5kZXhfdXBkYXRlcyABKAAwBTgCFBMaEmluc2VydF9hdXRvX2lkX2tleSACKAIwCzgDShthcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXmjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoOdXBzZXJ0X3ZlcnNpb24gAygAMAM4AxQTGg51cGRhdGVfdmVyc2lvbiAEKAAwAzgDFBMaDmluc2VydF92ZXJzaW9uIAUoADADOAMUExoWaW5zZXJ0X2F1dG9faWRfdmVyc2lvbiAGKAAwAzgDFBMaDmRlbGV0ZV92ZXJzaW9uIAcoADADOAMUwgEdYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRXJyb3I="))
+  _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KHWFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVycm9yc3oJRXJyb3JDb2RliwGSAQtCQURfUkVRVUVTVJgBAYwBiwGSARZDT05DVVJSRU5UX1RSQU5TQUNUSU9OmAECjAGLAZIBDklOVEVSTkFMX0VSUk9SmAEDjAGLAZIBCk5FRURfSU5ERViYAQSMAYsBkgEHVElNRU9VVJgBBYwBiwGSARFQRVJNSVNTSU9OX0RFTklFRJgBBowBiwGSAQ5CSUdUQUJMRV9FUlJPUpgBB4wBiwGSARxDT01NSVRURURfQlVUX1NUSUxMX0FQUExZSU5HmAEIjAGLAZIBE0NBUEFCSUxJVFlfRElTQUJMRUSYAQmMAYsBkgEVVFJZX0FMVEVSTkFURV9CQUNLRU5EmAEKjAGLAZIBEVNBRkVfVElNRV9UT09fT0xEmAELjAF0ugH4LQonYXBwaG9zdGluZy9kYXRhc3RvcmUvZGF0YXN0b3JlX3Y0LnByb3RvEhdhcHBob3N0aW5nLmRhdGFzdG9yZS52NBokYXBwaG9zdGluZy9kYXRhc3RvcmUvZW50aXR5X3Y0LnByb3RvIosCCgVFcnJvciKBAgoJRXJyb3JDb2RlEg8KC0JBRF9SRVFVRVNUEAESGgoWQ09OQ1VSUkVOVF9UUkFOU0FDVElPThACEhIKDklOVEVSTkFMX0VSUk9SEAMSDgoKTkVFRF9JTkRFWBAEEgsKB1RJTUVPVVQQBRIVChFQRVJNSVNTSU9OX0RFTklFRBAGEhIKDkJJR1RBQkxFX0VSUk9SEAcSIAocQ09NTUlUVEVEX0JVVF9TVElMTF9BUFBMWUlORxAIEhcKE0NBUEFCSUxJVFlfRElTQUJMRUQQCRIZChVUUllfQUxURVJOQVRFX0JBQ0tFTkQQChIVChFTQUZFX1RJTUVfVE9PX09MRBALIoYBCgxFbnRpdHlSZXN1bHQSLwoGZW50aXR5GAEgAigLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5Eg8KB3ZlcnNpb24YAiABKAMiNAoKUmVzdWx0VHlwZRIICgRGVUxMEAESDgoKUFJPSkVDVElPThACEgwKCEtFWV9PTkxZEAMi8QIKBVF1ZXJ5Ej8KCnByb2plY3Rpb24YAiADKAsyKy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUV4cHJlc3Npb24SNQoEa2luZBgDIAMoCzInLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LktpbmRFeHByZXNzaW9uEi8KBmZpbHRlchgEIAEoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkZpbHRlchI1CgVvcmRlchgFIAMoCzImLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5T3JkZXISPAoIZ3JvdXBfYnkYBiADKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRIUCgxzdGFydF9jdXJzb3IYByABKAwSEgoKZW5kX2N1cnNvchgIIAEoDBIRCgZvZmZzZXQYCiABKAU6ATASDQoFbGltaXQYCyABKAUiHgoOS2luZEV4cHJlc3Npb24SDAoEbmFtZRgBIAIoCSIhChFQcm9wZXJ0eVJlZmVyZW5jZRIMCgRuYW1lGAIgAigJItMBChJQcm9wZXJ0eUV4cHJlc3Npb24SPAoIcHJvcGVydHkYASACKAsyKi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eVJlZmVyZW5jZRJdChRhZ2dyZWdhdGlvbl9mdW5jdGlvbhgCIAEoDjI/LmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5RXhwcmVzc2lvbi5BZ2dyZWdhdGlvbkZ1bmN0aW9uIiAKE0FnZ3JlZ2F0aW9uRnVuY3Rpb24SCQoFRklSU1QQASLJAQoNUHJvcGVydHlPcmRlchI8Cghwcm9wZXJ0eRgBIAIoCzIqLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5UmVmZXJlbmNlEk4KCWRpcmVjdGlvbhgCIAEoDjIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlByb3BlcnR5T3JkZXIuRGlyZWN0aW9uOglBU0NFTkRJTkciKgoJRGlyZWN0aW9uEg0KCUFTQ0VORElORxABEg4KCkRFU0NFTkRJTkcQAiKOAQoGRmlsdGVyEkIKEGNvbXBvc2l0ZV9maWx0ZXIYASABKAsyKC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21wb3NpdGVGaWx0ZXISQAoPcHJvcGVydHlfZmlsdGVyGAIgASgLMicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlGaWx0ZXIinAEKD0NvbXBvc2l0ZUZpbHRlchJDCghvcGVyYXRvchgBIAIoDjIxLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbXBvc2l0ZUZpbHRlci5PcGVyYXRvchIvCgZmaWx0ZXIYAiADKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5GaWx0ZXIiEwoIT3BlcmF0b3ISBwoDQU5EEAEivgIKDlByb3BlcnR5RmlsdGVyEjwKCHByb3BlcnR5GAEgAigLMiouYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUHJvcGVydHlSZWZlcmVuY2USQgoIb3BlcmF0b3IYAiACKA4yMC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Qcm9wZXJ0eUZpbHRlci5PcGVyYXRvchItCgV2YWx1ZRgDIAIoCzIeLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlZhbHVlInsKCE9wZXJhdG9yEg0KCUxFU1NfVEhBThABEhYKEkxFU1NfVEhBTl9PUl9FUVVBTBACEhAKDEdSRUFURVJfVEhBThADEhkKFUdSRUFURVJfVEhBTl9PUl9FUVVBTBAEEgkKBUVRVUFMEAUSEAoMSEFTX0FOQ0VTVE9SEAsisAEKCEdxbFF1ZXJ5EhQKDHF1ZXJ5X3N0cmluZxgBIAIoCRIcCg1hbGxvd19saXRlcmFsGAIgASgIOgVmYWxzZRI2CghuYW1lX2FyZxgDIAMoCzIkLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkdxbFF1ZXJ5QXJnEjgKCm51bWJlcl9hcmcYBCADKAsyJC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HcWxRdWVyeUFyZyJaCgtHcWxRdWVyeUFyZxIMCgRuYW1lGAEgASgJEi0KBXZhbHVlGAIgASgLMh4uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuVmFsdWUSDgoGY3Vyc29yGAMgASgMIvcCChBRdWVyeVJlc3VsdEJhdGNoEkwKEmVudGl0eV9yZXN1bHRfdHlwZRgBIAIoDjIwLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eVJlc3VsdC5SZXN1bHRUeXBlEjwKDWVudGl0eV9yZXN1bHQYAiADKAsyJS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHlSZXN1bHQSEgoKZW5kX2N1cnNvchgEIAEoDBJPCgxtb3JlX3Jlc3VsdHMYBSACKA4yOS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeVJlc3VsdEJhdGNoLk1vcmVSZXN1bHRzVHlwZRIaCg9za2lwcGVkX3Jlc3VsdHMYBiABKAU6ATAiVgoPTW9yZVJlc3VsdHNUeXBlEhAKDE5PVF9GSU5JU0hFRBABEhwKGE1PUkVfUkVTVUxUU19BRlRFUl9MSU1JVBACEhMKD05PX01PUkVfUkVTVUxUUxADIpMCCghNdXRhdGlvbhIvCgZ1cHNlcnQYASADKAsyHy5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHkSLwoGdXBkYXRlGAIgAygLMh8uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5Ei8KBmluc2VydBgDIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eRI3Cg5pbnNlcnRfYXV0b19pZBgEIAMoCzIfLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eRIsCgZkZWxldGUYBSADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkSDQoFZm9yY2UYBiABKAgi4QEKDk11dGF0aW9uUmVzdWx0EhUKDWluZGV4X3VwZGF0ZXMYASACKAUSOAoSaW5zZXJ0X2F1dG9faWRfa2V5GAIgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5EhYKDnVwc2VydF92ZXJzaW9uGAMgAygDEhYKDnVwZGF0ZV92ZXJzaW9uGAQgAygDEhYKDmluc2VydF92ZXJzaW9uGAUgAygDEh4KFmluc2VydF9hdXRvX2lkX3ZlcnNpb24YBiADKAMSFgoOZGVsZXRlX3ZlcnNpb24YByADKAMitQEKC1JlYWRPcHRpb25zElcKEHJlYWRfY29uc2lzdGVuY3kYASABKA4yNC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5SZWFkT3B0aW9ucy5SZWFkQ29uc2lzdGVuY3k6B0RFRkFVTFQSEwoLdHJhbnNhY3Rpb24YAiABKAwiOAoPUmVhZENvbnNpc3RlbmN5EgsKB0RFRkFVTFQQABIKCgZTVFJPTkcQARIMCghFVkVOVFVBTBACInYKDUxvb2t1cFJlcXVlc3QSOgoMcmVhZF9vcHRpb25zGAEgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUmVhZE9wdGlvbnMSKQoDa2V5GAMgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5Iq4BCg5Mb29rdXBSZXNwb25zZRI0CgVmb3VuZBgBIAMoCzIlLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eVJlc3VsdBI2CgdtaXNzaW5nGAIgAygLMiUuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5UmVzdWx0Ei4KCGRlZmVycmVkGAMgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5IqsCCg9SdW5RdWVyeVJlcXVlc3QSOgoMcmVhZF9vcHRpb25zGAEgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUmVhZE9wdGlvbnMSOgoMcGFydGl0aW9uX2lkGAIgASgLMiQuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuUGFydGl0aW9uSWQSLQoFcXVlcnkYAyABKAsyHi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeRI0CglncWxfcXVlcnkYByABKAsyIS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5HcWxRdWVyeRIdChVtaW5fc2FmZV90aW1lX3NlY29uZHMYBCABKAMSHAoUc3VnZ2VzdGVkX2JhdGNoX3NpemUYBSABKAUiYgoQUnVuUXVlcnlSZXNwb25zZRI4CgViYXRjaBgBIAIoCzIpLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlF1ZXJ5UmVzdWx0QmF0Y2gSFAoMcXVlcnlfaGFuZGxlGAIgASgMIiwKFENvbnRpbnVlUXVlcnlSZXF1ZXN0EhQKDHF1ZXJ5X2hhbmRsZRgBIAIoDCJRChVDb250aW51ZVF1ZXJ5UmVzcG9uc2USOAoFYmF0Y2gYASACKAsyKS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5RdWVyeVJlc3VsdEJhdGNoIlMKF0JlZ2luVHJhbnNhY3Rpb25SZXF1ZXN0EhoKC2Nyb3NzX2dyb3VwGAEgASgIOgVmYWxzZRIcCg1jcm9zc19yZXF1ZXN0GAIgASgIOgVmYWxzZSIvChhCZWdpblRyYW5zYWN0aW9uUmVzcG9uc2USEwoLdHJhbnNhY3Rpb24YASACKAwiJgoPUm9sbGJhY2tSZXF1ZXN0EhMKC3RyYW5zYWN0aW9uGAEgAigMIhIKEFJvbGxiYWNrUmVzcG9uc2Ui1QEKDUNvbW1pdFJlcXVlc3QSEwoLdHJhbnNhY3Rpb24YASABKAwSMwoIbXV0YXRpb24YAiABKAsyIS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5NdXRhdGlvbhJICgRtb2RlGAQgASgOMisuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tbWl0UmVxdWVzdC5Nb2RlOg1UUkFOU0FDVElPTkFMIjAKBE1vZGUSEQoNVFJBTlNBQ1RJT05BTBABEhUKEU5PTl9UUkFOU0FDVElPTkFMEAIiUgoOQ29tbWl0UmVzcG9uc2USQAoPbXV0YXRpb25fcmVzdWx0GAEgASgLMicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTXV0YXRpb25SZXN1bHQicwoSQWxsb2NhdGVJZHNSZXF1ZXN0Ei4KCGFsbG9jYXRlGAEgAygLMhwuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuS2V5Ei0KB3Jlc2VydmUYAiADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkiRgoTQWxsb2NhdGVJZHNSZXNwb25zZRIvCglhbGxvY2F0ZWQYASADKAsyHC5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXkiQwoMV3JpdGVSZXF1ZXN0EjMKCG11dGF0aW9uGAEgAigLMiEuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTXV0YXRpb24yogcKEkRhdGFzdG9yZVY0U2VydmljZRJ5ChBCZWdpblRyYW5zYWN0aW9uEjAuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQmVnaW5UcmFuc2FjdGlvblJlcXVlc3QaMS5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5CZWdpblRyYW5zYWN0aW9uUmVzcG9uc2UiABJhCghSb2xsYmFjaxIoLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJvbGxiYWNrUmVxdWVzdBopLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJvbGxiYWNrUmVzcG9uc2UiABJbCgZDb21taXQSJi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db21taXRSZXF1ZXN0GicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29tbWl0UmVzcG9uc2UiABJhCghSdW5RdWVyeRIoLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJ1blF1ZXJ5UmVxdWVzdBopLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LlJ1blF1ZXJ5UmVzcG9uc2UiABJwCg1Db250aW51ZVF1ZXJ5Ei0uYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuQ29udGludWVRdWVyeVJlcXVlc3QaLi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Db250aW51ZVF1ZXJ5UmVzcG9uc2UiABJbCgZMb29rdXASJi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Mb29rdXBSZXF1ZXN0GicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTG9va3VwUmVzcG9uc2UiABJqCgtBbGxvY2F0ZUlkcxIrLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkFsbG9jYXRlSWRzUmVxdWVzdBosLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkFsbG9jYXRlSWRzUmVzcG9uc2UiABJYCgNHZXQSJi5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5Mb29rdXBSZXF1ZXN0GicuYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuTG9va3VwUmVzcG9uc2UiABJZCgVXcml0ZRIlLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LldyaXRlUmVxdWVzdBonLmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkNvbW1pdFJlc3BvbnNlIgBCIQofY29tLmdvb2dsZS5hcHBob3N0aW5nLmRhdGFzdG9yZQ=="))
   if _net_proto___parse__python is not None:
     _net_proto___parse__python.RegisterType(
         _SERIALIZED_DESCRIPTOR.tostring())
@@ -3709,6 +2915,800 @@
     _net_proto___parse__python.RegisterType(
         _SERIALIZED_DESCRIPTOR.tostring())
 
+class Mutation(ProtocolBuffer.ProtocolMessage):
+  has_force_ = 0
+  force_ = 0
+
+  def __init__(self, contents=None):
+    self.upsert_ = []
+    self.update_ = []
+    self.insert_ = []
+    self.insert_auto_id_ = []
+    self.delete_ = []
+    if contents is not None: self.MergeFromString(contents)
+
+  def upsert_size(self): return len(self.upsert_)
+  def upsert_list(self): return self.upsert_
+
+  def upsert(self, i):
+    return self.upsert_[i]
+
+  def mutable_upsert(self, i):
+    return self.upsert_[i]
+
+  def add_upsert(self):
+    x = google.appengine.datastore.entity_v4_pb.Entity()
+    self.upsert_.append(x)
+    return x
+
+  def clear_upsert(self):
+    self.upsert_ = []
+  def update_size(self): return len(self.update_)
+  def update_list(self): return self.update_
+
+  def update(self, i):
+    return self.update_[i]
+
+  def mutable_update(self, i):
+    return self.update_[i]
+
+  def add_update(self):
+    x = google.appengine.datastore.entity_v4_pb.Entity()
+    self.update_.append(x)
+    return x
+
+  def clear_update(self):
+    self.update_ = []
+  def insert_size(self): return len(self.insert_)
+  def insert_list(self): return self.insert_
+
+  def insert(self, i):
+    return self.insert_[i]
+
+  def mutable_insert(self, i):
+    return self.insert_[i]
+
+  def add_insert(self):
+    x = google.appengine.datastore.entity_v4_pb.Entity()
+    self.insert_.append(x)
+    return x
+
+  def clear_insert(self):
+    self.insert_ = []
+  def insert_auto_id_size(self): return len(self.insert_auto_id_)
+  def insert_auto_id_list(self): return self.insert_auto_id_
+
+  def insert_auto_id(self, i):
+    return self.insert_auto_id_[i]
+
+  def mutable_insert_auto_id(self, i):
+    return self.insert_auto_id_[i]
+
+  def add_insert_auto_id(self):
+    x = google.appengine.datastore.entity_v4_pb.Entity()
+    self.insert_auto_id_.append(x)
+    return x
+
+  def clear_insert_auto_id(self):
+    self.insert_auto_id_ = []
+  def delete_size(self): return len(self.delete_)
+  def delete_list(self): return self.delete_
+
+  def delete(self, i):
+    return self.delete_[i]
+
+  def mutable_delete(self, i):
+    return self.delete_[i]
+
+  def add_delete(self):
+    x = google.appengine.datastore.entity_v4_pb.Key()
+    self.delete_.append(x)
+    return x
+
+  def clear_delete(self):
+    self.delete_ = []
+  def force(self): return self.force_
+
+  def set_force(self, x):
+    self.has_force_ = 1
+    self.force_ = x
+
+  def clear_force(self):
+    if self.has_force_:
+      self.has_force_ = 0
+      self.force_ = 0
+
+  def has_force(self): return self.has_force_
+
+
+  def MergeFrom(self, x):
+    assert x is not self
+    for i in xrange(x.upsert_size()): self.add_upsert().CopyFrom(x.upsert(i))
+    for i in xrange(x.update_size()): self.add_update().CopyFrom(x.update(i))
+    for i in xrange(x.insert_size()): self.add_insert().CopyFrom(x.insert(i))
+    for i in xrange(x.insert_auto_id_size()): self.add_insert_auto_id().CopyFrom(x.insert_auto_id(i))
+    for i in xrange(x.delete_size()): self.add_delete().CopyFrom(x.delete(i))
+    if (x.has_force()): self.set_force(x.force())
+
+  if _net_proto___parse__python is not None:
+    def _CMergeFromString(self, s):
+      _net_proto___parse__python.MergeFromString(self, 'apphosting.datastore.v4.Mutation', s)
+
+  if _net_proto___parse__python is not None:
+    def _CEncode(self):
+      return _net_proto___parse__python.Encode(self, 'apphosting.datastore.v4.Mutation')
+
+  if _net_proto___parse__python is not None:
+    def _CEncodePartial(self):
+      return _net_proto___parse__python.EncodePartial(self, 'apphosting.datastore.v4.Mutation')
+
+  if _net_proto___parse__python is not None:
+    def _CToASCII(self, output_format):
+      return _net_proto___parse__python.ToASCII(self, 'apphosting.datastore.v4.Mutation', output_format)
+
+
+  if _net_proto___parse__python is not None:
+    def ParseASCII(self, s):
+      _net_proto___parse__python.ParseASCII(self, 'apphosting.datastore.v4.Mutation', s)
+
+
+  if _net_proto___parse__python is not None:
+    def ParseASCIIIgnoreUnknown(self, s):
+      _net_proto___parse__python.ParseASCIIIgnoreUnknown(self, 'apphosting.datastore.v4.Mutation', s)
+
+
+  def Equals(self, x):
+    if x is self: return 1
+    if len(self.upsert_) != len(x.upsert_): return 0
+    for e1, e2 in zip(self.upsert_, x.upsert_):
+      if e1 != e2: return 0
+    if len(self.update_) != len(x.update_): return 0
+    for e1, e2 in zip(self.update_, x.update_):
+      if e1 != e2: return 0
+    if len(self.insert_) != len(x.insert_): return 0
+    for e1, e2 in zip(self.insert_, x.insert_):
+      if e1 != e2: return 0
+    if len(self.insert_auto_id_) != len(x.insert_auto_id_): return 0
+    for e1, e2 in zip(self.insert_auto_id_, x.insert_auto_id_):
+      if e1 != e2: return 0
+    if len(self.delete_) != len(x.delete_): return 0
+    for e1, e2 in zip(self.delete_, x.delete_):
+      if e1 != e2: return 0
+    if self.has_force_ != x.has_force_: return 0
+    if self.has_force_ and self.force_ != x.force_: return 0
+    return 1
+
+  def IsInitialized(self, debug_strs=None):
+    initialized = 1
+    for p in self.upsert_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    for p in self.update_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    for p in self.insert_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    for p in self.insert_auto_id_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    for p in self.delete_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    return initialized
+
+  def ByteSize(self):
+    n = 0
+    n += 1 * len(self.upsert_)
+    for i in xrange(len(self.upsert_)): n += self.lengthString(self.upsert_[i].ByteSize())
+    n += 1 * len(self.update_)
+    for i in xrange(len(self.update_)): n += self.lengthString(self.update_[i].ByteSize())
+    n += 1 * len(self.insert_)
+    for i in xrange(len(self.insert_)): n += self.lengthString(self.insert_[i].ByteSize())
+    n += 1 * len(self.insert_auto_id_)
+    for i in xrange(len(self.insert_auto_id_)): n += self.lengthString(self.insert_auto_id_[i].ByteSize())
+    n += 1 * len(self.delete_)
+    for i in xrange(len(self.delete_)): n += self.lengthString(self.delete_[i].ByteSize())
+    if (self.has_force_): n += 2
+    return n
+
+  def ByteSizePartial(self):
+    n = 0
+    n += 1 * len(self.upsert_)
+    for i in xrange(len(self.upsert_)): n += self.lengthString(self.upsert_[i].ByteSizePartial())
+    n += 1 * len(self.update_)
+    for i in xrange(len(self.update_)): n += self.lengthString(self.update_[i].ByteSizePartial())
+    n += 1 * len(self.insert_)
+    for i in xrange(len(self.insert_)): n += self.lengthString(self.insert_[i].ByteSizePartial())
+    n += 1 * len(self.insert_auto_id_)
+    for i in xrange(len(self.insert_auto_id_)): n += self.lengthString(self.insert_auto_id_[i].ByteSizePartial())
+    n += 1 * len(self.delete_)
+    for i in xrange(len(self.delete_)): n += self.lengthString(self.delete_[i].ByteSizePartial())
+    if (self.has_force_): n += 2
+    return n
+
+  def Clear(self):
+    self.clear_upsert()
+    self.clear_update()
+    self.clear_insert()
+    self.clear_insert_auto_id()
+    self.clear_delete()
+    self.clear_force()
+
+  def OutputUnchecked(self, out):
+    for i in xrange(len(self.upsert_)):
+      out.putVarInt32(10)
+      out.putVarInt32(self.upsert_[i].ByteSize())
+      self.upsert_[i].OutputUnchecked(out)
+    for i in xrange(len(self.update_)):
+      out.putVarInt32(18)
+      out.putVarInt32(self.update_[i].ByteSize())
+      self.update_[i].OutputUnchecked(out)
+    for i in xrange(len(self.insert_)):
+      out.putVarInt32(26)
+      out.putVarInt32(self.insert_[i].ByteSize())
+      self.insert_[i].OutputUnchecked(out)
+    for i in xrange(len(self.insert_auto_id_)):
+      out.putVarInt32(34)
+      out.putVarInt32(self.insert_auto_id_[i].ByteSize())
+      self.insert_auto_id_[i].OutputUnchecked(out)
+    for i in xrange(len(self.delete_)):
+      out.putVarInt32(42)
+      out.putVarInt32(self.delete_[i].ByteSize())
+      self.delete_[i].OutputUnchecked(out)
+    if (self.has_force_):
+      out.putVarInt32(48)
+      out.putBoolean(self.force_)
+
+  def OutputPartial(self, out):
+    for i in xrange(len(self.upsert_)):
+      out.putVarInt32(10)
+      out.putVarInt32(self.upsert_[i].ByteSizePartial())
+      self.upsert_[i].OutputPartial(out)
+    for i in xrange(len(self.update_)):
+      out.putVarInt32(18)
+      out.putVarInt32(self.update_[i].ByteSizePartial())
+      self.update_[i].OutputPartial(out)
+    for i in xrange(len(self.insert_)):
+      out.putVarInt32(26)
+      out.putVarInt32(self.insert_[i].ByteSizePartial())
+      self.insert_[i].OutputPartial(out)
+    for i in xrange(len(self.insert_auto_id_)):
+      out.putVarInt32(34)
+      out.putVarInt32(self.insert_auto_id_[i].ByteSizePartial())
+      self.insert_auto_id_[i].OutputPartial(out)
+    for i in xrange(len(self.delete_)):
+      out.putVarInt32(42)
+      out.putVarInt32(self.delete_[i].ByteSizePartial())
+      self.delete_[i].OutputPartial(out)
+    if (self.has_force_):
+      out.putVarInt32(48)
+      out.putBoolean(self.force_)
+
+  def TryMerge(self, d):
+    while d.avail() > 0:
+      tt = d.getVarInt32()
+      if tt == 10:
+        length = d.getVarInt32()
+        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+        d.skip(length)
+        self.add_upsert().TryMerge(tmp)
+        continue
+      if tt == 18:
+        length = d.getVarInt32()
+        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+        d.skip(length)
+        self.add_update().TryMerge(tmp)
+        continue
+      if tt == 26:
+        length = d.getVarInt32()
+        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+        d.skip(length)
+        self.add_insert().TryMerge(tmp)
+        continue
+      if tt == 34:
+        length = d.getVarInt32()
+        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+        d.skip(length)
+        self.add_insert_auto_id().TryMerge(tmp)
+        continue
+      if tt == 42:
+        length = d.getVarInt32()
+        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+        d.skip(length)
+        self.add_delete().TryMerge(tmp)
+        continue
+      if tt == 48:
+        self.set_force(d.getBoolean())
+        continue
+
+
+      if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
+      d.skipData(tt)
+
+
+  def __str__(self, prefix="", printElemNumber=0):
+    res=""
+    cnt=0
+    for e in self.upsert_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("upsert%s <\n" % elm)
+      res+=e.__str__(prefix + "  ", printElemNumber)
+      res+=prefix+">\n"
+      cnt+=1
+    cnt=0
+    for e in self.update_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("update%s <\n" % elm)
+      res+=e.__str__(prefix + "  ", printElemNumber)
+      res+=prefix+">\n"
+      cnt+=1
+    cnt=0
+    for e in self.insert_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("insert%s <\n" % elm)
+      res+=e.__str__(prefix + "  ", printElemNumber)
+      res+=prefix+">\n"
+      cnt+=1
+    cnt=0
+    for e in self.insert_auto_id_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("insert_auto_id%s <\n" % elm)
+      res+=e.__str__(prefix + "  ", printElemNumber)
+      res+=prefix+">\n"
+      cnt+=1
+    cnt=0
+    for e in self.delete_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("delete%s <\n" % elm)
+      res+=e.__str__(prefix + "  ", printElemNumber)
+      res+=prefix+">\n"
+      cnt+=1
+    if self.has_force_: res+=prefix+("force: %s\n" % self.DebugFormatBool(self.force_))
+    return res
+
+
+  def _BuildTagLookupTable(sparse, maxtag, default=None):
+    return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
+
+  kupsert = 1
+  kupdate = 2
+  kinsert = 3
+  kinsert_auto_id = 4
+  kdelete = 5
+  kforce = 6
+
+  _TEXT = _BuildTagLookupTable({
+    0: "ErrorCode",
+    1: "upsert",
+    2: "update",
+    3: "insert",
+    4: "insert_auto_id",
+    5: "delete",
+    6: "force",
+  }, 6)
+
+  _TYPES = _BuildTagLookupTable({
+    0: ProtocolBuffer.Encoder.NUMERIC,
+    1: ProtocolBuffer.Encoder.STRING,
+    2: ProtocolBuffer.Encoder.STRING,
+    3: ProtocolBuffer.Encoder.STRING,
+    4: ProtocolBuffer.Encoder.STRING,
+    5: ProtocolBuffer.Encoder.STRING,
+    6: ProtocolBuffer.Encoder.NUMERIC,
+  }, 6, ProtocolBuffer.Encoder.MAX_TYPE)
+
+
+  _STYLE = """"""
+  _STYLE_CONTENT_TYPE = """"""
+  _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.Mutation'
+  _SERIALIZED_DESCRIPTOR = array.array('B')
+  _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KIGFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lk11dGF0aW9uExoGdXBzZXJ0IAEoAjALOANKHmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0LkVudGl0eaMBqgEFY3R5cGWyAQZwcm90bzKkARQTGgZ1cGRhdGUgAigCMAs4A0oeYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5owGqAQVjdHlwZbIBBnByb3RvMqQBFBMaBmluc2VydCADKAIwCzgDSh5hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FbnRpdHmjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoOaW5zZXJ0X2F1dG9faWQgBCgCMAs4A0oeYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRW50aXR5owGqAQVjdHlwZbIBBnByb3RvMqQBFBMaBmRlbGV0ZSAFKAIwCzgDShthcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXmjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoFZm9yY2UgBigAMAg4ARTCAR1hcHBob3N0aW5nLmRhdGFzdG9yZS52NC5FcnJvcg=="))
+  if _net_proto___parse__python is not None:
+    _net_proto___parse__python.RegisterType(
+        _SERIALIZED_DESCRIPTOR.tostring())
+
+class MutationResult(ProtocolBuffer.ProtocolMessage):
+  has_index_updates_ = 0
+  index_updates_ = 0
+
+  def __init__(self, contents=None):
+    self.insert_auto_id_key_ = []
+    self.upsert_version_ = []
+    self.update_version_ = []
+    self.insert_version_ = []
+    self.insert_auto_id_version_ = []
+    self.delete_version_ = []
+    if contents is not None: self.MergeFromString(contents)
+
+  def index_updates(self): return self.index_updates_
+
+  def set_index_updates(self, x):
+    self.has_index_updates_ = 1
+    self.index_updates_ = x
+
+  def clear_index_updates(self):
+    if self.has_index_updates_:
+      self.has_index_updates_ = 0
+      self.index_updates_ = 0
+
+  def has_index_updates(self): return self.has_index_updates_
+
+  def insert_auto_id_key_size(self): return len(self.insert_auto_id_key_)
+  def insert_auto_id_key_list(self): return self.insert_auto_id_key_
+
+  def insert_auto_id_key(self, i):
+    return self.insert_auto_id_key_[i]
+
+  def mutable_insert_auto_id_key(self, i):
+    return self.insert_auto_id_key_[i]
+
+  def add_insert_auto_id_key(self):
+    x = google.appengine.datastore.entity_v4_pb.Key()
+    self.insert_auto_id_key_.append(x)
+    return x
+
+  def clear_insert_auto_id_key(self):
+    self.insert_auto_id_key_ = []
+  def upsert_version_size(self): return len(self.upsert_version_)
+  def upsert_version_list(self): return self.upsert_version_
+
+  def upsert_version(self, i):
+    return self.upsert_version_[i]
+
+  def set_upsert_version(self, i, x):
+    self.upsert_version_[i] = x
+
+  def add_upsert_version(self, x):
+    self.upsert_version_.append(x)
+
+  def clear_upsert_version(self):
+    self.upsert_version_ = []
+
+  def update_version_size(self): return len(self.update_version_)
+  def update_version_list(self): return self.update_version_
+
+  def update_version(self, i):
+    return self.update_version_[i]
+
+  def set_update_version(self, i, x):
+    self.update_version_[i] = x
+
+  def add_update_version(self, x):
+    self.update_version_.append(x)
+
+  def clear_update_version(self):
+    self.update_version_ = []
+
+  def insert_version_size(self): return len(self.insert_version_)
+  def insert_version_list(self): return self.insert_version_
+
+  def insert_version(self, i):
+    return self.insert_version_[i]
+
+  def set_insert_version(self, i, x):
+    self.insert_version_[i] = x
+
+  def add_insert_version(self, x):
+    self.insert_version_.append(x)
+
+  def clear_insert_version(self):
+    self.insert_version_ = []
+
+  def insert_auto_id_version_size(self): return len(self.insert_auto_id_version_)
+  def insert_auto_id_version_list(self): return self.insert_auto_id_version_
+
+  def insert_auto_id_version(self, i):
+    return self.insert_auto_id_version_[i]
+
+  def set_insert_auto_id_version(self, i, x):
+    self.insert_auto_id_version_[i] = x
+
+  def add_insert_auto_id_version(self, x):
+    self.insert_auto_id_version_.append(x)
+
+  def clear_insert_auto_id_version(self):
+    self.insert_auto_id_version_ = []
+
+  def delete_version_size(self): return len(self.delete_version_)
+  def delete_version_list(self): return self.delete_version_
+
+  def delete_version(self, i):
+    return self.delete_version_[i]
+
+  def set_delete_version(self, i, x):
+    self.delete_version_[i] = x
+
+  def add_delete_version(self, x):
+    self.delete_version_.append(x)
+
+  def clear_delete_version(self):
+    self.delete_version_ = []
+
+
+  def MergeFrom(self, x):
+    assert x is not self
+    if (x.has_index_updates()): self.set_index_updates(x.index_updates())
+    for i in xrange(x.insert_auto_id_key_size()): self.add_insert_auto_id_key().CopyFrom(x.insert_auto_id_key(i))
+    for i in xrange(x.upsert_version_size()): self.add_upsert_version(x.upsert_version(i))
+    for i in xrange(x.update_version_size()): self.add_update_version(x.update_version(i))
+    for i in xrange(x.insert_version_size()): self.add_insert_version(x.insert_version(i))
+    for i in xrange(x.insert_auto_id_version_size()): self.add_insert_auto_id_version(x.insert_auto_id_version(i))
+    for i in xrange(x.delete_version_size()): self.add_delete_version(x.delete_version(i))
+
+  if _net_proto___parse__python is not None:
+    def _CMergeFromString(self, s):
+      _net_proto___parse__python.MergeFromString(self, 'apphosting.datastore.v4.MutationResult', s)
+
+  if _net_proto___parse__python is not None:
+    def _CEncode(self):
+      return _net_proto___parse__python.Encode(self, 'apphosting.datastore.v4.MutationResult')
+
+  if _net_proto___parse__python is not None:
+    def _CEncodePartial(self):
+      return _net_proto___parse__python.EncodePartial(self, 'apphosting.datastore.v4.MutationResult')
+
+  if _net_proto___parse__python is not None:
+    def _CToASCII(self, output_format):
+      return _net_proto___parse__python.ToASCII(self, 'apphosting.datastore.v4.MutationResult', output_format)
+
+
+  if _net_proto___parse__python is not None:
+    def ParseASCII(self, s):
+      _net_proto___parse__python.ParseASCII(self, 'apphosting.datastore.v4.MutationResult', s)
+
+
+  if _net_proto___parse__python is not None:
+    def ParseASCIIIgnoreUnknown(self, s):
+      _net_proto___parse__python.ParseASCIIIgnoreUnknown(self, 'apphosting.datastore.v4.MutationResult', s)
+
+
+  def Equals(self, x):
+    if x is self: return 1
+    if self.has_index_updates_ != x.has_index_updates_: return 0
+    if self.has_index_updates_ and self.index_updates_ != x.index_updates_: return 0
+    if len(self.insert_auto_id_key_) != len(x.insert_auto_id_key_): return 0
+    for e1, e2 in zip(self.insert_auto_id_key_, x.insert_auto_id_key_):
+      if e1 != e2: return 0
+    if len(self.upsert_version_) != len(x.upsert_version_): return 0
+    for e1, e2 in zip(self.upsert_version_, x.upsert_version_):
+      if e1 != e2: return 0
+    if len(self.update_version_) != len(x.update_version_): return 0
+    for e1, e2 in zip(self.update_version_, x.update_version_):
+      if e1 != e2: return 0
+    if len(self.insert_version_) != len(x.insert_version_): return 0
+    for e1, e2 in zip(self.insert_version_, x.insert_version_):
+      if e1 != e2: return 0
+    if len(self.insert_auto_id_version_) != len(x.insert_auto_id_version_): return 0
+    for e1, e2 in zip(self.insert_auto_id_version_, x.insert_auto_id_version_):
+      if e1 != e2: return 0
+    if len(self.delete_version_) != len(x.delete_version_): return 0
+    for e1, e2 in zip(self.delete_version_, x.delete_version_):
+      if e1 != e2: return 0
+    return 1
+
+  def IsInitialized(self, debug_strs=None):
+    initialized = 1
+    if (not self.has_index_updates_):
+      initialized = 0
+      if debug_strs is not None:
+        debug_strs.append('Required field: index_updates not set.')
+    for p in self.insert_auto_id_key_:
+      if not p.IsInitialized(debug_strs): initialized=0
+    return initialized
+
+  def ByteSize(self):
+    n = 0
+    n += self.lengthVarInt64(self.index_updates_)
+    n += 1 * len(self.insert_auto_id_key_)
+    for i in xrange(len(self.insert_auto_id_key_)): n += self.lengthString(self.insert_auto_id_key_[i].ByteSize())
+    n += 1 * len(self.upsert_version_)
+    for i in xrange(len(self.upsert_version_)): n += self.lengthVarInt64(self.upsert_version_[i])
+    n += 1 * len(self.update_version_)
+    for i in xrange(len(self.update_version_)): n += self.lengthVarInt64(self.update_version_[i])
+    n += 1 * len(self.insert_version_)
+    for i in xrange(len(self.insert_version_)): n += self.lengthVarInt64(self.insert_version_[i])
+    n += 1 * len(self.insert_auto_id_version_)
+    for i in xrange(len(self.insert_auto_id_version_)): n += self.lengthVarInt64(self.insert_auto_id_version_[i])
+    n += 1 * len(self.delete_version_)
+    for i in xrange(len(self.delete_version_)): n += self.lengthVarInt64(self.delete_version_[i])
+    return n + 1
+
+  def ByteSizePartial(self):
+    n = 0
+    if (self.has_index_updates_):
+      n += 1
+      n += self.lengthVarInt64(self.index_updates_)
+    n += 1 * len(self.insert_auto_id_key_)
+    for i in xrange(len(self.insert_auto_id_key_)): n += self.lengthString(self.insert_auto_id_key_[i].ByteSizePartial())
+    n += 1 * len(self.upsert_version_)
+    for i in xrange(len(self.upsert_version_)): n += self.lengthVarInt64(self.upsert_version_[i])
+    n += 1 * len(self.update_version_)
+    for i in xrange(len(self.update_version_)): n += self.lengthVarInt64(self.update_version_[i])
+    n += 1 * len(self.insert_version_)
+    for i in xrange(len(self.insert_version_)): n += self.lengthVarInt64(self.insert_version_[i])
+    n += 1 * len(self.insert_auto_id_version_)
+    for i in xrange(len(self.insert_auto_id_version_)): n += self.lengthVarInt64(self.insert_auto_id_version_[i])
+    n += 1 * len(self.delete_version_)
+    for i in xrange(len(self.delete_version_)): n += self.lengthVarInt64(self.delete_version_[i])
+    return n
+
+  def Clear(self):
+    self.clear_index_updates()
+    self.clear_insert_auto_id_key()
+    self.clear_upsert_version()
+    self.clear_update_version()
+    self.clear_insert_version()
+    self.clear_insert_auto_id_version()
+    self.clear_delete_version()
+
+  def OutputUnchecked(self, out):
+    out.putVarInt32(8)
+    out.putVarInt32(self.index_updates_)
+    for i in xrange(len(self.insert_auto_id_key_)):
+      out.putVarInt32(18)
+      out.putVarInt32(self.insert_auto_id_key_[i].ByteSize())
+      self.insert_auto_id_key_[i].OutputUnchecked(out)
+    for i in xrange(len(self.upsert_version_)):
+      out.putVarInt32(24)
+      out.putVarInt64(self.upsert_version_[i])
+    for i in xrange(len(self.update_version_)):
+      out.putVarInt32(32)
+      out.putVarInt64(self.update_version_[i])
+    for i in xrange(len(self.insert_version_)):
+      out.putVarInt32(40)
+      out.putVarInt64(self.insert_version_[i])
+    for i in xrange(len(self.insert_auto_id_version_)):
+      out.putVarInt32(48)
+      out.putVarInt64(self.insert_auto_id_version_[i])
+    for i in xrange(len(self.delete_version_)):
+      out.putVarInt32(56)
+      out.putVarInt64(self.delete_version_[i])
+
+  def OutputPartial(self, out):
+    if (self.has_index_updates_):
+      out.putVarInt32(8)
+      out.putVarInt32(self.index_updates_)
+    for i in xrange(len(self.insert_auto_id_key_)):
+      out.putVarInt32(18)
+      out.putVarInt32(self.insert_auto_id_key_[i].ByteSizePartial())
+      self.insert_auto_id_key_[i].OutputPartial(out)
+    for i in xrange(len(self.upsert_version_)):
+      out.putVarInt32(24)
+      out.putVarInt64(self.upsert_version_[i])
+    for i in xrange(len(self.update_version_)):
+      out.putVarInt32(32)
+      out.putVarInt64(self.update_version_[i])
+    for i in xrange(len(self.insert_version_)):
+      out.putVarInt32(40)
+      out.putVarInt64(self.insert_version_[i])
+    for i in xrange(len(self.insert_auto_id_version_)):
+      out.putVarInt32(48)
+      out.putVarInt64(self.insert_auto_id_version_[i])
+    for i in xrange(len(self.delete_version_)):
+      out.putVarInt32(56)
+      out.putVarInt64(self.delete_version_[i])
+
+  def TryMerge(self, d):
+    while d.avail() > 0:
+      tt = d.getVarInt32()
+      if tt == 8:
+        self.set_index_updates(d.getVarInt32())
+        continue
+      if tt == 18:
+        length = d.getVarInt32()
+        tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+        d.skip(length)
+        self.add_insert_auto_id_key().TryMerge(tmp)
+        continue
+      if tt == 24:
+        self.add_upsert_version(d.getVarInt64())
+        continue
+      if tt == 32:
+        self.add_update_version(d.getVarInt64())
+        continue
+      if tt == 40:
+        self.add_insert_version(d.getVarInt64())
+        continue
+      if tt == 48:
+        self.add_insert_auto_id_version(d.getVarInt64())
+        continue
+      if tt == 56:
+        self.add_delete_version(d.getVarInt64())
+        continue
+
+
+      if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
+      d.skipData(tt)
+
+
+  def __str__(self, prefix="", printElemNumber=0):
+    res=""
+    if self.has_index_updates_: res+=prefix+("index_updates: %s\n" % self.DebugFormatInt32(self.index_updates_))
+    cnt=0
+    for e in self.insert_auto_id_key_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("insert_auto_id_key%s <\n" % elm)
+      res+=e.__str__(prefix + "  ", printElemNumber)
+      res+=prefix+">\n"
+      cnt+=1
+    cnt=0
+    for e in self.upsert_version_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("upsert_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
+      cnt+=1
+    cnt=0
+    for e in self.update_version_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("update_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
+      cnt+=1
+    cnt=0
+    for e in self.insert_version_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("insert_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
+      cnt+=1
+    cnt=0
+    for e in self.insert_auto_id_version_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("insert_auto_id_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
+      cnt+=1
+    cnt=0
+    for e in self.delete_version_:
+      elm=""
+      if printElemNumber: elm="(%d)" % cnt
+      res+=prefix+("delete_version%s: %s\n" % (elm, self.DebugFormatInt64(e)))
+      cnt+=1
+    return res
+
+
+  def _BuildTagLookupTable(sparse, maxtag, default=None):
+    return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
+
+  kindex_updates = 1
+  kinsert_auto_id_key = 2
+  kupsert_version = 3
+  kupdate_version = 4
+  kinsert_version = 5
+  kinsert_auto_id_version = 6
+  kdelete_version = 7
+
+  _TEXT = _BuildTagLookupTable({
+    0: "ErrorCode",
+    1: "index_updates",
+    2: "insert_auto_id_key",
+    3: "upsert_version",
+    4: "update_version",
+    5: "insert_version",
+    6: "insert_auto_id_version",
+    7: "delete_version",
+  }, 7)
+
+  _TYPES = _BuildTagLookupTable({
+    0: ProtocolBuffer.Encoder.NUMERIC,
+    1: ProtocolBuffer.Encoder.NUMERIC,
+    2: ProtocolBuffer.Encoder.STRING,
+    3: ProtocolBuffer.Encoder.NUMERIC,
+    4: ProtocolBuffer.Encoder.NUMERIC,
+    5: ProtocolBuffer.Encoder.NUMERIC,
+    6: ProtocolBuffer.Encoder.NUMERIC,
+    7: ProtocolBuffer.Encoder.NUMERIC,
+  }, 7, ProtocolBuffer.Encoder.MAX_TYPE)
+
+
+  _STYLE = """"""
+  _STYLE_CONTENT_TYPE = """"""
+  _PROTO_DESCRIPTOR_NAME = 'apphosting.datastore.v4.MutationResult'
+  _SERIALIZED_DESCRIPTOR = array.array('B')
+  _SERIALIZED_DESCRIPTOR.fromstring(base64.decodestring("WidhcHBob3N0aW5nL2RhdGFzdG9yZS9kYXRhc3RvcmVfdjQucHJvdG8KJmFwcGhvc3RpbmcuZGF0YXN0b3JlLnY0Lk11dGF0aW9uUmVzdWx0ExoNaW5kZXhfdXBkYXRlcyABKAAwBTgCFBMaEmluc2VydF9hdXRvX2lkX2tleSACKAIwCzgDShthcHBob3N0aW5nLmRhdGFzdG9yZS52NC5LZXmjAaoBBWN0eXBlsgEGcHJvdG8ypAEUExoOdXBzZXJ0X3ZlcnNpb24gAygAMAM4AxQTGg51cGRhdGVfdmVyc2lvbiAEKAAwAzgDFBMaDmluc2VydF92ZXJzaW9uIAUoADADOAMUExoWaW5zZXJ0X2F1dG9faWRfdmVyc2lvbiAGKAAwAzgDFBMaDmRlbGV0ZV92ZXJzaW9uIAcoADADOAMUwgEdYXBwaG9zdGluZy5kYXRhc3RvcmUudjQuRXJyb3I="))
+  if _net_proto___parse__python is not None:
+    _net_proto___parse__python.RegisterType(
+        _SERIALIZED_DESCRIPTOR.tostring())
+
 class ReadOptions(ProtocolBuffer.ProtocolMessage):
 
 
@@ -6826,6 +6826,7 @@
 
   @classmethod
   def _MethodSignatures(cls):
+    """Returns a dict of {<method-name>: (<request-type>, <response-type>)}."""
     return {
       'BeginTransaction': (BeginTransactionRequest, BeginTransactionResponse),
       'Rollback': (RollbackRequest, RollbackResponse),
@@ -6838,6 +6839,12 @@
       'Write': (WriteRequest, CommitResponse),
       }
 
+  @classmethod
+  def _StreamMethodSignatures(cls):
+    """Returns a dict of {<method-name>: (<request-type>, <stream-type>, <response-type>)}."""
+    return {
+      }
+
   def __init__(self, *args, **kwargs):
     """Creates a Stubby RPC server.
 
@@ -7037,4 +7044,4 @@
 if _extension_runtime:
   pass
 
-__all__ = ['Error','Mutation','MutationResult','EntityResult','Query','KindExpression','PropertyReference','PropertyExpression','PropertyOrder','Filter','CompositeFilter','PropertyFilter','GqlQuery','GqlQueryArg','QueryResultBatch','ReadOptions','LookupRequest','LookupResponse','RunQueryRequest','RunQueryResponse','ContinueQueryRequest','ContinueQueryResponse','BeginTransactionRequest','BeginTransactionResponse','RollbackRequest','RollbackResponse','CommitRequest','CommitResponse','AllocateIdsRequest','AllocateIdsResponse','WriteRequest','DatastoreV4Service']
+__all__ = ['Error','EntityResult','Query','KindExpression','PropertyReference','PropertyExpression','PropertyOrder','Filter','CompositeFilter','PropertyFilter','GqlQuery','GqlQueryArg','QueryResultBatch','Mutation','MutationResult','ReadOptions','LookupRequest','LookupResponse','RunQueryRequest','RunQueryResponse','ContinueQueryRequest','ContinueQueryResponse','BeginTransactionRequest','BeginTransactionResponse','RollbackRequest','RollbackResponse','CommitRequest','CommitResponse','AllocateIdsRequest','AllocateIdsResponse','WriteRequest','DatastoreV4Service']
diff --git a/google/appengine/datastore/datastore_v4_stub.py b/google/appengine/datastore/datastore_v4_stub.py
index b983633..5e77289 100644
--- a/google/appengine/datastore/datastore_v4_stub.py
+++ b/google/appengine/datastore/datastore_v4_stub.py
@@ -31,10 +31,14 @@
 
 
 
+from google.appengine.datastore import entity_pb
 
+from google.appengine.api import api_base_pb
 from google.appengine.api import apiproxy_stub
 from google.appengine.api import apiproxy_stub_map
+from google.appengine.datastore import datastore_pb
 from google.appengine.datastore import datastore_pbs
+from google.appengine.datastore import datastore_stub_util
 from google.appengine.datastore import datastore_v4_pb
 from google.appengine.datastore import datastore_v4_validator
 from google.appengine.runtime import apiproxy_errors
@@ -54,9 +58,127 @@
     apiproxy_stub.APIProxyStub.__init__(self, SERVICE_NAME)
     self.__app_id = app_id
     self.__entity_converter = datastore_pbs.get_entity_converter()
+    self.__service_converter = datastore_stub_util.get_service_converter()
     self.__service_validator = datastore_v4_validator.get_service_validator()
 
+  def _Dynamic_BeginTransaction(self, req, resp):
+    try:
+      self.__service_validator.validate_begin_transaction_req(req)
+      v3_req = self.__service_converter.v4_to_v3_begin_transaction_req(
+          self.__app_id, req)
+      v3_resp = datastore_pb.Transaction()
+      self.__make_v3_call('BeginTransaction', v3_req, v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    except datastore_v4_validator.ValidationError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    try:
+      v4_resp = self.__service_converter.v3_to_v4_begin_transaction_resp(
+          v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.INTERNAL_ERROR, str(e))
+    resp.CopyFrom(v4_resp)
+
+  def _Dynamic_Rollback(self, req, unused_resp):
+    try:
+      self.__service_validator.validate_rollback_req(req)
+      v3_req = self.__service_converter.v4_rollback_req_to_v3_txn(req)
+      self.__make_v3_call('Rollback', v3_req, api_base_pb.VoidProto())
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    except datastore_v4_validator.ValidationError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+
+  def _Dynamic_Commit(self, req, resp):
+    try:
+      self.__service_validator.validate_commit_req(req)
+      if req.has_transaction():
+        resp.mutable_mutation_result()
+        resp.mutable_mutation_result().CopyFrom(
+            self.__apply_v4_mutation(req.mutation(), req.transaction()))
+        v3_req = self.__service_converter.v4_commit_req_to_v3_txn(req)
+        v3_resp = datastore_pb.CommitResponse()
+        self.__make_v3_call('Commit', v3_req, v3_resp)
+        total_index_updates = (resp.mutable_mutation_result().index_updates()
+                               + v3_resp.cost().index_writes())
+        resp.mutable_mutation_result().set_index_updates(total_index_updates)
+      else:
+        resp.mutable_mutation_result().CopyFrom(
+            self.__apply_v4_mutation(req.mutation(), None))
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    except datastore_v4_validator.ValidationError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+
+  def _Dynamic_RunQuery(self, req, resp):
+    try:
+      self.__normalize_v4_run_query_request(req)
+      self.__service_validator.validate_run_query_req(req)
+      v3_req = self.__service_converter.v4_run_query_req_to_v3_query(req)
+      v3_resp = datastore_pb.QueryResult()
+      self.__make_v3_call('RunQuery', v3_req, v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    except datastore_v4_validator.ValidationError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    try:
+      v4_resp = self.__service_converter.v3_to_v4_run_query_resp(v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.INTERNAL_ERROR, str(e))
+    resp.CopyFrom(v4_resp)
+
+  def _Dynamic_ContinueQuery(self, req, resp):
+    try:
+      self.__service_validator.validate_continue_query_req(req)
+      v3_req = self.__service_converter.v4_to_v3_next_req(req)
+      v3_resp = datastore_pb.QueryResult()
+      self.__make_v3_call('Next', v3_req, v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    except datastore_v4_validator.ValidationError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    try:
+      v4_resp = self.__service_converter.v3_to_v4_continue_query_resp(v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.INTERNAL_ERROR, str(e))
+    resp.CopyFrom(v4_resp)
+
+  def _Dynamic_Lookup(self, req, resp):
+    try:
+      self.__service_validator.validate_lookup_req(req)
+      v3_req = self.__service_converter.v4_to_v3_get_req(req)
+      v3_resp = datastore_pb.GetResponse()
+      self.__make_v3_call('Get', v3_req, v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    except datastore_v4_validator.ValidationError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.BAD_REQUEST, str(e))
+    try:
+      v4_resp = self.__service_converter.v3_to_v4_lookup_resp(v3_resp)
+    except datastore_pbs.InvalidConversionError, e:
+      raise apiproxy_errors.ApplicationError(
+          datastore_v4_pb.Error.INTERNAL_ERROR, str(e))
+    resp.CopyFrom(v4_resp)
+
   def _Dynamic_AllocateIds(self, req, resp):
+
+
+
     v3_stub = apiproxy_stub_map.apiproxy.GetStub(V3_SERVICE_NAME)
     try:
       self.__service_validator.validate_allocate_ids_req(req)
@@ -80,5 +202,158 @@
       raise apiproxy_errors.ApplicationError(
           datastore_v4_pb.Error.BAD_REQUEST, str(e))
 
+  def __insert_v3_entity(self, v3_entity, v3_txn):
+    """Inserts a v3 entity.
+
+    Args:
+      v3_entity: a datastore_v4_pb.Entity
+      v3_txn: a datastore_pb.Transaction or None
+
+    Returns:
+      the number of index writes that occurred
+
+    Raises:
+      ApplicationError: if the entity already exists
+    """
+    if not v3_txn:
+
+      v3_txn = datastore_pb.Transaction()
+      v3_begin_txn_req = datastore_pb.BeginTransactionRequest()
+      v3_begin_txn_req.set_app(v3_entity.key().app())
+      self.__make_v3_call('BeginTransaction', v3_begin_txn_req, v3_txn)
+      self.__insert_v3_entity(v3_entity, v3_txn)
+      v3_resp = datastore_pb.CommitResponse()
+      self.__make_v3_call('Commit', v3_txn, v3_resp)
+      return v3_resp.cost().index_writes()
+
+    v3_get_req = datastore_pb.GetRequest()
+    v3_get_req.mutable_transaction().CopyFrom(v3_txn)
+    v3_get_req.key_list().append(v3_entity.key())
+    v3_get_resp = datastore_pb.GetResponse()
+    self.__make_v3_call('Get', v3_get_req, v3_get_resp)
+    if v3_get_resp.entity(0).has_entity():
+      raise apiproxy_errors.ApplicationError(datastore_v4_pb.Error.BAD_REQUEST,
+                                             'Entity already exists.')
+    v3_put_req = datastore_pb.PutRequest()
+    v3_put_req.mutable_transaction().CopyFrom(v3_txn)
+    v3_put_req.entity_list().append(v3_entity)
+    v3_put_resp = datastore_pb.PutResponse()
+    self.__make_v3_call('Put', v3_put_req, v3_put_resp)
+    return v3_put_resp.cost().index_writes()
+
+  def __update_v3_entity(self, v3_entity, v3_txn):
+    """Updates a v3 entity.
+
+    Args:
+      v3_entity: a datastore_v4_pb.Entity
+      v3_txn: a datastore_pb.Transaction or None
+
+    Returns:
+      the number of index writes that occurred
+
+    Raises:
+      ApplicationError: if the entity does not exist
+    """
+    if not v3_txn:
+
+      v3_txn = datastore_pb.Transaction()
+      v3_begin_txn_req = datastore_pb.BeginTransactionRequest()
+      v3_begin_txn_req.set_app(v3_entity.key().app())
+      self.__make_v3_call('BeginTransaction', v3_begin_txn_req, v3_txn)
+      self.__update_v3_entity(v3_entity, v3_txn)
+      v3_resp = datastore_pb.CommitResponse()
+      self.__make_v3_call('Commit', v3_txn, v3_resp)
+      return v3_resp.cost().index_writes()
+
+    v3_get_req = datastore_pb.GetRequest()
+    v3_get_req.mutable_transaction().CopyFrom(v3_txn)
+    v3_get_req.key_list().append(v3_entity.key())
+    v3_get_resp = datastore_pb.GetResponse()
+    self.__make_v3_call('Get', v3_get_req, v3_get_resp)
+    if not v3_get_resp.entity(0).has_entity():
+      raise apiproxy_errors.ApplicationError(datastore_v4_pb.Error.BAD_REQUEST,
+                                             'Entity does not exist.')
+    v3_put_req = datastore_pb.PutRequest()
+    v3_put_req.mutable_transaction().CopyFrom(v3_txn)
+    v3_put_req.entity_list().append(v3_entity)
+    v3_put_resp = datastore_pb.PutResponse()
+    self.__make_v3_call('Put', v3_put_req, v3_put_resp)
+    return v3_put_resp.cost().index_writes()
+
+  def __apply_v4_mutation(self, v4_mutation, v4_txn):
+    """Applies a v4 Mutation.
+
+    Args:
+      v4_mutation: a datastore_v4_pb.Mutation
+      v4_txn: an optional v4 transaction handle or None
+
+    Returns:
+      a datastore_v4_pb.MutationResult
+    """
+    index_writes = 0
+    v3_txn = None
+    if v4_txn:
+      v3_txn = datastore_pb.Transaction()
+      self.__service_converter.v4_to_v3_txn(v4_txn, v3_txn)
+
+
+    for v4_entity in v4_mutation.insert_list():
+      v3_entity = entity_pb.EntityProto()
+      self.__entity_converter.v4_to_v3_entity(v4_entity, v3_entity)
+      index_writes += self.__insert_v3_entity(v3_entity, v3_txn)
+
+
+    for v4_entity in v4_mutation.update_list():
+      v3_entity = entity_pb.EntityProto()
+      self.__entity_converter.v4_to_v3_entity(v4_entity, v3_entity)
+      index_writes += self.__update_v3_entity(v3_entity, v3_txn)
+
+
+    v3_insert_auto_req = datastore_pb.PutRequest()
+    if v3_txn:
+      v3_insert_auto_req.mutable_transaction().CopyFrom(v3_txn)
+    for v4_entity in v4_mutation.insert_auto_id_list():
+      v3_entity = entity_pb.EntityProto()
+      self.__entity_converter.v4_to_v3_entity(v4_entity, v3_entity)
+      v3_insert_auto_req.entity_list().append(v3_entity)
+    v3_insert_auto_id_resp = datastore_pb.PutResponse()
+    self.__make_v3_call('Put', v3_insert_auto_req, v3_insert_auto_id_resp)
+    index_writes += v3_insert_auto_id_resp.cost().index_writes()
+
+
+    v3_upsert_req = datastore_pb.PutRequest()
+    if v3_txn:
+      v3_upsert_req.mutable_transaction().CopyFrom(v3_txn)
+    for v4_entity in v4_mutation.upsert_list():
+      v3_entity = entity_pb.EntityProto()
+      self.__entity_converter.v4_to_v3_entity(v4_entity, v3_entity)
+      v3_upsert_req.entity_list().append(v3_entity)
+    v3_upsert_resp = datastore_pb.PutResponse()
+    self.__make_v3_call('Put', v3_upsert_req, v3_upsert_resp)
+    index_writes += v3_upsert_resp.cost().index_writes()
+
+
+    v3_delete_req = datastore_pb.DeleteRequest()
+    if v3_txn:
+      v3_delete_req.mutable_transaction().CopyFrom(v3_txn)
+    for v4_key in v4_mutation.delete_list():
+      self.__entity_converter.v4_to_v3_reference(v4_key,
+                                                 v3_delete_req.add_key())
+    v3_delete_resp = datastore_pb.DeleteResponse()
+    self.__make_v3_call('Delete', v3_delete_req, v3_delete_resp)
+    index_writes += v3_delete_resp.cost().index_writes()
+
+    v4_mutation_result = datastore_v4_pb.MutationResult()
+    for v3_ref in v3_insert_auto_id_resp.key_list():
+      self.__entity_converter.v3_to_v4_key(
+          v3_ref, v4_mutation_result.add_insert_auto_id_key())
+    v4_mutation_result.set_index_updates(index_writes)
+
+    return v4_mutation_result
+
+  def __normalize_v4_run_query_request(self, v4_req):
+
+    pass
+
   def __make_v3_call(self, method, v3_req, v3_resp):
     apiproxy_stub_map.MakeSyncCall(V3_SERVICE_NAME, method, v3_req, v3_resp)
diff --git a/google/appengine/datastore/datastore_v4_validator.py b/google/appengine/datastore/datastore_v4_validator.py
index e0ea8db..5d958c3 100644
--- a/google/appengine/datastore/datastore_v4_validator.py
+++ b/google/appengine/datastore/datastore_v4_validator.py
@@ -506,7 +506,6 @@
                                 datastore_pbs.MAX_PARTITION_ID_LENGTH, desc)
     if not constraint.reserved_key_allowed:
       _assert_string_not_reserved(partition_dimension, desc)
-
     _assert_condition(_PARTITION_ID_RE.match(partition_dimension),
                       'Illegal string "%s" in %s.' % (partition_dimension,
                                                       desc))
@@ -1054,10 +1053,11 @@
 
     Raises:
       ValidationError: if the request is invalid
-      ValueError: if the request contains a GQL query
     """
-    if req.has_gql_query():
-      raise ValueError('RunQueryRequest not normalized.')
+
+
+
+    _assert_condition(not req.has_gql_query(), 'GQL not supported.')
     _assert_initialized(req)
     self.validate_read_options(req.read_options())
     self.__entity_validator.validate_partition_id(READ,
diff --git a/google/appengine/ext/analytics/static/analytics_js.js b/google/appengine/ext/analytics/static/analytics_js.js
index b8a441a..cf04198 100644
--- a/google/appengine/ext/analytics/static/analytics_js.js
+++ b/google/appengine/ext/analytics/static/analytics_js.js
@@ -1,25 +1,25 @@
-/* Copyright 2008-9 Google Inc. All Rights Reserved. */ (function(){var h,m=this,n=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
-else if("function"==b&&"undefined"==typeof a.call)return"object";return b},q=function(a){return"string"==typeof a},r=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},aa=Date.now||function(){return+new Date},t=function(a,b){var c=a.split("."),d=m;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},u=function(a,b){function c(){}
-c.prototype=b.prototype;a.o=b.prototype;a.prototype=new c};var v=function(a){Error.captureStackTrace?Error.captureStackTrace(this,v):this.stack=Error().stack||"";a&&(this.message=String(a))};u(v,Error);var ba=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join("%s")},x=function(a){a=String(a);var b=a.indexOf(".");-1==b&&(b=a.length);b=Math.max(0,2-b);return Array(b+1).join("0")+a};var y=function(a,b){b.unshift(a);v.call(this,ba.apply(null,b));b.shift()};u(y,v);var z=function(a,b,c){if(!a){var d=Array.prototype.slice.call(arguments,2),e="Assertion failed";if(b)var e=e+(": "+b),f=d;throw new y(""+e,f||[]);}};var A=Array.prototype,B=A.indexOf?function(a,b,c){z(null!=a.length);return A.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(q(a))return q(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},ca=A.forEach?function(a,b,c){z(null!=a.length);A.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=q(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},da=A.filter?function(a,b,c){z(null!=a.length);return A.filter.call(a,b,
-c)}:function(a,b,c){for(var d=a.length,e=[],f=0,g=q(a)?a.split(""):a,k=0;k<d;k++)if(k in g){var w=g[k];b.call(c,w,k,a)&&(e[f++]=w)}return e},C=function(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},D=function(a,b,c){z(null!=a.length);return 2>=arguments.length?A.slice.call(a,b):A.slice.call(a,b,c)};var E,F,G,H,ea=function(){return m.navigator?m.navigator.userAgent:null};H=G=F=E=!1;var I;if(I=ea()){var fa=m.navigator;E=0==I.lastIndexOf("Opera",0);F=!E&&(-1!=I.indexOf("MSIE")||-1!=I.indexOf("Trident"));G=!E&&-1!=I.indexOf("WebKit");H=!E&&!G&&!F&&"Gecko"==fa.product}var ga=E,J=F,K=H,L=G,ha=function(){var a=m.document;return a?a.documentMode:void 0},M;
-n:{var N="",O;if(ga&&m.opera)var P=m.opera.version,N="function"==typeof P?P():P;else if(K?O=/rv\:([^\);]+)(\)|;)/:J?O=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:L&&(O=/WebKit\/(\S+)/),O)var ia=O.exec(ea()),N=ia?ia[1]:"";if(J){var ja=ha();if(ja>parseFloat(N)){M=String(ja);break n}}M=N}
-var ka=M,la={},Q=function(a){var b;if(!(b=la[a])){b=0;for(var c=String(ka).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),d=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",k=d[f]||"",w=RegExp("(\\d*)(\\D*)","g"),p=RegExp("(\\d*)(\\D*)","g");do{var l=w.exec(g)||["","",""],s=p.exec(k)||["","",""];if(0==l[0].length&&0==s[0].length)break;b=((0==l[1].length?0:parseInt(l[1],10))<(0==s[1].length?0:parseInt(s[1],10))?-1:(0==l[1].length?
-0:parseInt(l[1],10))>(0==s[1].length?0:parseInt(s[1],10))?1:0)||((0==l[2].length)<(0==s[2].length)?-1:(0==l[2].length)>(0==s[2].length)?1:0)||(l[2]<s[2]?-1:l[2]>s[2]?1:0)}while(0==b)}b=la[a]=0<=b}return b},ma=m.document,na=ma&&J?ha()||("CSS1Compat"==ma.compatMode?parseInt(ka,10):5):void 0;!K&&!J||J&&J&&9<=na||K&&Q("1.9.1");J&&Q("9");var oa=function(a){a=a.className;return q(a)&&a.match(/\S+/g)||[]},pa=function(a,b){for(var c=oa(a),d=D(arguments,1),e=c,f=0;f<d.length;f++)0<=B(e,d[f])||e.push(d[f]);c=c.join(" ");a.className=c},ra=function(a,b){var c=oa(a),d=D(arguments,1),c=qa(c,d).join(" ");a.className=c},qa=function(a,b){return da(a,function(a){return!(0<=B(b,a))})};var R=function(a,b,c){var d=document;c=c||d;a=a&&"*"!=a?a.toUpperCase():"";if(c.querySelectorAll&&c.querySelector&&(a||b))return c.querySelectorAll(a+(b?"."+b:""));if(b&&c.getElementsByClassName){c=c.getElementsByClassName(b);if(a){for(var d={},e=0,f=0,g;g=c[f];f++)a==g.nodeName&&(d[e++]=g);d.length=e;return d}return c}c=c.getElementsByTagName(a||"*");if(b){d={};for(f=e=0;g=c[f];f++)a=g.className,"function"==typeof a.split&&0<=B(a.split(/\s+/),b)&&(d[e++]=g);d.length=e;return d}return c};var S=function(a){S[" "](a);return a};S[" "]=function(){};var sa=!J||J&&9<=na,ta=J&&!Q("9");!L||Q("528");K&&Q("1.9b")||J&&Q("8")||ga&&Q("9.5")||L&&Q("528");K&&!Q("8")||J&&Q("9");var T=function(a,b){this.type=a;this.currentTarget=this.target=b};T.prototype.i=!1;T.prototype.defaultPrevented=!1;T.prototype.preventDefault=function(){this.defaultPrevented=!0};var U=function(a,b){if(a){var c=this.type=a.type;T.call(this,c);this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(K){var e;n:{try{S(d.nodeName);e=!0;break n}catch(f){}e=!1}e||(d=null)}}else"mouseover"==c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=L||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=L||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:
-a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.j=a;a.defaultPrevented&&this.preventDefault();delete this.i}};u(U,T);h=U.prototype;h.target=null;h.relatedTarget=null;h.offsetX=0;h.offsetY=0;h.clientX=0;h.clientY=0;h.screenX=0;h.screenY=0;h.button=0;h.keyCode=0;
-h.charCode=0;h.ctrlKey=!1;h.altKey=!1;h.shiftKey=!1;h.metaKey=!1;h.j=null;h.preventDefault=function(){U.o.preventDefault.call(this);var a=this.j;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,ta)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var ua="closure_listenable_"+(1E6*Math.random()|0),va=function(a){try{return!(!a||!a[ua])}catch(b){return!1}},wa=0;var xa=function(a,b,c,d,e){this.c=a;this.e=null;this.src=b;this.type=c;this.capture=!!d;this.f=e;this.key=++wa;this.d=this.g=!1},ya=function(a){a.d=!0;a.c=null;a.e=null;a.src=null;a.f=null};var V=function(a){this.src=a;this.b={};this.h=0};V.prototype.add=function(a,b,c,d,e){var f=this.b[a];f||(f=this.b[a]=[],this.h++);var g;n:{for(g=0;g<f.length;++g){var k=f[g];if(!k.d&&k.c==b&&k.capture==!!d&&k.f==e)break n}g=-1}-1<g?(a=f[g],c||(a.g=!1)):(a=new xa(b,this.src,a,!!d,e),a.g=c,f.push(a));return a};var za=function(a,b){var c=b.type;if(c in a.b){var d=a.b[c],e=B(d,b),f;if(f=0<=e)z(null!=d.length),A.splice.call(d,e,1);f&&(ya(b),0==a.b[c].length&&(delete a.b[c],a.h--))}};var W="closure_lm_"+(1E6*Math.random()|0),X={},Aa=0,Ca=function(){var a=Ba,b=sa?function(c){return a.call(b.src,b.c,c)}:function(c){c=a.call(b.src,b.c,c);if(!c)return c};return b},Da=function(a,b,c,d,e){if("array"==n(b))for(var f=0;f<b.length;f++)Da(a,b[f],c,d,e);else if(c=Ea(c),va(a))a.k.add(String(b),c,!0,d,e);else{if(!b)throw Error("Invalid event type");var f=!!d,g=Y(a);g||(a[W]=g=new V(a));c=g.add(b,c,!0,d,e);c.e||(d=Ca(),c.e=d,d.src=a,d.c=c,a.addEventListener?a.addEventListener(b,d,f):a.attachEvent(b in
-X?X[b]:X[b]="on"+b,d),Aa++)}},Ga=function(a,b,c,d){var e=1;if(a=Y(a))if(b=a.b[b])for(b=C(b),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.d&&(e&=!1!==Fa(f,d))}return Boolean(e)},Fa=function(a,b){var c=a.c,d=a.f||a.src;if(a.g&&"number"!=typeof a&&a&&!a.d){var e=a.src;if(va(e))za(e.k,a);else{var f=a.type,g=a.e;e.removeEventListener?e.removeEventListener(f,g,a.capture):e.detachEvent&&e.detachEvent(f in X?X[f]:X[f]="on"+f,g);Aa--;(f=Y(e))?(za(f,a),0==f.h&&(f.src=null,e[W]=null)):ya(a)}}return c.call(d,
-b)},Ba=function(a,b){if(a.d)return!0;if(!sa){var c;if(!(c=b))n:{c=["window","event"];for(var d=m,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break n}c=d}e=c;c=new U(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){n:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break n}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,k=e.length-1;!c.i&&0<=k;k--)c.currentTarget=e[k],d&=Ga(e[k],f,!0,c);for(k=0;!c.i&&k<e.length;k++)c.currentTarget=
-e[k],d&=Ga(e[k],f,!1,c)}return d}return Fa(a,new U(b,this))},Y=function(a){a=a[W];return a instanceof V?a:null},Ha="__closure_events_fn_"+(1E9*Math.random()>>>0),Ea=function(a){z(a,"Listener can not be null.");if("function"==n(a))return a;z(a.handleEvent,"An object listener must have handleEvent method.");return a[Ha]||(a[Ha]=function(b){return a.handleEvent(b)})};var $=function(a,b,c){"number"==typeof a?(this.a=new Date(a,b||0,c||1),Z(this,c||1)):(b=typeof a,"object"==b&&null!=a||"function"==b?(this.a=new Date(a.getFullYear(),a.getMonth(),a.getDate()),Z(this,a.getDate())):(this.a=new Date(aa()),this.a.setHours(0),this.a.setMinutes(0),this.a.setSeconds(0),this.a.setMilliseconds(0)))};h=$.prototype;h.getFullYear=function(){return this.a.getFullYear()};h.getYear=function(){return this.getFullYear()};h.getMonth=function(){return this.a.getMonth()};h.getDate=function(){return this.a.getDate()};
-h.getTime=function(){return this.a.getTime()};h.getUTCHours=function(){return this.a.getUTCHours()};h.setFullYear=function(a){this.a.setFullYear(a)};h.setMonth=function(a){this.a.setMonth(a)};h.setDate=function(a){this.a.setDate(a)};
-h.add=function(a){if(a.n||a.m){var b=this.getMonth()+a.m+12*a.n,c=this.getYear()+Math.floor(b/12),b=b%12;0>b&&(b+=12);var d;n:{switch(b){case 1:d=0!=c%4||0==c%100&&0!=c%400?28:29;break n;case 5:case 8:case 10:case 3:d=30;break n}d=31}d=Math.min(d,this.getDate());this.setDate(1);this.setFullYear(c);this.setMonth(b);this.setDate(d)}a.l&&(b=new Date(this.getYear(),this.getMonth(),this.getDate(),12),a=new Date(b.getTime()+864E5*a.l),this.setDate(1),this.setFullYear(a.getFullYear()),this.setMonth(a.getMonth()),
-this.setDate(a.getDate()),Z(this,a.getDate()))};h.p=function(){return[this.getFullYear(),x(this.getMonth()+1),x(this.getDate())].join("")+""};h.toString=function(){return this.p()};var Z=function(a,b){if(a.getDate()!=b){var c=a.getDate()<b?1:-1;a.a.setUTCHours(a.a.getUTCHours()+c)}};$.prototype.valueOf=function(){return this.a.valueOf()};new $(0,0,1);new $(9999,11,31);J||L&&Q("525");t("ae.init",function(){Ia();Ja();Da(window,"load",function(){});Ka()});
-var Ia=function(){var a;a=document;if(a=q("ae-content")?a.getElementById("ae-content"):"ae-content"){a=R("table","ae-table-striped",a);for(var b=0,c;c=a[b];b++){c=R("tbody",null,c);for(var d=0,e;e=c[d];d++){e=R("tr",null,e);for(var f=0,g;g=e[f];f++)f%2&&pa(g,"ae-even")}}}},Ja=function(){var a=R(null,"ae-noscript",void 0);ca(C(a),function(a){ra(a,"ae-noscript")})},Ka=function(){m._gaq=m._gaq||[];m._gaq.push(function(){m._gaq._createAsyncTracker("UA-3739047-3","ae")._trackPageview()});(function(){var a=
-document.createElement("script");a.src=("https:"==document.location.protocol?"https://ssl":"http://www")+".google-analytics.com/ga.js";a.setAttribute("async","true");document.documentElement.firstChild.appendChild(a)})()};t("ae.trackPageView",function(){m._gaq&&m._gaq._getAsyncTracker("ae")._trackPageview()});var Ma=function(a){if(void 0==a||null==a||0==a.length)return 0;a=Math.max.apply(Math,a);return La(a)},La=function(a){var b=5;2>b&&(b=2);b-=1;return Math.ceil(a/b)*b},Na=function(a,b,c){a=a.getSelection();1==a.length&&(a=a[0],null!=a.row&&(null!=b.starttime&&(c+="&starttime="+b.starttime),null!=b.endtime&&(c+="&endtime="+b.endtime),null!=b.latency_lower&&(c+="&latency_lower="+b.latency_lower),null!=b.latency_upper&&(c+="&latency_upper="+b.latency_upper),b=c+"&detail="+a.row,window.location.href=b))},
-Oa=function(a,b,c,d,e){var f=new google.visualization.DataTable;f.addColumn("string","");f.addColumn("number","");f.addColumn({type:"string",role:"tooltip"});for(var g=0;g<b.length;g++)f.addRow(["",b[g],c[g]]);c=Math.max(10*b.length,200);b=Ma(b);a=new google.visualization.ColumnChart(document.getElementById("rpctime-"+a));a.draw(f,{height:100,width:c,legend:"none",chartArea:{left:40},fontSize:11,vAxis:{minValue:0,maxValue:b,gridlines:{count:5}}});google.visualization.events.addListener(a,"select",
-r(Na,a,d,e))};t("ae.Charts.latencyHistogram",function(a,b,c){var d=new google.visualization.DataTable;d.addColumn("string","");d.addColumn("number","");for(var e=0;e<b.length;e++)d.addRow([""+a[e],b[e]]);for(e=b.length;e<a.length;e++)d.addRow([""+a[e],0]);b=Ma(b);(new google.visualization.ColumnChart(document.getElementById("latency-"+c))).draw(d,{legend:"none",width:20*a.length,height:200,vAxis:{maxValue:b,gridlines:{count:5}}})});
-t("ae.Charts.latencyTimestampScatter",function(a,b,c,d,e){var f=new google.visualization.DataTable;f.addColumn("number","Time (seconds from start)");f.addColumn("number","Latency");for(var g=0;g<a.length;g++){var k=Math.round(a[g]-c);f.addRow([k,b[g]])}a=d.starttime?d.starttime:0;b=new google.visualization.ScatterChart(document.getElementById("LatencyVsTimestamp"));b.draw(f,{hAxis:{title:"Time (seconds from start of recording)",minValue:a},vAxis:{title:"Request Latency (milliseconds)",minValue:0},
-tooltip:{trigger:"none"},legend:"none"});google.visualization.events.addListener(b,"select",r(Na,b,d,e))});
-t("ae.Charts.entityCountBarChart",function(a,b,c,d){var e=new google.visualization.DataTable;e.addColumn("string","");e.addColumn("number","Reads");e.addColumn({type:"string",role:"tooltip"});e.addColumn("number","Misses");e.addColumn({type:"string",role:"tooltip"});e.addColumn("number","Writes");e.addColumn({type:"string",role:"tooltip"});var f=50;f>b.length&&(f=b.length);for(var g=0;g<f;g++)e.addRow(["",b[g][1]-b[g][3],b[g][0],b[g][3],b[g][0],b[g][2],b[g][0]]);b=20*f;f=b+130;a=new google.visualization.ColumnChart(document.getElementById(d+
-"-"+a));c=La(c);a.draw(e,{height:100,width:f,chartArea:{width:b},fontSize:10,isStacked:!0,vAxis:{minValue:0,maxValue:c,gridlines:{count:5}}})});
-t("ae.Charts.rpcVariationCandlestick",function(a){var b=new google.visualization.DataTable;b.addColumn("string","");b.addColumn("number","");b.addColumn("number","");b.addColumn("number","");b.addColumn("number","");b.addRows(a);(new google.visualization.CandlestickChart(document.getElementById("rpcvariation"))).draw(b,{vAxis:{title:"RPC Latency variation (milliseconds)"},hAxis:{textPosition:"out",slantedText:!0,slantedTextAngle:45,textStyle:{fontSize:13}},height:250,chartArea:{top:10,height:100},
-legend:"none",tooltip:{trigger:"none"}})});t("ae.Charts.totalTimeBarChart",function(a,b,c,d){for(var e=[],f=0;f<b.length;f++)e[f]=b[f]+" milliseconds";Oa(a,b,e,c,d)});t("ae.Charts.rpcTimeBarChart",function(a,b,c,d,e){var f=[],g=[],k=c.indices,w=c.times;c=c.stats;for(var p=0;p<b;p++)f[p]=0,g[p]=null;for(p=0;p<k.length;p++){f[k[p]]=w[p];b=c[p];var l="Calls: "+b[0];if(0<b[1]||0<b[2]||0<b[3])l+=" Entities";0<b[1]&&(l+=" R:"+b[1]);0<b[2]&&(l+=" W:"+b[2]);0<b[3]&&(l+=" M:"+b[3]);g[k[p]]=l}Oa(a,f,g,d,e)});})();
+/* Copyright 2008-9 Google Inc. All Rights Reserved. */ (function(){var k,m=this,n=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if("function"==b&&"undefined"==typeof a.call)return"object";return b},q=function(a){return"string"==typeof a},r=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},aa=Date.now||function(){return+new Date},s=function(a,b){var c=a.split("."),e=m;c[0]in e||!e.execScript||e.execScript("var "+c[0]);for(var d;c.length&&(d=c.shift());)c.length||void 0===b?e=e[d]?e[d]:e[d]={}:e[d]=b},t=function(a,b){function c(){}
+c.prototype=b.prototype;a.n=b.prototype;a.prototype=new c;a.q=function(a,c,f){var g=Array.prototype.slice.call(arguments,2);return b.prototype[c].apply(a,g)}};var u=function(a){Error.captureStackTrace?Error.captureStackTrace(this,u):this.stack=Error().stack||"";a&&(this.message=String(a))};t(u,Error);var ba=function(a,b){for(var c=a.split("%s"),e="",d=Array.prototype.slice.call(arguments,1);d.length&&1<c.length;)e+=c.shift()+d.shift();return e+c.join("%s")},w=function(a){a=String(a);var b=a.indexOf(".");-1==b&&(b=a.length);b=Math.max(0,2-b);return Array(b+1).join("0")+a},x=function(a,b){return a<b?-1:a>b?1:0};var y=function(a,b){b.unshift(a);u.call(this,ba.apply(null,b));b.shift()};t(y,u);var z=function(a,b,c){if(!a){var e=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b)var d=d+(": "+b),f=e;throw new y(""+d,f||[]);}};var A=Array.prototype,B=A.indexOf?function(a,b,c){z(null!=a.length);return A.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(q(a))return q(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},ca=A.forEach?function(a,b,c){z(null!=a.length);A.forEach.call(a,b,c)}:function(a,b,c){for(var e=a.length,d=q(a)?a.split(""):a,f=0;f<e;f++)f in d&&b.call(c,d[f],f,a)},da=A.filter?function(a,b,c){z(null!=a.length);return A.filter.call(a,b,
+c)}:function(a,b,c){for(var e=a.length,d=[],f=0,g=q(a)?a.split(""):a,h=0;h<e;h++)if(h in g){var v=g[h];b.call(c,v,h,a)&&(d[f++]=v)}return d},D=function(a){var b=a.length;if(0<b){for(var c=Array(b),e=0;e<b;e++)c[e]=a[e];return c}return[]},ea=function(a,b,c){z(null!=a.length);return 2>=arguments.length?A.slice.call(a,b):A.slice.call(a,b,c)};var E,F,G,H,fa=function(){return m.navigator?m.navigator.userAgent:null};H=G=F=E=!1;var I;if(I=fa()){var ga=m.navigator;E=0==I.lastIndexOf("Opera",0);F=!E&&(-1!=I.indexOf("MSIE")||-1!=I.indexOf("Trident"));G=!E&&-1!=I.indexOf("WebKit");H=!E&&!G&&!F&&"Gecko"==ga.product}var ha=E,J=F,K=H,L=G,ia=function(){var a=m.document;return a?a.documentMode:void 0},M;
+t:{var N="",O;if(ha&&m.opera)var P=m.opera.version,N="function"==typeof P?P():P;else if(K?O=/rv\:([^\);]+)(\)|;)/:J?O=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:L&&(O=/WebKit\/(\S+)/),O)var ja=O.exec(fa()),N=ja?ja[1]:"";if(J){var ka=ia();if(ka>parseFloat(N)){M=String(ka);break t}}M=N}
+var la=M,ma={},Q=function(a){var b;if(!(b=ma[a])){b=0;for(var c=String(la).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),e=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),d=Math.max(c.length,e.length),f=0;0==b&&f<d;f++){var g=c[f]||"",h=e[f]||"",v=RegExp("(\\d*)(\\D*)","g"),p=RegExp("(\\d*)(\\D*)","g");do{var l=v.exec(g)||["","",""],C=p.exec(h)||["","",""];if(0==l[0].length&&0==C[0].length)break;b=x(0==l[1].length?0:parseInt(l[1],10),0==C[1].length?0:parseInt(C[1],10))||x(0==l[2].length,
+0==C[2].length)||x(l[2],C[2])}while(0==b)}b=ma[a]=0<=b}return b},na=m.document,oa=na&&J?ia()||("CSS1Compat"==na.compatMode?parseInt(la,10):5):void 0;!K&&!J||J&&J&&9<=oa||K&&Q("1.9.1");J&&Q("9");var pa=function(a){a=a.className;return q(a)&&a.match(/\S+/g)||[]},qa=function(a,b){for(var c=pa(a),e=ea(arguments,1),d=c,f=0;f<e.length;f++)0<=B(d,e[f])||d.push(e[f]);c=c.join(" ");a.className=c},sa=function(a,b){var c=pa(a),e=ea(arguments,1),c=ra(c,e).join(" ");a.className=c},ra=function(a,b){return da(a,function(a){return!(0<=B(b,a))})};var R=function(a,b,c){var e=document;c=c||e;var d=a&&"*"!=a?a.toUpperCase():"";if(c.querySelectorAll&&c.querySelector&&(d||b))return c.querySelectorAll(d+(b?"."+b:""));if(b&&c.getElementsByClassName){a=c.getElementsByClassName(b);if(d){c={};for(var f=e=0,g;g=a[f];f++)d==g.nodeName&&(c[e++]=g);c.length=e;return c}return a}a=c.getElementsByTagName(d||"*");if(b){c={};for(f=e=0;g=a[f];f++){var d=g.className,h;if(h="function"==typeof d.split)h=0<=B(d.split(/\s+/),b);h&&(c[e++]=g)}c.length=e;return c}return a};var S=function(a){S[" "](a);return a};S[" "]=function(){};var ta=!J||J&&9<=oa,ua=J&&!Q("9");!L||Q("528");K&&Q("1.9b")||J&&Q("8")||ha&&Q("9.5")||L&&Q("528");K&&!Q("8")||J&&Q("9");var T=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.i=!1};T.prototype.preventDefault=function(){this.defaultPrevented=!0};var U=function(a,b){T.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.j=this.state=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var e=a.relatedTarget;if(e){if(K){var d;t:{try{S(e.nodeName);d=!0;break t}catch(f){}d=!1}d||(e=null)}}else"mouseover"==
+c?e=a.fromElement:"mouseout"==c&&(e=a.toElement);this.relatedTarget=e;this.offsetX=L||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=L||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=
+a.metaKey;this.state=a.state;this.j=a;a.defaultPrevented&&this.preventDefault()}};t(U,T);U.prototype.preventDefault=function(){U.n.preventDefault.call(this);var a=this.j;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,ua)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var va="closure_listenable_"+(1E6*Math.random()|0),wa=function(a){try{return!(!a||!a[va])}catch(b){return!1}},xa=0;var ya=function(a,b,c,e,d){this.c=a;this.e=null;this.src=b;this.type=c;this.capture=!!e;this.f=d;this.key=++xa;this.d=this.g=!1},za=function(a){a.d=!0;a.c=null;a.e=null;a.src=null;a.f=null};var V=function(a){this.src=a;this.b={};this.h=0};V.prototype.add=function(a,b,c,e,d){var f=this.b[a];f||(f=this.b[a]=[],this.h++);var g;t:{for(g=0;g<f.length;++g){var h=f[g];if(!h.d&&h.c==b&&h.capture==!!e&&h.f==d)break t}g=-1}-1<g?(a=f[g],c||(a.g=!1)):(a=new ya(b,this.src,a,!!e,d),a.g=c,f.push(a));return a};var Aa=function(a,b){var c=b.type;if(c in a.b){var e=a.b[c],d=B(e,b),f;if(f=0<=d)z(null!=e.length),A.splice.call(e,d,1);f&&(za(b),0==a.b[c].length&&(delete a.b[c],a.h--))}};var W="closure_lm_"+(1E6*Math.random()|0),X={},Ba=0,Da=function(){var a=Ca,b=ta?function(c){return a.call(b.src,b.c,c)}:function(c){c=a.call(b.src,b.c,c);if(!c)return c};return b},Ea=function(a,b,c,e,d){if("array"==n(b))for(var f=0;f<b.length;f++)Ea(a,b[f],c,e,d);else if(c=Fa(c),wa(a))a.k.add(String(b),c,!0,e,d);else{if(!b)throw Error("Invalid event type");var f=!!e,g=Y(a);g||(a[W]=g=new V(a));c=g.add(b,c,!0,e,d);c.e||(e=Da(),c.e=e,e.src=a,e.c=c,a.addEventListener?a.addEventListener(b,e,f):a.attachEvent(b in
+X?X[b]:X[b]="on"+b,e),Ba++)}},Ha=function(a,b,c,e){var d=1;if(a=Y(a))if(b=a.b[b])for(b=D(b),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.d&&(d&=!1!==Ga(f,e))}return Boolean(d)},Ga=function(a,b){var c=a.c,e=a.f||a.src;if(a.g&&"number"!=typeof a&&a&&!a.d){var d=a.src;if(wa(d))Aa(d.k,a);else{var f=a.type,g=a.e;d.removeEventListener?d.removeEventListener(f,g,a.capture):d.detachEvent&&d.detachEvent(f in X?X[f]:X[f]="on"+f,g);Ba--;(f=Y(d))?(Aa(f,a),0==f.h&&(f.src=null,d[W]=null)):za(a)}}return c.call(e,
+b)},Ca=function(a,b){if(a.d)return!0;if(!ta){var c;if(!(c=b))t:{c=["window","event"];for(var e=m,d;d=c.shift();)if(null!=e[d])e=e[d];else{c=null;break t}c=e}d=c;c=new U(d,this);e=!0;if(!(0>d.keyCode||void 0!=d.returnValue)){t:{var f=!1;if(0==d.keyCode)try{d.keyCode=-1;break t}catch(g){f=!0}if(f||void 0==d.returnValue)d.returnValue=!0}d=[];for(f=c.currentTarget;f;f=f.parentNode)d.push(f);for(var f=a.type,h=d.length-1;!c.i&&0<=h;h--)c.currentTarget=d[h],e&=Ha(d[h],f,!0,c);for(h=0;!c.i&&h<d.length;h++)c.currentTarget=
+d[h],e&=Ha(d[h],f,!1,c)}return e}return Ga(a,new U(b,this))},Y=function(a){a=a[W];return a instanceof V?a:null},Ia="__closure_events_fn_"+(1E9*Math.random()>>>0),Fa=function(a){z(a,"Listener can not be null.");if("function"==n(a))return a;z(a.handleEvent,"An object listener must have handleEvent method.");return a[Ia]||(a[Ia]=function(b){return a.handleEvent(b)})};var $=function(a,b,c){"number"==typeof a?(this.a=new Date(a,b||0,c||1),Z(this,c||1)):(b=typeof a,"object"==b&&null!=a||"function"==b?(this.a=new Date(a.getFullYear(),a.getMonth(),a.getDate()),Z(this,a.getDate())):(this.a=new Date(aa()),this.a.setHours(0),this.a.setMinutes(0),this.a.setSeconds(0),this.a.setMilliseconds(0)))};k=$.prototype;k.getFullYear=function(){return this.a.getFullYear()};k.getYear=function(){return this.getFullYear()};k.getMonth=function(){return this.a.getMonth()};k.getDate=function(){return this.a.getDate()};
+k.getTime=function(){return this.a.getTime()};k.getUTCHours=function(){return this.a.getUTCHours()};k.setFullYear=function(a){this.a.setFullYear(a)};k.setMonth=function(a){this.a.setMonth(a)};k.setDate=function(a){this.a.setDate(a)};
+k.add=function(a){if(a.o||a.m){var b=this.getMonth()+a.m+12*a.o,c=this.getYear()+Math.floor(b/12),b=b%12;0>b&&(b+=12);var e;t:{switch(b){case 1:e=0!=c%4||0==c%100&&0!=c%400?28:29;break t;case 5:case 8:case 10:case 3:e=30;break t}e=31}e=Math.min(e,this.getDate());this.setDate(1);this.setFullYear(c);this.setMonth(b);this.setDate(e)}a.l&&(b=new Date(this.getYear(),this.getMonth(),this.getDate(),12),a=new Date(b.getTime()+864E5*a.l),this.setDate(1),this.setFullYear(a.getFullYear()),this.setMonth(a.getMonth()),
+this.setDate(a.getDate()),Z(this,a.getDate()))};k.p=function(){return[this.getFullYear(),w(this.getMonth()+1),w(this.getDate())].join("")+""};k.toString=function(){return this.p()};var Z=function(a,b){if(a.getDate()!=b){var c=a.getDate()<b?1:-1;a.a.setUTCHours(a.a.getUTCHours()+c)}};$.prototype.valueOf=function(){return this.a.valueOf()};new $(0,0,1);new $(9999,11,31);J||L&&Q("525");s("ae.init",function(){Ja();Ka();Ea(window,"load",function(){});La()});
+var Ja=function(){var a;a=document;if(a=q("ae-content")?a.getElementById("ae-content"):"ae-content"){a=R("table","ae-table-striped",a);for(var b=0,c;c=a[b];b++){c=R("tbody",null,c);for(var e=0,d;d=c[e];e++){d=R("tr",null,d);for(var f=0,g;g=d[f];f++)f%2&&qa(g,"ae-even")}}}},Ka=function(){var a=R(null,"ae-noscript",void 0);ca(D(a),function(a){sa(a,"ae-noscript")})},La=function(){m._gaq=m._gaq||[];m._gaq.push(function(){m._gaq._createAsyncTracker("UA-3739047-3","ae")._trackPageview()});(function(){var a=
+document.createElement("script");a.src=("https:"==document.location.protocol?"https://ssl":"http://www")+".google-analytics.com/ga.js";a.setAttribute("async","true");document.documentElement.firstChild.appendChild(a)})()};s("ae.trackPageView",function(){m._gaq&&m._gaq._getAsyncTracker("ae")._trackPageview()});var Na=function(a){if(void 0==a||null==a||0==a.length)return 0;a=Math.max.apply(Math,a);return Ma(a)},Ma=function(a){var b=5;2>b&&(b=2);b-=1;return Math.ceil(a/b)*b},Oa=function(a,b,c){a=a.getSelection();1==a.length&&(a=a[0],null!=a.row&&(null!=b.starttime&&(c+="&starttime="+b.starttime),null!=b.endtime&&(c+="&endtime="+b.endtime),null!=b.latency_lower&&(c+="&latency_lower="+b.latency_lower),null!=b.latency_upper&&(c+="&latency_upper="+b.latency_upper),b=c+"&detail="+a.row,window.location.href=b))},
+Pa=function(a,b,c,e,d){var f=new google.visualization.DataTable;f.addColumn("string","");f.addColumn("number","");f.addColumn({type:"string",role:"tooltip"});for(var g=0;g<b.length;g++)f.addRow(["",b[g],c[g]]);c=Math.max(10*b.length,200);b=Na(b);a=new google.visualization.ColumnChart(document.getElementById("rpctime-"+a));a.draw(f,{height:100,width:c,legend:"none",chartArea:{left:40},fontSize:11,vAxis:{minValue:0,maxValue:b,gridlines:{count:5}}});google.visualization.events.addListener(a,"select",
+r(Oa,a,e,d))};s("ae.Charts.latencyHistogram",function(a,b,c){var e=new google.visualization.DataTable;e.addColumn("string","");e.addColumn("number","");for(var d=0;d<b.length;d++)e.addRow([""+a[d],b[d]]);for(d=b.length;d<a.length;d++)e.addRow([""+a[d],0]);b=Na(b);(new google.visualization.ColumnChart(document.getElementById("latency-"+c))).draw(e,{legend:"none",width:20*a.length,height:200,vAxis:{maxValue:b,gridlines:{count:5}}})});
+s("ae.Charts.latencyTimestampScatter",function(a,b,c,e,d){var f=new google.visualization.DataTable;f.addColumn("number","Time (seconds from start)");f.addColumn("number","Latency");for(var g=0;g<a.length;g++){var h=Math.round(a[g]-c);f.addRow([h,b[g]])}a=e.starttime?e.starttime:0;b=new google.visualization.ScatterChart(document.getElementById("LatencyVsTimestamp"));b.draw(f,{hAxis:{title:"Time (seconds from start of recording)",minValue:a},vAxis:{title:"Request Latency (milliseconds)",minValue:0},
+tooltip:{trigger:"none"},legend:"none"});google.visualization.events.addListener(b,"select",r(Oa,b,e,d))});
+s("ae.Charts.entityCountBarChart",function(a,b,c,e){var d=new google.visualization.DataTable;d.addColumn("string","");d.addColumn("number","Reads");d.addColumn({type:"string",role:"tooltip"});d.addColumn("number","Misses");d.addColumn({type:"string",role:"tooltip"});d.addColumn("number","Writes");d.addColumn({type:"string",role:"tooltip"});var f=50;f>b.length&&(f=b.length);for(var g=0;g<f;g++)d.addRow(["",b[g][1]-b[g][3],b[g][0],b[g][3],b[g][0],b[g][2],b[g][0]]);b=20*f;f=b+130;a=new google.visualization.ColumnChart(document.getElementById(e+
+"-"+a));c=Ma(c);a.draw(d,{height:100,width:f,chartArea:{width:b},fontSize:10,isStacked:!0,vAxis:{minValue:0,maxValue:c,gridlines:{count:5}}})});
+s("ae.Charts.rpcVariationCandlestick",function(a){var b=new google.visualization.DataTable;b.addColumn("string","");b.addColumn("number","");b.addColumn("number","");b.addColumn("number","");b.addColumn("number","");b.addRows(a);(new google.visualization.CandlestickChart(document.getElementById("rpcvariation"))).draw(b,{vAxis:{title:"RPC Latency variation (milliseconds)"},hAxis:{textPosition:"out",slantedText:!0,slantedTextAngle:45,textStyle:{fontSize:13}},height:250,chartArea:{top:10,height:100},
+legend:"none",tooltip:{trigger:"none"}})});s("ae.Charts.totalTimeBarChart",function(a,b,c,e){for(var d=[],f=0;f<b.length;f++)d[f]=b[f]+" milliseconds";Pa(a,b,d,c,e)});s("ae.Charts.rpcTimeBarChart",function(a,b,c,e,d){var f=[],g=[],h=c.indices,v=c.times;c=c.stats;for(var p=0;p<b;p++)f[p]=0,g[p]=null;for(p=0;p<h.length;p++){f[h[p]]=v[p];b=c[p];var l="Calls: "+b[0];if(0<b[1]||0<b[2]||0<b[3])l+=" Entities";0<b[1]&&(l+=" R:"+b[1]);0<b[2]&&(l+=" W:"+b[2]);0<b[3]&&(l+=" M:"+b[3]);g[h[p]]=l}Pa(a,f,g,e,d)});})();
diff --git a/google/appengine/ext/appstats/static/appstats_css.css b/google/appengine/ext/appstats/static/appstats_css.css
index 0c8e821..7c4525e 100644
--- a/google/appengine/ext/appstats/static/appstats_css.css
+++ b/google/appengine/ext/appstats/static/appstats_css.css
@@ -1,2 +1,2 @@
-/* Copyright 2013 Google Inc. All Rights Reserved. */
+/* Copyright 2014 Google Inc. All Rights Reserved. */
 html,body,div,h1,h2,h3,h4,h5,h6,p,img,dl,dt,dd,ol,ul,li,table,caption,tbody,tfoot,thead,tr,th,td,form,fieldset,embed,object,applet{margin:0;padding:0;border:0;}body{font-size:62.5%;font-family:Arial,sans-serif;color:#000;background:#fff}a{color:#00c}a:active{color:#f00}a:visited{color:#551a8b}table{border-collapse:collapse;border-width:0;empty-cells:show}ul{padding:0 0 1em 1em}ol{padding:0 0 1em 1.3em}li{line-height:1.5em;padding:0 0 .5em 0}p{padding:0 0 1em 0}h1,h2,h3,h4,h5{padding:0 0 1em 0}h1,h2{font-size:1.3em}h3{font-size:1.1em}h4,h5,table{font-size:1em}sup,sub{font-size:.7em}input,select,textarea,option{font-family:inherit;font-size:inherit}.g-doc,.g-doc-1024,.g-doc-800{font-size:130%}.g-doc{width:100%;text-align:left}.g-section{width:100%;vertical-align:top;display:inline-block}*:first-child+html .g-section{display:block}* html .g-section{overflow:hidden}@-moz-document url-prefix(''){.g-section{overflow:hidden}}@-moz-document url-prefix(''){.g-section,tt:default{overflow:visible}}.g-section,.g-unit{zoom:1}.g-split .g-unit{text-align:right}.g-split .g-first{text-align:left}.g-doc-1024{width:73.074em;min-width:950px;margin:0 auto;text-align:left}* html .g-doc-1024{width:71.313em}*+html .g-doc-1024{width:71.313em}.g-doc-800{width:57.69em;min-width:750px;margin:0 auto;text-align:left}* html .g-doc-800{width:56.3em}*+html .g-doc-800{width:56.3em}.g-tpl-160 .g-unit,.g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-unit .g-tpl-160 .g-unit{margin:0 0 0 160px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-tpl-160 .g-first,.g-tpl-160 .g-first{margin:0;width:160px;float:left}.g-tpl-160-alt .g-unit,.g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-160-alt .g-unit{margin:0 160px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-tpl-160-alt .g-first,.g-tpl-160-alt .g-first{margin:0;width:160px;float:right}.g-tpl-180 .g-unit,.g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-unit .g-tpl-180 .g-unit{margin:0 0 0 180px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-tpl-180 .g-first,.g-tpl-180 .g-first{margin:0;width:180px;float:left}.g-tpl-180-alt .g-unit,.g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-180-alt .g-unit{margin:0 180px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-tpl-180-alt .g-first,.g-tpl-180-alt .g-first{margin:0;width:180px;float:right}.g-tpl-300 .g-unit,.g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-unit .g-tpl-300 .g-unit{margin:0 0 0 300px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-tpl-300 .g-first,.g-tpl-300 .g-first{margin:0;width:300px;float:left}.g-tpl-300-alt .g-unit,.g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-300-alt .g-unit{margin:0 300px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-tpl-300-alt .g-first,.g-tpl-300-alt .g-first{margin:0;width:300px;float:right}.g-tpl-25-75 .g-unit,.g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75 .g-unit{width:74.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-tpl-25-75 .g-first,.g-tpl-25-75 .g-first{width:24.999%;float:left;margin:0}.g-tpl-25-75-alt .g-unit,.g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-unit{width:24.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-tpl-25-75-alt .g-first,.g-tpl-25-75-alt .g-first{width:74.999%;float:right;margin:0}.g-tpl-75-25 .g-unit,.g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25 .g-unit{width:24.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-tpl-75-25 .g-first,.g-tpl-75-25 .g-first{width:74.999%;float:left;margin:0}.g-tpl-75-25-alt .g-unit,.g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-unit{width:74.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-tpl-75-25-alt .g-first,.g-tpl-75-25-alt .g-first{width:24.999%;float:right;margin:0}.g-tpl-33-67 .g-unit,.g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67 .g-unit{width:66.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-tpl-33-67 .g-first,.g-tpl-33-67 .g-first{width:32.999%;float:left;margin:0}.g-tpl-33-67-alt .g-unit,.g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-unit{width:32.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-tpl-33-67-alt .g-first,.g-tpl-33-67-alt .g-first{width:66.999%;float:right;margin:0}.g-tpl-67-33 .g-unit,.g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33 .g-unit{width:32.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-tpl-67-33 .g-first,.g-tpl-67-33 .g-first{width:66.999%;float:left;margin:0}.g-tpl-67-33-alt .g-unit,.g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-unit{width:66.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-tpl-67-33-alt .g-first,.g-tpl-67-33-alt .g-first{width:32.999%;float:right;margin:0}.g-tpl-50-50 .g-unit,.g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50 .g-unit{width:49.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-tpl-50-50 .g-first,.g-tpl-50-50 .g-first{width:49.999%;float:left;margin:0}.g-tpl-50-50-alt .g-unit,.g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-unit{width:49.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-tpl-50-50-alt .g-first,.g-tpl-50-50-alt .g-first{width:49.999%;float:right;margin:0}.g-tpl-nest{width:auto}.g-tpl-nest .g-section{display:inline}.g-tpl-nest .g-unit,.g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest .g-unit{float:left;width:auto;margin:0}.g-tpl-nest-alt .g-unit,.g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest-alt .g-unit{float:right;width:auto;margin:0}html>body .goog-inline-block{display:-moz-inline-box;display:inline-block;}.goog-inline-block{position:relative;display:inline-block}* html .goog-inline-block{display:inline}*:first-child+html .goog-inline-block{display:inline}.goog-tab{position:relative;border:1px solid #8ac;padding:4px 9px;color:#000;background:#e5ecf9;border-top-left-radius:2px;border-top-right-radius:2px;-moz-border-radius-topleft:2px;-webkit-border-top-left-radius:2px;-moz-border-radius-topright:2px;-webkit-border-top-right-radius:2px}.goog-tab-bar-top .goog-tab{margin:1px 4px 0 0;border-bottom:0;float:left}.goog-tab-bar-bottom .goog-tab{margin:0 4px 1px 0;border-top:0;float:left}.goog-tab-bar-start .goog-tab{margin:0 0 4px 1px;border-right:0}.goog-tab-bar-end .goog-tab{margin:0 1px 4px 0;border-left:0}.goog-tab-hover{text-decoration:underline;cursor:pointer}.goog-tab-disabled{color:#fff;background:#ccc;border-color:#ccc}.goog-tab-selected{background:#fff!important;color:black;font-weight:bold}.goog-tab-bar-top .goog-tab-selected{top:1px;margin-top:0;padding-bottom:5px}.goog-tab-bar-bottom .goog-tab-selected{top:-1px;margin-bottom:0;padding-top:5px}.goog-tab-bar-start .goog-tab-selected{left:1px;margin-left:0;padding-right:9px}.goog-tab-bar-end .goog-tab-selected{left:-1px;margin-right:0;padding-left:9px}.goog-tab-content{padding:.1em .8em .8em .8em;border:1px solid #8ac;border-top:none}.goog-tab-bar{position:relative;margin:0 0 0 5px;border:0;padding:0;list-style:none;cursor:default;outline:none}.goog-tab-bar-clear{border-top:1px solid #8ac;clear:both;height:0;overflow:hidden}.goog-tab-bar-start{float:left}.goog-tab-bar-end{float:right}* html .goog-tab-bar-start{margin-right:-3px}* html .goog-tab-bar-end{margin-left:-3px}.ae-table-plain{border-collapse:collapse;width:100%}.ae-table{border:1px solid #c5d7ef;border-collapse:collapse;width:100%}#bd h2.ae-table-title{background:#e5ecf9;margin:0;color:#000;font-size:1em;padding:3px 0 3px 5px;border-left:1px solid #c5d7ef;border-right:1px solid #c5d7ef;border-top:1px solid #c5d7ef}.ae-table-caption,.ae-table caption{border:1px solid #c5d7ef;background:#e5ecf9;-moz-margin-start:-1px}.ae-table caption{padding:3px 5px;text-align:left}.ae-table th,.ae-table td{background-color:#fff;padding:.35em 1em .25em .35em;margin:0}.ae-table thead th{font-weight:bold;text-align:left;background:#c5d7ef;vertical-align:bottom}.ae-table thead th .ae-no-bold{font-weight:normal}.ae-table tfoot tr td{border-top:1px solid #c5d7ef;background-color:#e5ecf9}.ae-table td{border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even>td,.ae-even th,.ae-even-top td,.ae-even-tween td,.ae-even-bottom td,ol.ae-even{background-color:#e9e9e9;border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even-top td{border-bottom:0}.ae-even-bottom td{border-top:0}.ae-even-tween td{border:0}.ae-table .ae-tween td{border:0}.ae-table .ae-tween-top td{border-bottom:0}.ae-table .ae-tween-bottom td{border-top:0}#bd .ae-table .cbc{width:1.5em;padding-right:0}.ae-table #ae-live td{background-color:#ffeac0}.ae-table-fixed{table-layout:fixed}.ae-table-fixed td,.ae-table-nowrap{overflow:hidden;white-space:nowrap}.ae-paginate strong{margin:0 .5em}tfoot .ae-paginate{text-align:right}.ae-table-caption .ae-paginate,.ae-table-caption .ae-orderby{padding:2px 5px}.g-doc{width:auto;margin:8px 10px 0 10px}#ae-logo{margin-bottom:0}#ae-appbar-lrg{margin:0 0 1.25em 0;padding:.2em .6em;background-color:#e5ecf9;border-top:1px solid #6b90da}#ae-appbar-lrg h1{font-size:1em;margin:0;padding:0}#ft p{text-align:center;margin-top:2.5em;padding-top:.5em;border-top:2px solid #c3d9ff}#bd h3{font-weight:bold;font-size:1.4em}#bd p{padding:0 0 1em 0}#ae-content{padding-left:1em;border-left:1px solid #6b90da;min-height:200px}.ae-table .ae-pager{background-color:#c5d7ef}#ae-nav ul{list-style-type:none;margin:0;padding:1em 0}#ae-nav ul li{padding:.1em 0 .1em .5em;margin-bottom:.3em}#ae-nav .ae-nav-selected{color:#44464a;display:block;font-weight:bold;background-color:#e5ecf9;border-bottom:1px solid #cedff2}a.ae-nav-selected{color:#44464a;text-decoration:none}#ae-nav ul li span.ae-nav-disabled{color:#666}#ae-nav ul ul{margin:0;padding:0 0 0 .5em}#ae-nav ul ul li{padding-left:.5em}#ae-nav ul li a,#ae-nav ul li span,#ae-nav ul ul li a{padding-left:.5em}#ae-nav li a:link,#ae-nav li a:visited{color:#00c}#ae-nav li a:link.ae-nav-selected,#ae-nav li a:visited.ae-nav-selected{color:#000;text-decoration:none}.ae-nav-group{padding:.5em;margin:0 .75em 0 0;background-color:#fffbe8;border:1px solid #fff1a9}.ae-nav-group h4{font-weight:bold;padding:auto auto .5em .5em;padding-left:.4em;margin-bottom:.5em;padding-bottom:0}.ae-nav-group ul{margin:0 0 .5em 0;padding:0 0 0 1.3em;list-style-type:none}.ae-nav-group ul li{padding-bottom:.5em}.ae-nav-group li a:link,.ae-nav-group li a:visited{color:#00c}.ae-nav-group li a:hover{color:#00c}#datastore_search{margin-bottom:1em}#hint{background-color:#f6f9ff;border:1px solid #e5ecf9;margin-bottom:1em;padding:0.5em 1em}#message{color:red;position:relative;bottom:6px}#pagetotal{float:right}#pagetotal .count{font-weight:bold}table.entities{border:1px solid #c5d7ef;border-collapse:collapse;width:100%;margin-bottom:0}table.entities th,table.entities td{padding:.25em 1.5em .5em .5em}table.entities th{font-weight:bold;text-align:left;background:#e5ecf9;white-space:nowrap}table.entities th a,table.entities th a:visited{color:black;text-decoration:none}table.entities td{background-color:#fff;text-align:left;vertical-align:top;cursor:pointer}table.entities tr.even td{background-color:#f9f9f9}div.entities{background-color:#c5d7ef;margin-top:0}#entities-pager,#entities-control{padding:.3em 1em .4em 1em}#entities-pager{text-align:right}.ae-page-number{margin:0 0.5em}.ae-page-selected{font-weight:bold}#ae-stats-hd span{font-weight:normal}#ae-rpc-label-col{width:85%}#ae-rpc-stats-col{width:15%}#ae-path-label-col{width:45%}#ae-path-reqs-col{width:10%}#ae-path-rpcs-col{width:10%}#ae-path-stats-col{width:35%}#ae-stats-refresh{margin-bottom:1em}.ae-table-wrapper-left{margin-right:.5em}.ae-table-wrapper-right{margin-left:.5em}#ae-req-history,#ae-rpc-traces{margin-top:1em}.ae-zippy,.ae-zippy-all{position:relative;top:1px;height:12px;width:12px}.goog-zippy-collapsed{background:transparent url('./plus.gif') no-repeat}.goog-zippy-expanded{background:transparent url('./minus.gif') no-repeat}td.ae-hanging-indent{padding-left:20px;text-indent:-20px}.ae-stats-request-link{text-decoration:none}.ae-table td.rpc-req{padding-left:20px;width:20em}#bd div.ae-table-title{background:#e5ecf9;margin:0;color:#000;padding:3px 0 3px 5px;border-left:1px solid #c5d7ef;border-right:1px solid #c5d7ef;border-top:1px solid #c5d7ef}#bd div.ae-table-title h2{font-size:1em;margin:0;padding:0}#bd div.ae-table-title h2.ae-zippy{padding-left:16px;text-decoration:underline;color:#00c}#ae-head-glance span,#ae-rpc-expand-all span,#ae-path-expand-all span,#ae-request-expand-all span{padding-right:.5em}.ae-action{color:#00c;text-decoration:underline;cursor:pointer}.ae-toggle{padding-left:16px;background-position:left center;background-repeat:no-repeat;cursor:pointer}.ae-minus{background-image:url('./minus.gif')}.ae-plus{background-image:url('./plus.gif')}#ae-stats-summary{margin-bottom:1em}#ae-stats-summary dt{float:left;text-align:right;margin-right:1em;font-weight:bold}.ae-stats-date{color:#666}.ae-stats-response-200{color:green}#ae-stats-summary dd{float:left}table.ae-stats-gantt-table{width:95%;border:1px solid #999}div.ae-stats-gantt-container{position:relative;width:100%;height:1em;background-color:#eeeeff}img.ae-stats-gantt-bar{border:0;height:1em;background-color:#7777ff;position:absolute;top:0}img.ae-stats-gantt-extra{border:0;height:0.5em;background-color:#ff6666;position:absolute;top:25%}span.ae-stats-gantt-inline{font-size:80%;position:absolute;top:0.1em;white-space:nowrap;overflow:hidden}a.ae-stats-gantt-link{text-decoration:none}div.ae-stats-gantt-axis{position:relative;width:100%;height:1em}img.ae-stats-gantt-tick{width:1px;height:1em;position:absolute;background-color:gray}span.ae-stats-gantt-scale{position:absolute}
\ No newline at end of file
diff --git a/google/appengine/ext/appstats/static/appstats_js.js b/google/appengine/ext/appstats/static/appstats_js.js
index 0f0d5e9..f79c25b 100644
--- a/google/appengine/ext/appstats/static/appstats_js.js
+++ b/google/appengine/ext/appstats/static/appstats_js.js
@@ -1,82 +1,84 @@
-/* Copyright 2008-10 Google Inc. All Rights Reserved. */ (function(){var f,l=this,aa=function(){},ba=function(a){a.ga=function(){return a.Eb?a.Eb:a.Eb=new a}},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=
-typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},da=function(a){return"array"==ca(a)},ea=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},m=function(a){return"string"==typeof a},n=function(a){return"function"==ca(a)},fa=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ja=function(a){return a[ga]||
-(a[ga]=++ha)},ga="closure_uid_"+(1E9*Math.random()>>>0),ha=0,ka=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},la=function(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},p=function(a,b){function c(){}c.prototype=b.prototype;a.f=b.prototype;a.prototype=new c;a.prototype.constructor=a};var ma=function(a){Error.captureStackTrace?Error.captureStackTrace(this,ma):this.stack=Error().stack||"";a&&(this.message=String(a))};p(ma,Error);ma.prototype.name="CustomError";var na;var oa=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join("%s")},pa=function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},va=function(a){if(!qa.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ra,"&amp;"));-1!=a.indexOf("<")&&(a=a.replace(sa,"&lt;"));-1!=a.indexOf(">")&&(a=a.replace(ta,"&gt;"));-1!=a.indexOf('"')&&(a=a.replace(ua,"&quot;"));return a},ra=/&/g,sa=/</g,ta=/>/g,ua=/\"/g,qa=/[&<>\"]/;var wa=function(a,b){b.unshift(a);ma.call(this,oa.apply(null,b));b.shift()};p(wa,ma);wa.prototype.name="AssertionError";var xa=function(a,b,c){var d="Assertion failed";if(b)var d=d+(": "+b),e=c;else a&&(d+=": "+a,e=null);throw new wa(""+d,e||[]);},r=function(a,b,c){a||xa("",b,Array.prototype.slice.call(arguments,2))},ya=function(a,b,c,d){a instanceof b||xa("instanceof check failed.",c,Array.prototype.slice.call(arguments,3))};var s=Array.prototype,za=s.indexOf?function(a,b,c){r(null!=a.length);return s.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(m(a))return m(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},Aa=s.forEach?function(a,b,c){r(null!=a.length);s.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)g in e&&b.call(c,e[g],g,a)},Ba=s.filter?function(a,b,c){r(null!=a.length);return s.filter.call(a,
-b,c)}:function(a,b,c){for(var d=a.length,e=[],g=0,h=m(a)?a.split(""):a,k=0;k<d;k++)if(k in h){var q=h[k];b.call(c,q,k,a)&&(e[g++]=q)}return e},Ca=s.every?function(a,b,c){r(null!=a.length);return s.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)if(g in e&&!b.call(c,e[g],g,a))return!1;return!0},t=function(a,b){return 0<=za(a,b)},Da=function(a,b){var c=za(a,b),d;if(d=0<=c)r(null!=a.length),s.splice.call(a,c,1);return d},Ea=function(a){var b=a.length;if(0<b){for(var c=
-Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},Ga=function(a,b,c,d){r(null!=a.length);s.splice.apply(a,Fa(arguments,1))},Fa=function(a,b,c){r(null!=a.length);return 2>=arguments.length?s.slice.call(a,b):s.slice.call(a,b,c)};var Ha=function(a,b){for(var c in a)b.call(void 0,a[c],c,a)},Ia=function(a,b){for(var c in a)if(a[c]==b)return!0;return!1},Ja=function(a,b,c){if(b in a)throw Error('The object already contains the key "'+b+'"');a[b]=c},Ka=function(a){var b={},c;for(c in a)b[a[c]]=c;return b},La="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Ma=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var g=0;g<La.length;g++)c=
-La[g],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var u,Na,Oa,Pa,Qa=function(){return l.navigator?l.navigator.userAgent:null};Pa=Oa=Na=u=!1;var Ra;if(Ra=Qa()){var Sa=l.navigator;u=0==Ra.lastIndexOf("Opera",0);Na=!u&&(-1!=Ra.indexOf("MSIE")||-1!=Ra.indexOf("Trident"));Oa=!u&&-1!=Ra.indexOf("WebKit");Pa=!u&&!Oa&&!Na&&"Gecko"==Sa.product}var Ta=u,v=Na,w=Pa,x=Oa,Ua=l.navigator,y=-1!=(Ua&&Ua.platform||"").indexOf("Mac"),Va=function(){var a=l.document;return a?a.documentMode:void 0},Wa;
-t:{var Xa="",Ya;if(Ta&&l.opera)var Za=l.opera.version,Xa="function"==typeof Za?Za():Za;else if(w?Ya=/rv\:([^\);]+)(\)|;)/:v?Ya=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:x&&(Ya=/WebKit\/(\S+)/),Ya)var $a=Ya.exec(Qa()),Xa=$a?$a[1]:"";if(v){var ab=Va();if(ab>parseFloat(Xa)){Wa=String(ab);break t}}Wa=Xa}
-var bb=Wa,cb={},z=function(a){var b;if(!(b=cb[a])){b=0;for(var c=pa(String(bb)).split("."),d=pa(String(a)).split("."),e=Math.max(c.length,d.length),g=0;0==b&&g<e;g++){var h=c[g]||"",k=d[g]||"",q=RegExp("(\\d*)(\\D*)","g"),ia=RegExp("(\\d*)(\\D*)","g");do{var A=q.exec(h)||["","",""],J=ia.exec(k)||["","",""];if(0==A[0].length&&0==J[0].length)break;b=((0==A[1].length?0:parseInt(A[1],10))<(0==J[1].length?0:parseInt(J[1],10))?-1:(0==A[1].length?0:parseInt(A[1],10))>(0==J[1].length?0:parseInt(J[1],10))?
-1:0)||((0==A[2].length)<(0==J[2].length)?-1:(0==A[2].length)>(0==J[2].length)?1:0)||(A[2]<J[2]?-1:A[2]>J[2]?1:0)}while(0==b)}b=cb[a]=0<=b}return b},db=l.document,eb=db&&v?Va()||("CSS1Compat"==db.compatMode?parseInt(bb,10):5):void 0;var fb=!v||v&&9<=eb;!w&&!v||v&&v&&9<=eb||w&&z("1.9.1");var gb=v&&!z("9");var hb=function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},ib=function(a,b){for(var c=hb(a),d=Fa(arguments,1),e=c.length+d.length,g=c,h=0;h<d.length;h++)t(g,d[h])||g.push(d[h]);a.className=c.join(" ");return c.length==e},kb=function(a,b){var c=hb(a),d=Fa(arguments,1),e=jb(c,d);a.className=e.join(" ");return e.length==c.length-d.length},jb=function(a,b){return Ba(a,function(a){return!t(b,a)})};var nb=function(a){return a?new lb(mb(a)):na||(na=new lb)},ob=function(a,b){return m(b)?a.getElementById(b):b},pb=function(a,b,c){var d=document;c=c||d;a=a&&"*"!=a?a.toUpperCase():"";if(c.querySelectorAll&&c.querySelector&&(a||b))return c.querySelectorAll(a+(b?"."+b:""));if(b&&c.getElementsByClassName){c=c.getElementsByClassName(b);if(a){for(var d={},e=0,g=0,h;h=c[g];g++)a==h.nodeName&&(d[e++]=h);d.length=e;return d}return c}c=c.getElementsByTagName(a||"*");if(b){d={};for(g=e=0;h=c[g];g++)a=h.className,
-"function"==typeof a.split&&t(a.split(/\s+/),b)&&(d[e++]=h);d.length=e;return d}return c},rb=function(a,b){Ha(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in qb?a.setAttribute(qb[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})},qb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",
-valign:"vAlign",width:"width"},tb=function(a,b,c){return sb(document,arguments)},sb=function(a,b){var c=b[0],d=b[1];if(!fb&&d&&(d.name||d.type)){c=["<",c];d.name&&c.push(' name="',va(d.name),'"');if(d.type){c.push(' type="',va(d.type),'"');var e={};Ma(e,d);delete e.type;d=e}c.push(">");c=c.join("")}c=a.createElement(c);d&&(m(d)?c.className=d:da(d)?ib.apply(null,[c].concat(d)):rb(c,d));2<b.length&&ub(a,c,b);return c},ub=function(a,b,c){function d(c){c&&b.appendChild(m(c)?a.createTextNode(c):c)}for(var e=
-2;e<c.length;e++){var g=c[e];if(!ea(g)||fa(g)&&0<g.nodeType)d(g);else{var h;t:{if(g&&"number"==typeof g.length){if(fa(g)){h="function"==typeof g.item||"string"==typeof g.item;break t}if(n(g)){h="function"==typeof g.item;break t}}h=!1}Aa(h?Ea(g):g,d)}}},vb=function(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a},mb=function(a){return 9==a.nodeType?
-a:a.ownerDocument||a.document},wb=function(a,b){r(null!=a,"goog.dom.setTextContent expects a non-null value for node");if("textContent"in a)a.textContent=b;else if(3==a.nodeType)a.data=b;else if(a.firstChild&&3==a.firstChild.nodeType){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);a.firstChild.data=b}else{for(var c;c=a.firstChild;)a.removeChild(c);a.appendChild(mb(a).createTextNode(String(b)))}},xb={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},yb={IMG:" ",BR:"\n"},zb=function(a){a=a.getAttributeNode("tabindex");
-return null!=a&&a.specified},Ab=function(a){a=a.tabIndex;return"number"==typeof a&&0<=a&&32768>a},Bb=function(a,b,c){if(!(a.nodeName in xb))if(3==a.nodeType)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in yb)b.push(yb[a.nodeName]);else for(a=a.firstChild;a;)Bb(a,b,c),a=a.nextSibling},lb=function(a){this.F=a||l.document||document};f=lb.prototype;f.kb=nb;f.a=function(a){return ob(this.F,a)};f.r=function(a,b,c){return sb(this.F,arguments)};
-f.createElement=function(a){return this.F.createElement(a)};f.createTextNode=function(a){return this.F.createTextNode(String(a))};f.appendChild=function(a,b){a.appendChild(b)};f.contains=vb;f.J=function(a){var b;(b="A"==a.tagName||"INPUT"==a.tagName||"TEXTAREA"==a.tagName||"SELECT"==a.tagName||"BUTTON"==a.tagName?!a.disabled&&(!zb(a)||Ab(a)):zb(a)&&Ab(a))&&v?(a=n(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth},a=null!=a&&0<a.height&&0<a.width):a=b;return a};var Cb=function(a){Cb[" "](a);return a};Cb[" "]=aa;var Db=!v||v&&9<=eb,Eb=!v||v&&9<=eb,Fb=v&&!z("9");!x||z("528");w&&z("1.9b")||v&&z("8")||Ta&&z("9.5")||x&&z("528");w&&!z("8")||v&&z("9");var Gb=function(){};Gb.prototype.Sb=!1;var B=function(a,b){this.type=a;this.currentTarget=this.target=b};f=B.prototype;f.S=!1;f.defaultPrevented=!1;f.xb=!0;f.stopPropagation=function(){this.S=!0};f.preventDefault=function(){this.defaultPrevented=!0;this.xb=!1};var C=function(a,b){a&&Hb(this,a,b)};p(C,B);var Ib=[1,4,2];f=C.prototype;f.target=null;f.relatedTarget=null;f.offsetX=0;f.offsetY=0;f.clientX=0;f.clientY=0;f.screenX=0;f.screenY=0;f.button=0;f.keyCode=0;f.charCode=0;f.ctrlKey=!1;f.altKey=!1;f.shiftKey=!1;f.metaKey=!1;f.bb=!1;f.Q=null;
-var Hb=function(a,b,c){var d=a.type=b.type;B.call(a,d);a.target=b.target||b.srcElement;a.currentTarget=c;if(c=b.relatedTarget){if(w){var e;t:{try{Cb(c.nodeName);e=!0;break t}catch(g){}e=!1}e||(c=null)}}else"mouseover"==d?c=b.fromElement:"mouseout"==d&&(c=b.toElement);a.relatedTarget=c;a.offsetX=x||void 0!==b.offsetX?b.offsetX:b.layerX;a.offsetY=x||void 0!==b.offsetY?b.offsetY:b.layerY;a.clientX=void 0!==b.clientX?b.clientX:b.pageX;a.clientY=void 0!==b.clientY?b.clientY:b.pageY;a.screenX=b.screenX||
-0;a.screenY=b.screenY||0;a.button=b.button;a.keyCode=b.keyCode||0;a.charCode=b.charCode||("keypress"==d?b.keyCode:0);a.ctrlKey=b.ctrlKey;a.altKey=b.altKey;a.shiftKey=b.shiftKey;a.metaKey=b.metaKey;a.bb=y?b.metaKey:b.ctrlKey;a.state=b.state;a.Q=b;b.defaultPrevented&&a.preventDefault();delete a.S},Jb=function(a){return Db?0==a.Q.button:"click"==a.type?!0:!!(a.Q.button&Ib[0])};
-C.prototype.stopPropagation=function(){C.f.stopPropagation.call(this);this.Q.stopPropagation?this.Q.stopPropagation():this.Q.cancelBubble=!0};C.prototype.preventDefault=function(){C.f.preventDefault.call(this);var a=this.Q;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Fb)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var Kb="closure_listenable_"+(1E6*Math.random()|0),Lb=function(a){try{return!(!a||!a[Kb])}catch(b){return!1}},Mb=0;var Nb=function(a,b,c,d,e){this.$=a;this.Da=null;this.src=b;this.type=c;this.capture=!!d;this.Ia=e;this.key=++Mb;this.ha=this.Ga=!1},Ob=function(a){a.ha=!0;a.$=null;a.Da=null;a.src=null;a.Ia=null};var D=function(a){this.src=a;this.p={};this.va=0};D.prototype.add=function(a,b,c,d,e){var g=this.p[a];g||(g=this.p[a]=[],this.va++);var h=Pb(g,b,d,e);-1<h?(a=g[h],c||(a.Ga=!1)):(a=new Nb(b,this.src,a,!!d,e),a.Ga=c,g.push(a));return a};D.prototype.remove=function(a,b,c,d){if(!(a in this.p))return!1;var e=this.p[a];b=Pb(e,b,c,d);return-1<b?(Ob(e[b]),r(null!=e.length),s.splice.call(e,b,1),0==e.length&&(delete this.p[a],this.va--),!0):!1};
-var Qb=function(a,b){var c=b.type;if(!(c in a.p))return!1;var d=Da(a.p[c],b);d&&(Ob(b),0==a.p[c].length&&(delete a.p[c],a.va--));return d};D.prototype.$a=function(a){var b=0,c;for(c in this.p)if(!a||c==a){for(var d=this.p[c],e=0;e<d.length;e++)++b,Ob(d[e]);delete this.p[c];this.va--}return b};D.prototype.wa=function(a,b,c,d){a=this.p[a];var e=-1;a&&(e=Pb(a,b,c,d));return-1<e?a[e]:null};var Pb=function(a,b,c,d){for(var e=0;e<a.length;++e){var g=a[e];if(!g.ha&&g.$==b&&g.capture==!!c&&g.Ia==d)return e}return-1};var Rb="closure_lm_"+(1E6*Math.random()|0),E={},Sb=0,F=function(a,b,c,d,e){if(da(b)){for(var g=0;g<b.length;g++)F(a,b[g],c,d,e);return null}c=Tb(c);if(Lb(a))a=a.d(b,c,d,e);else{if(!b)throw Error("Invalid event type");var g=!!d,h=Ub(a);h||(a[Rb]=h=new D(a));c=h.add(b,c,!1,d,e);c.Da||(d=Vb(),c.Da=d,d.src=a,d.$=c,a.addEventListener?a.addEventListener(b,d,g):a.attachEvent(b in E?E[b]:E[b]="on"+b,d),Sb++);a=c}return a},Vb=function(){var a=Wb,b=Eb?function(c){return a.call(b.src,b.$,c)}:function(c){c=a.call(b.src,
-b.$,c);if(!c)return c};return b},Xb=function(a,b,c,d,e){if(da(b))for(var g=0;g<b.length;g++)Xb(a,b[g],c,d,e);else c=Tb(c),Lb(a)?a.w(b,c,d,e):a&&(a=Ub(a))&&(b=a.wa(b,c,!!d,e))&&G(b)},G=function(a){if("number"==typeof a||!a||a.ha)return!1;var b=a.src;if(Lb(b))return Qb(b.aa,a);var c=a.type,d=a.Da;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(c in E?E[c]:E[c]="on"+c,d);Sb--;(c=Ub(b))?(Qb(c,a),0==c.va&&(c.src=null,b[Rb]=null)):Ob(a);return!0},Zb=function(a,b,
-c,d){var e=1;if(a=Ub(a))if(b=a.p[b])for(b=Ea(b),a=0;a<b.length;a++){var g=b[a];g&&g.capture==c&&!g.ha&&(e&=!1!==Yb(g,d))}return Boolean(e)},Yb=function(a,b){var c=a.$,d=a.Ia||a.src;a.Ga&&G(a);return c.call(d,b)},Wb=function(a,b){if(a.ha)return!0;if(!Eb){var c;if(!(c=b))t:{c=["window","event"];for(var d=l,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break t}c=d}e=c;c=new C(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){t:{var g=!1;if(0==e.keyCode)try{e.keyCode=-1;break t}catch(h){g=!0}if(g||
-void 0==e.returnValue)e.returnValue=!0}e=[];for(g=c.currentTarget;g;g=g.parentNode)e.push(g);for(var g=a.type,k=e.length-1;!c.S&&0<=k;k--)c.currentTarget=e[k],d&=Zb(e[k],g,!0,c);for(k=0;!c.S&&k<e.length;k++)c.currentTarget=e[k],d&=Zb(e[k],g,!1,c)}return d}return Yb(a,new C(b,this))},Ub=function(a){a=a[Rb];return a instanceof D?a:null},$b="__closure_events_fn_"+(1E9*Math.random()>>>0),Tb=function(a){r(a,"Listener can not be null.");if(n(a))return a;r(a.handleEvent,"An object listener must have handleEvent method.");
-return a[$b]||(a[$b]=function(b){return a.handleEvent(b)})};var H=function(a){this.Db=a;this.La={}};p(H,Gb);var ac=[];H.prototype.d=function(a,b,c,d,e){da(b)||(ac[0]=b,b=ac);for(var g=0;g<b.length;g++){var h=F(a,b[g],c||this.handleEvent,d||!1,e||this.Db||this);if(!h)break;this.La[h.key]=h}return this};H.prototype.w=function(a,b,c,d,e){if(da(b))for(var g=0;g<b.length;g++)this.w(a,b[g],c,d,e);else c=c||this.handleEvent,e=e||this.Db||this,c=Tb(c),d=!!d,b=Lb(a)?a.wa(b,c,d,e):a?(a=Ub(a))?a.wa(b,c,d,e):null:null,b&&(G(b),delete this.La[b.key]);return this};
-H.prototype.$a=function(){Ha(this.La,G);this.La={}};H.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var I=function(){this.aa=new D(this);this.bc=this};p(I,Gb);I.prototype[Kb]=!0;f=I.prototype;f.mb=null;f.Ya=function(a){this.mb=a};f.addEventListener=function(a,b,c,d){F(this,a,b,c,d)};f.removeEventListener=function(a,b,c,d){Xb(this,a,b,c,d)};
-f.dispatchEvent=function(a){bc(this);var b,c=this.mb;if(c){b=[];for(var d=1;c;c=c.mb)b.push(c),r(1E3>++d,"infinite loop")}c=this.bc;d=a.type||a;if(m(a))a=new B(a,c);else if(a instanceof B)a.target=a.target||c;else{var e=a;a=new B(d,c);Ma(a,e)}var e=!0,g;if(b)for(var h=b.length-1;!a.S&&0<=h;h--)g=a.currentTarget=b[h],e=cc(g,d,!0,a)&&e;a.S||(g=a.currentTarget=c,e=cc(g,d,!0,a)&&e,a.S||(e=cc(g,d,!1,a)&&e));if(b)for(h=0;!a.S&&h<b.length;h++)g=a.currentTarget=b[h],e=cc(g,d,!1,a)&&e;return e};
-f.d=function(a,b,c,d){bc(this);return this.aa.add(String(a),b,!1,c,d)};f.w=function(a,b,c,d){return this.aa.remove(String(a),b,c,d)};var cc=function(a,b,c,d){b=a.aa.p[String(b)];if(!b)return!0;b=Ea(b);for(var e=!0,g=0;g<b.length;++g){var h=b[g];if(h&&!h.ha&&h.capture==c){var k=h.$,q=h.Ia||h.src;h.Ga&&Qb(a.aa,h);e=!1!==k.call(q,d)&&e}}return e&&!1!=d.xb};I.prototype.wa=function(a,b,c,d){return this.aa.wa(String(a),b,c,d)};var bc=function(a){r(a.aa,"Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?")};var K=function(a,b){a.style.display=b?"":"none"},dc=w?"MozUserSelect":x?"WebkitUserSelect":null,ec=function(a,b,c){c=c?null:a.getElementsByTagName("*");if(dc){if(b=b?"none":"",a.style[dc]=b,c){a=0;for(var d;d=c[a];a++)d.style[dc]=b}}else if(v||Ta)if(b=b?"on":"",a.setAttribute("unselectable",b),c)for(a=0;d=c[a];a++)d.setAttribute("unselectable",b)};var fc=function(){};ba(fc);fc.prototype.ec=0;var L=function(a){I.call(this);this.n=a||nb();this.sa=gc};p(L,I);L.prototype.dc=fc.ga();var gc=null,hc=function(a,b){switch(a){case 1:return b?"disable":"enable";case 2:return b?"highlight":"unhighlight";case 4:return b?"activate":"deactivate";case 8:return b?"select":"unselect";case 16:return b?"check":"uncheck";case 32:return b?"focus":"blur";case 64:return b?"open":"close"}throw Error("Invalid component state");};f=L.prototype;f.P=null;f.e=!1;f.c=null;f.sa=null;f.o=null;f.s=null;f.j=null;
-var ic=function(a){return a.P||(a.P=":"+(a.dc.ec++).toString(36))},jc=function(a,b){if(a.o&&a.o.j){var c=a.o.j,d=a.P;d in c&&delete c[d];Ja(a.o.j,b,a)}a.P=b};L.prototype.a=function(){return this.c};var kc=function(a){return a.ib||(a.ib=new H(a))},lc=function(a,b){if(a==b)throw Error("Unable to set parent component");if(b&&a.o&&a.P&&a.o.j&&a.P&&a.P in a.o.j&&a.o.j[a.P]&&a.o!=b)throw Error("Unable to set parent component");a.o=b;L.f.Ya.call(a,b)};f=L.prototype;f.getParent=function(){return this.o};
-f.Ya=function(a){if(this.o&&this.o!=a)throw Error("Method not supported");L.f.Ya.call(this,a)};f.kb=function(){return this.n};f.r=function(){this.c=this.n.createElement("div")};f.L=function(a){if(this.e)throw Error("Component already rendered");if(a&&this.Z(a)){var b=mb(a);this.n&&this.n.F==b||(this.n=nb(a));this.Xa(a);this.G()}else throw Error("Invalid element to decorate");};f.Z=function(){return!0};f.Xa=function(a){this.c=a};f.G=function(){this.e=!0;mc(this,function(a){!a.e&&a.a()&&a.G()})};
-f.ca=function(){mc(this,function(a){a.e&&a.ca()});this.ib&&this.ib.$a();this.e=!1};f.Ca=function(a,b){this.Ta(a,nc(this),b)};
-f.Ta=function(a,b,c){if(a.e&&(c||!this.e))throw Error("Component already rendered");if(0>b||b>nc(this))throw Error("Child component index out of bounds");this.j&&this.s||(this.j={},this.s=[]);if(a.getParent()==this){var d=ic(a);this.j[d]=a;Da(this.s,a)}else Ja(this.j,ic(a),a);lc(a,this);Ga(this.s,b,0,a);if(a.e&&this.e&&a.getParent()==this)c=this.C(),c.insertBefore(a.a(),c.childNodes[b]||null);else if(c){this.c||this.r();c=M(this,b+1);b=this.C();c=c?c.c:null;if(a.e)throw Error("Component already rendered");
-a.c||a.r();b?b.insertBefore(a.c,c||null):a.n.F.body.appendChild(a.c);a.o&&!a.o.e||a.G()}else this.e&&!a.e&&a.c&&a.c.parentNode&&1==a.c.parentNode.nodeType&&a.G()};f.C=function(){return this.c};
-var oc=function(a){if(null==a.sa){var b;t:{b=a.e?a.c:a.n.F.body;var c=mb(b);if(c.defaultView&&c.defaultView.getComputedStyle&&(b=c.defaultView.getComputedStyle(b,null))){b=b.direction||b.getPropertyValue("direction")||"";break t}b=""}a.sa="rtl"==(b||((a.e?a.c:a.n.F.body).currentStyle?(a.e?a.c:a.n.F.body).currentStyle.direction:null)||(a.e?a.c:a.n.F.body).style&&(a.e?a.c:a.n.F.body).style.direction)}return a.sa};
-L.prototype.qa=function(a){if(this.e)throw Error("Component already rendered");this.sa=a};var nc=function(a){return a.s?a.s.length:0},M=function(a,b){return a.s?a.s[b]||null:null},mc=function(a,b,c){a.s&&Aa(a.s,b,c)},pc=function(a,b){return a.s&&b?za(a.s,b):-1};
-L.prototype.removeChild=function(a,b){if(a){var c=m(a)?a:ic(a);a=this.j&&c?(c in this.j?this.j[c]:void 0)||null:null;if(c&&a){var d=this.j;c in d&&delete d[c];Da(this.s,a);b&&(a.ca(),a.c&&(c=a.c)&&c.parentNode&&c.parentNode.removeChild(c));lc(a,null)}}if(!a)throw Error("Child is not in parent component");return a};var qc,rc={kc:"activedescendant",pc:"atomic",qc:"autocomplete",sc:"busy",vc:"checked",Ac:"controls",Cc:"describedby",Fc:"disabled",Hc:"dropeffect",Ic:"expanded",Jc:"flowto",Lc:"grabbed",Pc:"haspopup",Rc:"hidden",Tc:"invalid",Uc:"label",Vc:"labelledby",Wc:"level",ad:"live",ld:"multiline",md:"multiselectable",qd:"orientation",rd:"owns",sd:"posinset",ud:"pressed",yd:"readonly",Ad:"relevant",Bd:"required",Hd:"selected",Jd:"setsize",Ld:"sort",Yd:"valuemax",Zd:"valuemin",$d:"valuenow",ae:"valuetext"};var sc={lc:"alert",mc:"alertdialog",nc:"application",oc:"article",rc:"banner",tc:"button",uc:"checkbox",wc:"columnheader",xc:"combobox",yc:"complementary",zc:"contentinfo",Bc:"definition",Dc:"dialog",Ec:"directory",Gc:"document",Kc:"form",Mc:"grid",Nc:"gridcell",Oc:"group",Qc:"heading",Sc:"img",Xc:"link",Yc:"list",Zc:"listbox",$c:"listitem",bd:"log",cd:"main",dd:"marquee",ed:"math",fd:"menu",gd:"menubar",hd:"menuitem",jd:"menuitemcheckbox",kd:"menuitemradio",nd:"navigation",od:"note",pd:"option",
-td:"presentation",vd:"progressbar",wd:"radio",xd:"radiogroup",zd:"region",Cd:"row",Dd:"rowgroup",Ed:"rowheader",Fd:"scrollbar",Gd:"search",Id:"separator",Kd:"slider",Md:"spinbutton",Nd:"status",Od:"tab",Pd:"tablist",Qd:"tabpanel",Rd:"textbox",Sd:"timer",Td:"toolbar",Ud:"tooltip",Vd:"tree",Wd:"treegrid",Xd:"treeitem"};var tc=function(a,b){b?(r(Ia(sc,b),"No such ARIA role "+b),a.setAttribute("role",b)):a.removeAttribute("role")},vc=function(a,b,c){ea(c)&&(c=c.join(" "));var d=uc(b);""===c||void 0==c?(qc||(qc={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=qc,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d,
-c)},uc=function(a){r(a,"ARIA attribute cannot be empty.");r(Ia(rc,a),"No such ARIA attribute "+a);return"aria-"+a};var yc=function(a,b,c,d,e){if(!(v||x&&z("525")))return!0;if(y&&e)return wc(a);if(e&&!d)return!1;"number"==typeof b&&(b=xc(b));if(!c&&(17==b||18==b||y&&91==b))return!1;if(x&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(v&&d&&b==a)return!1;switch(a){case 13:return!(v&&v&&9<=eb);case 27:return!x}return wc(a)},wc=function(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||x&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;
-default:return!1}},xc=function(a){if(w)a=zc(a);else if(y&&x)t:switch(a){case 93:a=91;break t}return a},zc=function(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};var N=function(a,b){I.call(this);a&&Ac(this,a,b)};p(N,I);f=N.prototype;f.c=null;f.Ea=null;f.Wa=null;f.Fa=null;f.t=-1;f.O=-1;f.jb=!1;
-var Bc={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},Cc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Dc=v||x&&z("525"),Ec=y&&w;
-N.prototype.Qb=function(a){x&&(17==this.t&&!a.ctrlKey||18==this.t&&!a.altKey||y&&91==this.t&&!a.metaKey)&&(this.O=this.t=-1);-1==this.t&&(a.ctrlKey&&17!=a.keyCode?this.t=17:a.altKey&&18!=a.keyCode?this.t=18:a.metaKey&&91!=a.keyCode&&(this.t=91));Dc&&!yc(a.keyCode,this.t,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.O=xc(a.keyCode),Ec&&(this.jb=a.altKey))};N.prototype.Rb=function(a){this.O=this.t=-1;this.jb=a.altKey};
-N.prototype.handleEvent=function(a){var b=a.Q,c,d,e=b.altKey;v&&"keypress"==a.type?(c=this.O,d=13!=c&&27!=c?b.keyCode:0):x&&"keypress"==a.type?(c=this.O,d=0<=b.charCode&&63232>b.charCode&&wc(c)?b.charCode:0):Ta?(c=this.O,d=wc(c)?b.keyCode:0):(c=b.keyCode||this.O,d=b.charCode||0,Ec&&(e=this.jb),y&&63==d&&224==c&&(c=191));var g=c=xc(c),h=b.keyIdentifier;c?63232<=c&&c in Bc?g=Bc[c]:25==c&&a.shiftKey&&(g=9):h&&h in Cc&&(g=Cc[h]);a=g==this.t;this.t=g;b=new Fc(g,d,a,b);b.altKey=e;this.dispatchEvent(b)};
-N.prototype.a=function(){return this.c};var Ac=function(a,b,c){a.Fa&&a.detach();a.c=b;a.Ea=F(a.c,"keypress",a,c);a.Wa=F(a.c,"keydown",a.Qb,c,a);a.Fa=F(a.c,"keyup",a.Rb,c,a)};N.prototype.detach=function(){this.Ea&&(G(this.Ea),G(this.Wa),G(this.Fa),this.Fa=this.Wa=this.Ea=null);this.c=null;this.O=this.t=-1};var Fc=function(a,b,c,d){d&&Hb(this,d,void 0);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};p(Fc,C);var Gc=!!l.DOMTokenList,Hc=Gc?function(a){return a.classList}:function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},Ic=Gc?function(a,b){r(!!a.classList);return a.classList.contains(b)}:function(a,b){return t(Hc(a),b)},Jc=Gc?function(a,b){a.classList.add(b)}:function(a,b){Ic(a,b)||(a.className+=0<a.className.length?" "+b:b)},Kc=Gc?function(a,b){a.classList.remove(b)}:function(a,b){Ic(a,b)&&(a.className=Ba(Hc(a),function(a){return a!=b}).join(" "))};var Mc=function(a,b){if(!a)throw Error("Invalid class name "+a);if(!n(b))throw Error("Invalid decorator function "+b);Lc[a]=b},Nc={},Lc={};var O=function(){};ba(O);O.prototype.V=function(){};var Oc=function(a,b){a&&(a.tabIndex=b?0:-1)};f=O.prototype;f.r=function(a){return a.kb().r("div",this.ta(a).join(" "))};f.C=function(a){return a};f.Z=function(a){return"DIV"==a.tagName};f.L=function(a,b){b.id&&jc(a,b.id);var c=this.A(),d=!1,e=Hc(b);e&&Aa(e,function(b){b==c?d=!0:b&&this.ab(a,b,c)},this);d||Jc(b,c);Pc(a,this.C(b));return b};
-f.ab=function(a,b,c){b==c+"-disabled"?a.pa(!1):b==c+"-horizontal"?Qc(a,"horizontal"):b==c+"-vertical"&&Qc(a,"vertical")};var Pc=function(a,b){if(b)for(var c=b.firstChild,d;c&&c.parentNode==b;){d=c.nextSibling;if(1==c.nodeType){var e;t:{e=void 0;for(var g=Hc(c),h=0,k=g.length;h<k;h++)if(e=g[h]in Lc?Lc[g[h]]():null)break t;e=null}e&&(e.c=c,a.isEnabled()||e.pa(!1),a.Ca(e),e.L(c))}else c.nodeValue&&""!=pa(c.nodeValue)||b.removeChild(c);c=d}};
-O.prototype.Ma=function(a){a=a.a();r(a,"The container DOM element cannot be null.");ec(a,!0,w);v&&(a.hideFocus=!0);var b=this.V();b&&tc(a,b)};O.prototype.k=function(a){return a.a()};O.prototype.A=function(){return"goog-container"};O.prototype.ta=function(a){var b=this.A(),c=[b,"horizontal"==a.M?b+"-horizontal":b+"-vertical"];a.isEnabled()||c.push(b+"-disabled");return c};var P=function(){},Rc;ba(P);f=P.prototype;f.V=function(){};f.r=function(a){var b=a.kb().r("div",this.ta(a).join(" "),a.Ba);Sc(a,b);return b};f.C=function(a){return a};f.ra=function(a,b,c){if(a=a.a?a.a():a)if(v&&!z("7")){var d=Tc(hb(a),b);d.push(b);ka(c?ib:kb,a).apply(null,d)}else c?ib(a,b):kb(a,b)};f.Z=function(){return!0};
-f.L=function(a,b){b.id&&jc(a,b.id);var c=this.C(b);c&&c.firstChild?Uc(a,c.firstChild.nextSibling?Ea(c.childNodes):c.firstChild):a.Ba=null;var d=0,e=this.A(),g=this.A(),h=!1,k=!1,c=!1,q=hb(b);Aa(q,function(a){if(h||a!=e)if(k||a!=g){var b=d;this.tb||(this.Ha||Vc(this),this.tb=Ka(this.Ha));a=parseInt(this.tb[a],10);d=b|(isNaN(a)?0:a)}else k=!0;else h=!0,g==e&&(k=!0)},this);a.g=d;h||(q.push(e),g==e&&(k=!0));k||q.push(g);var ia=a.H;ia&&q.push.apply(q,ia);if(v&&!z("7")){var A=Tc(q);0<A.length&&(q.push.apply(q,
-A),c=!0)}if(!h||!k||ia||c)b.className=q.join(" ");Sc(a,b);return b};f.Ma=function(a){oc(a)&&this.qa(a.a(),!0);a.isEnabled()&&this.na(a,a.u())};var Wc=function(a,b,c){if(a=c||a.V())r(b,"The element passed as a first parameter cannot be null."),tc(b,a)},Sc=function(a,b){r(a);r(b);a.u()||vc(b,"hidden",!a.u());a.isEnabled()||Xc(b,1,!a.isEnabled());a.m&8&&Xc(b,8,!!(a.g&8));a.m&16&&Xc(b,16,!!(a.g&16));a.m&64&&Xc(b,64,!!(a.g&64))};f=P.prototype;f.za=function(a,b){ec(a,!b,!v&&!Ta)};
-f.qa=function(a,b){this.ra(a,this.A()+"-rtl",b)};f.J=function(a){var b;return a.m&32&&(b=a.k())?zb(b)&&Ab(b):!1};f.na=function(a,b){var c;if(a.m&32&&(c=a.k())){if(!b&&a.g&32){try{c.blur()}catch(d){}a.g&32&&a.la(null)}(zb(c)&&Ab(c))!=b&&(b?c.tabIndex=0:(c.tabIndex=-1,c.removeAttribute("tabIndex")))}};f.ja=function(a,b){K(a,b);a&&vc(a,"hidden",!b)};f.v=function(a,b,c){var d=a.a();if(d){var e=Yc(this,b);e&&this.ra(a,e,c);Xc(d,b,c)}};
-var Xc=function(a,b,c){Rc||(Rc={1:"disabled",8:"selected",16:"checked",64:"expanded"});if(b=Rc[b])r(a,"The element passed as a first parameter cannot be null."),vc(a,b,c)};P.prototype.k=function(a){return a.a()};P.prototype.A=function(){return"goog-control"};P.prototype.ta=function(a){var b=this.A(),c=[b],d=this.A();d!=b&&c.push(d);b=a.g;for(d=[];b;){var e=b&-b;d.push(Yc(this,e));b&=~e}c.push.apply(c,d);(a=a.H)&&c.push.apply(c,a);v&&!z("7")&&c.push.apply(c,Tc(c));return c};
-var Tc=function(a,b){var c=[];b&&(a=a.concat([b]));Aa([],function(d){!Ca(d,ka(t,a))||b&&!t(d,b)||c.push(d.join("_"))});return c},Yc=function(a,b){a.Ha||Vc(a);return a.Ha[b]},Vc=function(a){var b=a.A();a.Ha={1:b+"-disabled",2:b+"-hover",4:b+"-active",8:b+"-selected",16:b+"-checked",32:b+"-focused",64:b+"-open"}};var Q=function(a,b,c){L.call(this,c);if(!b){b=this.constructor;for(var d;b;){d=ja(b);if(d=Nc[d])break;b=b.f?b.f.constructor:null}b=d?n(d.ga)?d.ga():new d:null}this.b=b;this.Ba=void 0!==a?a:null};p(Q,L);f=Q.prototype;f.Ba=null;f.g=0;f.m=39;f.cc=255;f.W=0;f.q=!0;f.H=null;f.ba=!0;f.xa=!1;f.qb=null;f.pb=function(){return this.ba};f.Na=function(a){this.e&&a!=this.ba&&Zc(this,a);this.ba=a};f.k=function(){return this.b.k(this)};f.ya=function(){return this.fa||(this.fa=new N)};f.zb=function(){return this.b};
-f.ra=function(a,b){b?a&&(this.H?t(this.H,a)||this.H.push(a):this.H=[a],this.b.ra(this,a,!0)):a&&this.H&&Da(this.H,a)&&(0==this.H.length&&(this.H=null),this.b.ra(this,a,!1))};f.r=function(){var a=this.b.r(this);this.c=a;Wc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.u()||this.b.ja(a,!1)};f.C=function(){return this.b.C(this.a())};f.Z=function(a){return this.b.Z(a)};f.Xa=function(a){this.c=a=this.b.L(this,a);Wc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.q="none"!=a.style.display};
-f.G=function(){Q.f.G.call(this);this.b.Ma(this);if(this.m&-2&&(this.pb()&&Zc(this,!0),this.m&32)){var a=this.k();if(a){var b=this.ya();Ac(b,a);kc(this).d(b,"key",this.K).d(a,"focus",this.ma).d(a,"blur",this.la)}}};
-var Zc=function(a,b){var c=kc(a),d=a.a();b?(c.d(d,"mouseover",a.Qa).d(d,"mousedown",a.ka).d(d,"mouseup",a.Ra).d(d,"mouseout",a.Pa),a.oa!=aa&&c.d(d,"contextmenu",a.oa),v&&c.d(d,"dblclick",a.sb)):(c.w(d,"mouseover",a.Qa).w(d,"mousedown",a.ka).w(d,"mouseup",a.Ra).w(d,"mouseout",a.Pa),a.oa!=aa&&c.w(d,"contextmenu",a.oa),v&&c.w(d,"dblclick",a.sb))};Q.prototype.ca=function(){Q.f.ca.call(this);this.fa&&this.fa.detach();this.u()&&this.isEnabled()&&this.b.na(this,!1)};var Uc=function(a,b){a.Ba=b};f=Q.prototype;
-f.qa=function(a){Q.f.qa.call(this,a);var b=this.a();b&&this.b.qa(b,a)};f.za=function(a){this.xa=a;var b=this.a();b&&this.b.za(b,a)};f.u=function(){return this.q};f.ja=function(a,b){if(b||this.q!=a&&this.dispatchEvent(a?"show":"hide")){var c=this.a();c&&this.b.ja(c,a);this.isEnabled()&&this.b.na(this,a);this.q=a;return!0}return!1};f.isEnabled=function(){return!(this.g&1)};
-f.pa=function(a){var b=this.getParent();b&&"function"==typeof b.isEnabled&&!b.isEnabled()||!R(this,1,!a)||(a||(this.setActive(!1),this.D(!1)),this.u()&&this.b.na(this,a),this.v(1,!a))};f.D=function(a){R(this,2,a)&&this.v(2,a)};f.setActive=function(a){R(this,4,a)&&this.v(4,a)};var $c=function(a,b){R(a,8,b)&&a.v(8,b)},S=function(a,b){R(a,64,b)&&a.v(64,b)};Q.prototype.v=function(a,b){this.m&a&&b!=!!(this.g&a)&&(this.b.v(this,a,b),this.g=b?this.g|a:this.g&~a)};
-var ad=function(a,b,c){if(a.e&&a.g&b&&!c)throw Error("Component already rendered");!c&&a.g&b&&a.v(b,!1);a.m=c?a.m|b:a.m&~b},T=function(a,b){return!!(a.cc&b)&&!!(a.m&b)},R=function(a,b,c){return!!(a.m&b)&&!!(a.g&b)!=c&&(!(a.W&b)||a.dispatchEvent(hc(b,c)))&&!a.Sb};f=Q.prototype;f.Qa=function(a){(!a.relatedTarget||!vb(this.a(),a.relatedTarget))&&this.dispatchEvent("enter")&&this.isEnabled()&&T(this,2)&&this.D(!0)};
-f.Pa=function(a){a.relatedTarget&&vb(this.a(),a.relatedTarget)||!this.dispatchEvent("leave")||(T(this,4)&&this.setActive(!1),T(this,2)&&this.D(!1))};f.oa=aa;f.ka=function(a){this.isEnabled()&&(T(this,2)&&this.D(!0),!Jb(a)||x&&y&&a.ctrlKey||(T(this,4)&&this.setActive(!0),this.b.J(this)&&this.k().focus()));this.xa||!Jb(a)||x&&y&&a.ctrlKey||a.preventDefault()};f.Ra=function(a){this.isEnabled()&&(T(this,2)&&this.D(!0),this.g&4&&bd(this,a)&&T(this,4)&&this.setActive(!1))};
-f.sb=function(a){this.isEnabled()&&bd(this,a)};var bd=function(a,b){if(T(a,16)){var c=!(a.g&16);R(a,16,c)&&a.v(16,c)}T(a,8)&&$c(a,!0);T(a,64)&&S(a,!(a.g&64));c=new B("action",a);b&&(c.altKey=b.altKey,c.ctrlKey=b.ctrlKey,c.metaKey=b.metaKey,c.shiftKey=b.shiftKey,c.bb=b.bb);return a.dispatchEvent(c)};Q.prototype.ma=function(){T(this,32)&&R(this,32,!0)&&this.v(32,!0)};Q.prototype.la=function(){T(this,4)&&this.setActive(!1);T(this,32)&&R(this,32,!1)&&this.v(32,!1)};
-Q.prototype.K=function(a){return this.u()&&this.isEnabled()&&this.lb(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};Q.prototype.lb=function(a){return 13==a.keyCode&&bd(this,a)};if(!n(Q))throw Error("Invalid component class "+Q);if(!n(P))throw Error("Invalid renderer class "+P);var cd=ja(Q);Nc[cd]=P;Mc("goog-control",function(){return new Q(null)});var U=function(a,b,c){L.call(this,c);this.b=b||O.ga();this.M=a||"vertical"};p(U,L);f=U.prototype;f.ub=null;f.fa=null;f.b=null;f.M=null;f.q=!0;f.X=!0;f.Za=!0;f.h=-1;f.i=null;f.da=!1;f.Pb=!1;f.Ob=!0;f.N=null;f.k=function(){return this.ub||this.b.k(this)};f.ya=function(){return this.fa||(this.fa=new N(this.k()))};f.zb=function(){return this.b};f.r=function(){this.c=this.b.r(this)};f.C=function(){return this.b.C(this.a())};f.Z=function(a){return this.b.Z(a)};
-f.Xa=function(a){this.c=this.b.L(this,a);"none"==a.style.display&&(this.q=!1)};f.G=function(){U.f.G.call(this);mc(this,function(a){a.e&&dd(this,a)},this);var a=this.a();this.b.Ma(this);this.ja(this.q,!0);kc(this).d(this,"enter",this.Ib).d(this,"highlight",this.Jb).d(this,"unhighlight",this.Lb).d(this,"open",this.Kb).d(this,"close",this.Gb).d(a,"mousedown",this.ka).d(mb(a),"mouseup",this.Hb).d(a,["mousedown","mouseup","mouseover","mouseout","contextmenu"],this.Fb);this.J()&&ed(this,!0)};
-var ed=function(a,b){var c=kc(a),d=a.k();b?c.d(d,"focus",a.ma).d(d,"blur",a.la).d(a.ya(),"key",a.K):c.w(d,"focus",a.ma).w(d,"blur",a.la).w(a.ya(),"key",a.K)};f=U.prototype;f.ca=function(){fd(this,-1);this.i&&S(this.i,!1);this.da=!1;U.f.ca.call(this)};f.Ib=function(){return!0};
-f.Jb=function(a){var b=pc(this,a.target);if(-1<b&&b!=this.h){var c=M(this,this.h);c&&c.D(!1);this.h=b;c=M(this,this.h);this.da&&c.setActive(!0);this.Ob&&this.i&&c!=this.i&&(c.m&64?S(c,!0):S(this.i,!1))}b=this.a();r(b,"The DOM element for the container cannot be null.");null!=a.target.a()&&vc(b,"activedescendant",a.target.a().id)};f.Lb=function(a){a.target==M(this,this.h)&&(this.h=-1);a=this.a();r(a,"The DOM element for the container cannot be null.");a.removeAttribute(uc("activedescendant"))};
-f.Kb=function(a){(a=a.target)&&a!=this.i&&a.getParent()==this&&(this.i&&S(this.i,!1),this.i=a)};f.Gb=function(a){a.target==this.i&&(this.i=null)};f.ka=function(a){this.X&&(this.da=!0);var b=this.k();b&&zb(b)&&Ab(b)?b.focus():a.preventDefault()};f.Hb=function(){this.da=!1};
-f.Fb=function(a){var b;t:{b=a.target;if(this.N)for(var c=this.a();b&&b!==c;){var d=b.id;if(d in this.N){b=this.N[d];break t}b=b.parentNode}b=null}if(b)switch(a.type){case "mousedown":b.ka(a);break;case "mouseup":b.Ra(a);break;case "mouseover":b.Qa(a);break;case "mouseout":b.Pa(a);break;case "contextmenu":b.oa(a)}};f.ma=function(){};f.la=function(){fd(this,-1);this.da=!1;this.i&&S(this.i,!1)};
-f.K=function(a){return this.isEnabled()&&this.u()&&(0!=nc(this)||this.ub)&&this.lb(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};
-f.lb=function(a){var b=M(this,this.h);if(b&&"function"==typeof b.K&&b.K(a)||this.i&&this.i!=b&&"function"==typeof this.i.K&&this.i.K(a))return!0;if(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)return!1;switch(a.keyCode){case 27:if(this.J())this.k().blur();else return!1;break;case 36:gd(this);break;case 35:hd(this);break;case 38:if("vertical"==this.M)id(this);else return!1;break;case 37:if("horizontal"==this.M)oc(this)?jd(this):id(this);else return!1;break;case 40:if("vertical"==this.M)jd(this);else return!1;
-break;case 39:if("horizontal"==this.M)oc(this)?id(this):jd(this);else return!1;break;default:return!1}return!0};var dd=function(a,b){var c=b.a(),c=c.id||(c.id=ic(b));a.N||(a.N={});a.N[c]=b};U.prototype.Ca=function(a,b){ya(a,Q,"The child of a container must be a control");U.f.Ca.call(this,a,b)};U.prototype.Ta=function(a,b,c){a.W|=2;a.W|=64;!this.J()&&this.Pb||ad(a,32,!1);a.Na(!1);U.f.Ta.call(this,a,b,c);a.e&&this.e&&dd(this,a);b<=this.h&&this.h++};
-U.prototype.removeChild=function(a,b){if(a=m(a)?this.j&&a?(a in this.j?this.j[a]:void 0)||null:null:a){var c=pc(this,a);-1!=c&&(c==this.h?(a.D(!1),this.h=-1):c<this.h&&this.h--);var d=a.a();d&&d.id&&this.N&&(c=this.N,d=d.id,d in c&&delete c[d])}a=U.f.removeChild.call(this,a,b);a.Na(!0);return a};var Qc=function(a,b){if(a.a())throw Error("Component already rendered");a.M=b};f=U.prototype;f.u=function(){return this.q};
-f.ja=function(a,b){if(b||this.q!=a&&this.dispatchEvent(a?"show":"hide")){this.q=a;var c=this.a();c&&(K(c,a),this.J()&&Oc(this.k(),this.X&&this.q),b||this.dispatchEvent(this.q?"aftershow":"afterhide"));return!0}return!1};f.isEnabled=function(){return this.X};f.pa=function(a){this.X!=a&&this.dispatchEvent(a?"enable":"disable")&&(a?(this.X=!0,mc(this,function(a){a.vb?delete a.vb:a.pa(!0)})):(mc(this,function(a){a.isEnabled()?a.pa(!1):a.vb=!0}),this.da=this.X=!1),this.J()&&Oc(this.k(),a&&this.q))};
-f.J=function(){return this.Za};f.na=function(a){a!=this.Za&&this.e&&ed(this,a);this.Za=a;this.X&&this.q&&Oc(this.k(),a)};var fd=function(a,b){var c=M(a,b);c?c.D(!0):-1<a.h&&M(a,a.h).D(!1)};U.prototype.D=function(a){fd(this,pc(this,a))};
-var gd=function(a){kd(a,function(a,c){return(a+1)%c},nc(a)-1)},hd=function(a){kd(a,function(a,c){a--;return 0>a?c-1:a},0)},jd=function(a){kd(a,function(a,c){return(a+1)%c},a.h)},id=function(a){kd(a,function(a,c){a--;return 0>a?c-1:a},a.h)},kd=function(a,b,c){c=0>c?pc(a,a.i):c;var d=nc(a);c=b.call(a,c,d);for(var e=0;e<=d;){var g=M(a,c);if(g&&g.u()&&g.isEnabled()&&g.m&2){a.Ua(c);break}e++;c=b.call(a,c,d)}};U.prototype.Ua=function(a){fd(this,a)};var V=function(){};p(V,P);ba(V);f=V.prototype;f.A=function(){return"goog-tab"};f.V=function(){return"tab"};f.r=function(a){var b=V.f.r.call(this,a);(a=a.Sa())&&this.Va(b,a);return b};f.L=function(a,b){b=V.f.L.call(this,a,b);var c=this.Sa(b);c&&(a.rb=c);a.g&8&&(c=a.getParent())&&n(c.Y)&&(a.v(8,!1),c.Y(a));return b};f.Sa=function(a){return a.title||""};f.Va=function(a,b){a&&(a.title=b||"")};var ld=function(a,b,c){Q.call(this,a,b||V.ga(),c);ad(this,8,!0);this.W|=9};p(ld,Q);ld.prototype.Sa=function(){return this.rb};ld.prototype.Va=function(a){this.zb().Va(this.a(),a);this.rb=a};Mc("goog-tab",function(){return new ld(null)});var W=function(){};p(W,O);ba(W);W.prototype.A=function(){return"goog-tab-bar"};W.prototype.V=function(){return"tablist"};W.prototype.ab=function(a,b,c){this.Ab||(this.Ja||md(this),this.Ab=Ka(this.Ja));var d=this.Ab[b];d?(Qc(a,nd(d)),a.wb=d):W.f.ab.call(this,a,b,c)};W.prototype.ta=function(a){var b=W.f.ta.call(this,a);this.Ja||md(this);b.push(this.Ja[a.wb]);return b};var md=function(a){var b=a.A();a.Ja={top:b+"-top",bottom:b+"-bottom",start:b+"-start",end:b+"-end"}};var X=function(a,b,c){a=a||"top";Qc(this,nd(a));this.wb=a;U.call(this,this.M,b||W.ga(),c);od(this)};p(X,U);f=X.prototype;f.Zb=!0;f.I=null;f.G=function(){X.f.G.call(this);od(this)};f.removeChild=function(a,b){pd(this,a);return X.f.removeChild.call(this,a,b)};f.Ua=function(a){X.f.Ua.call(this,a);this.Zb&&this.Y(M(this,a))};f.Y=function(a){a?$c(a,!0):this.I&&$c(this.I,!1)};
-var pd=function(a,b){if(b&&b==a.I){for(var c=pc(a,b),d=c-1;b=M(a,d);d--)if(b.u()&&b.isEnabled()){a.Y(b);return}for(c+=1;b=M(a,c);c++)if(b.u()&&b.isEnabled()){a.Y(b);return}a.Y(null)}};f=X.prototype;f.Xb=function(a){this.I&&this.I!=a.target&&$c(this.I,!1);this.I=a.target};f.Yb=function(a){a.target==this.I&&(this.I=null)};f.Vb=function(a){pd(this,a.target)};f.Wb=function(a){pd(this,a.target)};f.ma=function(){M(this,this.h)||this.D(this.I||M(this,0))};
-var od=function(a){kc(a).d(a,"select",a.Xb).d(a,"unselect",a.Yb).d(a,"disable",a.Vb).d(a,"hide",a.Wb)},nd=function(a){return"start"==a||"end"==a?"vertical":"horizontal"};Mc("goog-tab-bar",function(){return new X});var Y=function(a,b,c,d,e){function g(a){a&&(a.tabIndex=0,tc(a,h.V()),Jc(a,"goog-zippy-header"),qd(h,a),a&&h.Mb.d(a,"keydown",h.Nb))}I.call(this);this.n=e||nb();this.T=this.n.a(a)||null;this.Aa=this.n.a(d||null);this.ea=(this.Oa=n(b)?b:null)||!b?null:this.n.a(b);this.l=!0==c;this.Mb=new H(this);this.ob=new H(this);var h=this;g(this.T);g(this.Aa);this.U(this.l)};p(Y,I);f=Y.prototype;f.ba=!0;f.V=function(){return"tab"};f.C=function(){return this.ea};f.toggle=function(){this.U(!this.l)};
-f.U=function(a){this.ea?K(this.ea,a):a&&this.Oa&&(this.ea=this.Oa());this.ea&&Jc(this.ea,"goog-zippy-content");if(this.Aa)K(this.T,!a),K(this.Aa,a);else if(this.T){var b=this.T;a?Jc(b,"goog-zippy-expanded"):Kc(b,"goog-zippy-expanded");b=this.T;a?Kc(b,"goog-zippy-collapsed"):Jc(b,"goog-zippy-collapsed");vc(this.T,"expanded",a)}this.l=a;this.dispatchEvent(new rd("toggle",this))};f.pb=function(){return this.ba};f.Na=function(a){this.ba!=a&&((this.ba=a)?(qd(this,this.T),qd(this,this.Aa)):this.ob.$a())};
-var qd=function(a,b){b&&a.ob.d(b,"click",a.$b)};Y.prototype.Nb=function(a){if(13==a.keyCode||32==a.keyCode)this.toggle(),this.dispatchEvent(new B("action",this)),a.preventDefault(),a.stopPropagation()};Y.prototype.$b=function(){this.toggle();this.dispatchEvent(new B("action",this))};var rd=function(a,b){B.call(this,a,b)};p(rd,B);var Z=function(a,b){this.nb=[];for(var c=pb("span","ae-zippy",ob(document,a)),d=0,e;e=c[d];d++){var g;if(void 0!=e.parentNode.parentNode.parentNode.nextElementSibling)g=e.parentNode.parentNode.parentNode.nextElementSibling;else for(g=e.parentNode.parentNode.parentNode.nextSibling;g&&1!=g.nodeType;)g=g.nextSibling;e=new Y(e,g,!1);this.nb.push(e)}this.fc=new sd(this.nb,ob(document,b))};Z.prototype.ic=function(){return this.fc};Z.prototype.jc=function(){return this.nb};
-var sd=function(a,b){this.ua=a;if(this.ua.length)for(var c=0,d;d=this.ua[c];c++)F(d,"toggle",this.Ub,!1,this);this.Ka=0;this.l=!1;c="ae-toggle ae-plus ae-action";this.ua.length||(c+=" ae-disabled");this.R=tb("span",{className:c},"Expand All");F(this.R,"click",this.Tb,!1,this);b&&b.appendChild(this.R)};sd.prototype.Tb=function(){this.ua.length&&this.U(!this.l)};
-sd.prototype.Ub=function(a){a=a.currentTarget;this.Ka=a.l?this.Ka+1:this.Ka-1;a.l!=this.l&&(a.l?(this.l=!0,td(this,!0)):0==this.Ka&&(this.l=!1,td(this,!1)))};sd.prototype.U=function(a){this.l=a;a=0;for(var b;b=this.ua[a];a++)b.l!=this.l&&b.U(this.l);td(this)};
-var td=function(a,b){(void 0!==b?b:a.l)?(kb(a.R,"ae-plus"),ib(a.R,"ae-minus"),wb(a.R,"Collapse All")):(kb(a.R,"ae-minus"),ib(a.R,"ae-plus"),wb(a.R,"Expand All"))},ud=function(a){this.ac=a;this.Cb={};var b,c=tb("div",{},b=tb("div",{id:"ae-stats-details-tabs",className:"goog-tab-bar goog-tab-bar-top"}),tb("div",{className:"goog-tab-bar-clear"}),a=tb("div",{id:"ae-stats-details-tabs-content",className:"goog-tab-content"})),d=new X;d.L(b);F(d,"select",this.Bb,!1,this);F(d,"unselect",this.Bb,!1,this);
-b=0;for(var e;e=this.ac[b];b++)if(e=ob(document,"ae-stats-details-"+e)){var g=pb("h2",null,e)[0],h;h=g;var k=void 0;gb&&"innerText"in h?k=h.innerText.replace(/(\r\n|\r|\n)/g,"\n"):(k=[],Bb(h,k,!0),k=k.join(""));k=k.replace(/ \xAD /g," ").replace(/\xAD/g,"");k=k.replace(/\u200B/g,"");gb||(k=k.replace(/ +/g," "));" "!=k&&(k=k.replace(/^\s*/,""));h=k;g&&g.parentNode&&g.parentNode.removeChild(g);g=new ld(h);this.Cb[ja(g)]=e;d.Ca(g,!0);a.appendChild(e);0==b?d.Y(g):K(e,!1)}ob(document,"bd").appendChild(c)};
-ud.prototype.Bb=function(a){var b=this.Cb[ja(a.target)];K(b,"select"==a.type)};la("ae.Stats.Details.Tabs",ud);la("goog.ui.Zippy",Y);Y.prototype.setExpanded=Y.prototype.U;la("ae.Stats.MakeZippys",Z);Z.prototype.getExpandCollapse=Z.prototype.ic;Z.prototype.getZippys=Z.prototype.jc;sd.prototype.setExpanded=sd.prototype.U;var $=function(){this.cb=[];this.hb=[]},vd=[[5,0.2,1],[6,0.2,1.2],[5,0.25,1.25],[6,0.25,1.5],[4,0.5,2],[5,0.5,2.5],[6,0.5,3],[4,1,4],[5,1,5],[6,1,6],[4,2,8],[5,2,10]],wd=function(a){if(0>=a)return[2,0.5,1];for(var b=1;1>a;)a*=10,b/=10;for(;10<=a;)a/=10,b*=10;for(var c=0;c<vd.length;c++)if(a<=vd[c][2])return[vd[c][0],vd[c][1]*b,vd[c][2]*b];return[5,2*b,10*b]};$.prototype.gb="stats/static/pix.gif";$.prototype.B="ae-stats-gantt-";$.prototype.fb=0;$.prototype.write=function(a){this.hb.push(a)};
-var xd=function(a,b,c,d){a.write('<tr class="'+a.B+'axisrow"><td width="20%"></td><td>');a.write('<div class="'+a.B+'axis">');for(var e=0;e<=b;e++)a.write('<img class="'+a.B+'tick" src="'+a.gb+'" alt="" '),a.write('style="left:'+e*c*d+'%"\n>'),a.write('<span class="'+a.B+'scale" style="left:'+e*c*d+'%">'),a.write("&nbsp;"+e*c+"</span>");a.write("</div></td></tr>\n")};
-$.prototype.hc=function(){this.hb=[];var a=wd(this.fb),b=a[0],c=a[1],a=100/a[2];this.write('<table class="'+this.B+'table">\n');xd(this,b,c,a);for(var d=0;d<this.cb.length;d++){var e=this.cb[d];this.write('<tr class="'+this.B+'datarow"><td width="20%">');0<e.label.length&&(0<e.ia.length&&this.write('<a class="'+this.B+'link" href="'+e.ia+'">'),this.write(e.label),0<e.ia.length&&this.write("</a>"));this.write("</td>\n<td>");this.write('<div class="'+this.B+'container">');0<e.ia.length&&this.write('<a class="'+
-this.B+'link" href="'+e.ia+'"\n>');this.write('<img class="'+this.B+'bar" src="'+this.gb+'" alt="" ');this.write('style="left:'+e.start*a+"%;width:"+e.duration*a+'%;min-width:1px"\n>');0<e.eb&&(this.write('<img class="'+this.B+'extra" src="'+this.gb+'" alt="" '),this.write('style="left:'+e.start*a+"%;width:"+e.eb*a+'%"\n>'));0<e.yb.length&&(this.write('<span class="'+this.B+'inline" style="left:'+(e.start+Math.max(e.duration,e.eb))*a+'%">&nbsp;'),this.write(e.yb),this.write("</span>"));0<e.ia.length&&
-this.write("</a>");this.write("</div></td></tr>\n")}xd(this,b,c,a);this.write("</table>\n");return this.hb.join("")};$.prototype.gc=function(a,b,c,d,e,g){this.fb=Math.max(this.fb,Math.max(b+c,b+d));this.cb.push({label:a,start:b,duration:c,eb:d,yb:e,ia:g})};la("Gantt",$);$.prototype.add_bar=$.prototype.gc;$.prototype.draw=$.prototype.hc;})();
+/* Copyright 2008-10 Google Inc. All Rights Reserved. */ (function(){var f,l=this,aa=function(){},ba=function(a){a.fa=function(){return a.Eb?a.Eb:a.Eb=new a}},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=
+typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},da=function(a){return"array"==ca(a)},ea=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},m=function(a){return"string"==typeof a},n=function(a){return"function"==ca(a)},fa=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ka=function(a){return a[ga]||
+(a[ga]=++ja)},ga="closure_uid_"+(1E9*Math.random()>>>0),ja=0,la=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},ma=function(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},p=function(a,b){function c(){}c.prototype=b.prototype;a.e=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.kc=
+function(a,c,g){var h=Array.prototype.slice.call(arguments,2);return b.prototype[c].apply(a,h)}};var na=function(a){Error.captureStackTrace?Error.captureStackTrace(this,na):this.stack=Error().stack||"";a&&(this.message=String(a))};p(na,Error);na.prototype.name="CustomError";var oa;var pa=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join("%s")},qa=function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},xa=function(a){if(!ra.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(sa,"&amp;"));-1!=a.indexOf("<")&&(a=a.replace(ta,"&lt;"));-1!=a.indexOf(">")&&(a=a.replace(ua,"&gt;"));-1!=a.indexOf('"')&&(a=a.replace(va,"&quot;"));-1!=a.indexOf("'")&&(a=a.replace(wa,"&#39;"));return a},sa=
+/&/g,ta=/</g,ua=/>/g,va=/"/g,wa=/'/g,ra=/[&<>"']/,ya=function(a,b){return a<b?-1:a>b?1:0};var za=function(a,b){b.unshift(a);na.call(this,pa.apply(null,b));b.shift()};p(za,na);za.prototype.name="AssertionError";var Aa=function(a,b,c){var d="Assertion failed";if(b)var d=d+(": "+b),e=c;else a&&(d+=": "+a,e=null);throw new za(""+d,e||[]);},r=function(a,b,c){a||Aa("",b,Array.prototype.slice.call(arguments,2))},Ba=function(a,b,c,d){a instanceof b||Aa("instanceof check failed.",c,Array.prototype.slice.call(arguments,3))};var s=Array.prototype,Ca=s.indexOf?function(a,b,c){r(null!=a.length);return s.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(m(a))return m(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},Da=s.forEach?function(a,b,c){r(null!=a.length);s.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)g in e&&b.call(c,e[g],g,a)},Ea=s.filter?function(a,b,c){r(null!=a.length);return s.filter.call(a,
+b,c)}:function(a,b,c){for(var d=a.length,e=[],g=0,h=m(a)?a.split(""):a,k=0;k<d;k++)if(k in h){var q=h[k];b.call(c,q,k,a)&&(e[g++]=q)}return e},Fa=s.every?function(a,b,c){r(null!=a.length);return s.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)if(g in e&&!b.call(c,e[g],g,a))return!1;return!0},t=function(a,b){return 0<=Ca(a,b)},Ga=function(a,b){var c=Ca(a,b),d;if(d=0<=c)r(null!=a.length),s.splice.call(a,c,1);return d},Ha=function(a){var b=a.length;if(0<b){for(var c=
+Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},Ja=function(a,b,c,d){r(null!=a.length);s.splice.apply(a,Ia(arguments,1))},Ia=function(a,b,c){r(null!=a.length);return 2>=arguments.length?s.slice.call(a,b):s.slice.call(a,b,c)};var Ka=function(a,b){for(var c in a)b.call(void 0,a[c],c,a)},La=function(a,b){for(var c in a)if(a[c]==b)return!0;return!1},Ma=function(a,b,c){if(b in a)throw Error('The object already contains the key "'+b+'"');a[b]=c},Na=function(a){var b={},c;for(c in a)b[a[c]]=c;return b},Oa="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Pa=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var g=0;g<Oa.length;g++)c=
+Oa[g],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var u,Qa,Ra,Sa,Ta=function(){return l.navigator?l.navigator.userAgent:null};Sa=Ra=Qa=u=!1;var Ua;if(Ua=Ta()){var Va=l.navigator;u=0==Ua.lastIndexOf("Opera",0);Qa=!u&&(-1!=Ua.indexOf("MSIE")||-1!=Ua.indexOf("Trident"));Ra=!u&&-1!=Ua.indexOf("WebKit");Sa=!u&&!Ra&&!Qa&&"Gecko"==Va.product}var Wa=u,v=Qa,w=Sa,x=Ra,Xa=l.navigator,y=-1!=(Xa&&Xa.platform||"").indexOf("Mac"),Ya=function(){var a=l.document;return a?a.documentMode:void 0},Za;
+t:{var $a="",ab;if(Wa&&l.opera)var bb=l.opera.version,$a="function"==typeof bb?bb():bb;else if(w?ab=/rv\:([^\);]+)(\)|;)/:v?ab=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:x&&(ab=/WebKit\/(\S+)/),ab)var cb=ab.exec(Ta()),$a=cb?cb[1]:"";if(v){var db=Ya();if(db>parseFloat($a)){Za=String(db);break t}}Za=$a}
+var eb=Za,fb={},z=function(a){var b;if(!(b=fb[a])){b=0;for(var c=qa(String(eb)).split("."),d=qa(String(a)).split("."),e=Math.max(c.length,d.length),g=0;0==b&&g<e;g++){var h=c[g]||"",k=d[g]||"",q=RegExp("(\\d*)(\\D*)","g"),ha=RegExp("(\\d*)(\\D*)","g");do{var M=q.exec(h)||["","",""],ia=ha.exec(k)||["","",""];if(0==M[0].length&&0==ia[0].length)break;b=ya(0==M[1].length?0:parseInt(M[1],10),0==ia[1].length?0:parseInt(ia[1],10))||ya(0==M[2].length,0==ia[2].length)||ya(M[2],ia[2])}while(0==b)}b=fb[a]=0<=
+b}return b},gb=l.document,hb=gb&&v?Ya()||("CSS1Compat"==gb.compatMode?parseInt(eb,10):5):void 0;var ib=!v||v&&9<=hb;!w&&!v||v&&v&&9<=hb||w&&z("1.9.1");var jb=v&&!z("9");var kb=function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},lb=function(a,b){for(var c=kb(a),d=Ia(arguments,1),e=c.length+d.length,g=c,h=0;h<d.length;h++)t(g,d[h])||g.push(d[h]);a.className=c.join(" ");return c.length==e},nb=function(a,b){var c=kb(a),d=Ia(arguments,1),e=mb(c,d);a.className=e.join(" ");return e.length==c.length-d.length},mb=function(a,b){return Ea(a,function(a){return!t(b,a)})};var qb=function(a){return a?new ob(pb(a)):oa||(oa=new ob)},rb=function(a,b){return m(b)?a.getElementById(b):b},sb=function(a,b,c){var d=document;c=c||d;a=a&&"*"!=a?a.toUpperCase():"";if(c.querySelectorAll&&c.querySelector&&(a||b))return c.querySelectorAll(a+(b?"."+b:""));if(b&&c.getElementsByClassName){c=c.getElementsByClassName(b);if(a){for(var d={},e=0,g=0,h;h=c[g];g++)a==h.nodeName&&(d[e++]=h);d.length=e;return d}return c}c=c.getElementsByTagName(a||"*");if(b){d={};for(g=e=0;h=c[g];g++)a=h.className,
+"function"==typeof a.split&&t(a.split(/\s+/),b)&&(d[e++]=h);d.length=e;return d}return c},ub=function(a,b){Ka(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in tb?a.setAttribute(tb[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})},tb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",
+valign:"vAlign",width:"width"},wb=function(a,b,c){return vb(document,arguments)},vb=function(a,b){var c=b[0],d=b[1];if(!ib&&d&&(d.name||d.type)){c=["<",c];d.name&&c.push(' name="',xa(d.name),'"');if(d.type){c.push(' type="',xa(d.type),'"');var e={};Pa(e,d);delete e.type;d=e}c.push(">");c=c.join("")}c=a.createElement(c);d&&(m(d)?c.className=d:da(d)?lb.apply(null,[c].concat(d)):ub(c,d));2<b.length&&xb(a,c,b);return c},xb=function(a,b,c){function d(c){c&&b.appendChild(m(c)?a.createTextNode(c):c)}for(var e=
+2;e<c.length;e++){var g=c[e];if(!ea(g)||fa(g)&&0<g.nodeType)d(g);else{var h;t:{if(g&&"number"==typeof g.length){if(fa(g)){h="function"==typeof g.item||"string"==typeof g.item;break t}if(n(g)){h="function"==typeof g.item;break t}}h=!1}Da(h?Ha(g):g,d)}}},yb=function(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a},pb=function(a){return 9==a.nodeType?
+a:a.ownerDocument||a.document},zb=function(a,b){r(null!=a,"goog.dom.setTextContent expects a non-null value for node");if("textContent"in a)a.textContent=b;else if(3==a.nodeType)a.data=b;else if(a.firstChild&&3==a.firstChild.nodeType){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);a.firstChild.data=b}else{for(var c;c=a.firstChild;)a.removeChild(c);a.appendChild(pb(a).createTextNode(String(b)))}},Ab={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},Bb={IMG:" ",BR:"\n"},Cb=function(a){a=a.getAttributeNode("tabindex");
+return null!=a&&a.specified},Db=function(a){a=a.tabIndex;return"number"==typeof a&&0<=a&&32768>a},Eb=function(a,b,c){if(!(a.nodeName in Ab))if(3==a.nodeType)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in Bb)b.push(Bb[a.nodeName]);else for(a=a.firstChild;a;)Eb(a,b,c),a=a.nextSibling},ob=function(a){this.Q=a||l.document||document};f=ob.prototype;f.kb=qb;f.a=function(a){return rb(this.Q,a)};f.o=function(a,b,c){return vb(this.Q,arguments)};
+f.createElement=function(a){return this.Q.createElement(a)};f.createTextNode=function(a){return this.Q.createTextNode(String(a))};f.appendChild=function(a,b){a.appendChild(b)};f.contains=yb;f.I=function(a){var b;(b="A"==a.tagName||"INPUT"==a.tagName||"TEXTAREA"==a.tagName||"SELECT"==a.tagName||"BUTTON"==a.tagName?!a.disabled&&(!Cb(a)||Db(a)):Cb(a)&&Db(a))&&v?(a=n(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth},a=null!=a&&0<a.height&&0<a.width):a=b;return a};var Fb=function(a){Fb[" "](a);return a};Fb[" "]=aa;var Gb=!v||v&&9<=hb,Hb=!v||v&&9<=hb,Ib=v&&!z("9");!x||z("528");w&&z("1.9b")||v&&z("8")||Wa&&z("9.5")||x&&z("528");w&&!z("8")||v&&z("9");var Jb=function(){};Jb.prototype.Sb=!1;var A=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.aa=!1;this.xb=!0};A.prototype.stopPropagation=function(){this.aa=!0};A.prototype.preventDefault=function(){this.defaultPrevented=!0;this.xb=!1};var B=function(a,b){A.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.state=null;this.hb=!1;this.O=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(w){var e;t:{try{Fb(d.nodeName);e=!0;break t}catch(g){}e=!1}e||(d=null)}}else"mouseover"==
+c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=x||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=x||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=
+a.metaKey;this.hb=y?a.metaKey:a.ctrlKey;this.state=a.state;this.O=a;a.defaultPrevented&&this.preventDefault()}};p(B,A);var Kb=[1,4,2],Lb=function(a){return Gb?0==a.O.button:"click"==a.type?!0:!!(a.O.button&Kb[0])};B.prototype.stopPropagation=function(){B.e.stopPropagation.call(this);this.O.stopPropagation?this.O.stopPropagation():this.O.cancelBubble=!0};
+B.prototype.preventDefault=function(){B.e.preventDefault.call(this);var a=this.O;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Ib)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var Mb="closure_listenable_"+(1E6*Math.random()|0),Nb=function(a){try{return!(!a||!a[Mb])}catch(b){return!1}},Ob=0;var Pb=function(a,b,c,d,e){this.X=a;this.Da=null;this.src=b;this.type=c;this.capture=!!d;this.Ia=e;this.key=++Ob;this.ga=this.Ga=!1},Qb=function(a){a.ga=!0;a.X=null;a.Da=null;a.src=null;a.Ia=null};var C=function(a){this.src=a;this.m={};this.va=0};C.prototype.add=function(a,b,c,d,e){var g=this.m[a];g||(g=this.m[a]=[],this.va++);var h=Rb(g,b,d,e);-1<h?(a=g[h],c||(a.Ga=!1)):(a=new Pb(b,this.src,a,!!d,e),a.Ga=c,g.push(a));return a};C.prototype.remove=function(a,b,c,d){if(!(a in this.m))return!1;var e=this.m[a];b=Rb(e,b,c,d);return-1<b?(Qb(e[b]),r(null!=e.length),s.splice.call(e,b,1),0==e.length&&(delete this.m[a],this.va--),!0):!1};
+var Sb=function(a,b){var c=b.type;if(!(c in a.m))return!1;var d=Ga(a.m[c],b);d&&(Qb(b),0==a.m[c].length&&(delete a.m[c],a.va--));return d};C.prototype.Za=function(a){var b=0,c;for(c in this.m)if(!a||c==a){for(var d=this.m[c],e=0;e<d.length;e++)++b,Qb(d[e]);delete this.m[c];this.va--}return b};C.prototype.wa=function(a,b,c,d){a=this.m[a];var e=-1;a&&(e=Rb(a,b,c,d));return-1<e?a[e]:null};var Rb=function(a,b,c,d){for(var e=0;e<a.length;++e){var g=a[e];if(!g.ga&&g.X==b&&g.capture==!!c&&g.Ia==d)return e}return-1};var Tb="closure_lm_"+(1E6*Math.random()|0),D={},Ub=0,E=function(a,b,c,d,e){if(da(b)){for(var g=0;g<b.length;g++)E(a,b[g],c,d,e);return null}c=Vb(c);if(Nb(a))a=a.c(b,c,d,e);else{if(!b)throw Error("Invalid event type");var g=!!d,h=Wb(a);h||(a[Tb]=h=new C(a));c=h.add(b,c,!1,d,e);c.Da||(d=Xb(),c.Da=d,d.src=a,d.X=c,a.addEventListener?a.addEventListener(b,d,g):a.attachEvent(b in D?D[b]:D[b]="on"+b,d),Ub++);a=c}return a},Xb=function(){var a=Yb,b=Hb?function(c){return a.call(b.src,b.X,c)}:function(c){c=a.call(b.src,
+b.X,c);if(!c)return c};return b},Zb=function(a,b,c,d,e){if(da(b))for(var g=0;g<b.length;g++)Zb(a,b[g],c,d,e);else c=Vb(c),Nb(a)?a.u(b,c,d,e):a&&(a=Wb(a))&&(b=a.wa(b,c,!!d,e))&&F(b)},F=function(a){if("number"==typeof a||!a||a.ga)return!1;var b=a.src;if(Nb(b))return Sb(b.Z,a);var c=a.type,d=a.Da;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(c in D?D[c]:D[c]="on"+c,d);Ub--;(c=Wb(b))?(Sb(c,a),0==c.va&&(c.src=null,b[Tb]=null)):Qb(a);return!0},ac=function(a,b,c,
+d){var e=1;if(a=Wb(a))if(b=a.m[b])for(b=Ha(b),a=0;a<b.length;a++){var g=b[a];g&&g.capture==c&&!g.ga&&(e&=!1!==$b(g,d))}return Boolean(e)},$b=function(a,b){var c=a.X,d=a.Ia||a.src;a.Ga&&F(a);return c.call(d,b)},Yb=function(a,b){if(a.ga)return!0;if(!Hb){var c;if(!(c=b))t:{c=["window","event"];for(var d=l,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break t}c=d}e=c;c=new B(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){t:{var g=!1;if(0==e.keyCode)try{e.keyCode=-1;break t}catch(h){g=!0}if(g||
+void 0==e.returnValue)e.returnValue=!0}e=[];for(g=c.currentTarget;g;g=g.parentNode)e.push(g);for(var g=a.type,k=e.length-1;!c.aa&&0<=k;k--)c.currentTarget=e[k],d&=ac(e[k],g,!0,c);for(k=0;!c.aa&&k<e.length;k++)c.currentTarget=e[k],d&=ac(e[k],g,!1,c)}return d}return $b(a,new B(b,this))},Wb=function(a){a=a[Tb];return a instanceof C?a:null},bc="__closure_events_fn_"+(1E9*Math.random()>>>0),Vb=function(a){r(a,"Listener can not be null.");if(n(a))return a;r(a.handleEvent,"An object listener must have handleEvent method.");
+return a[bc]||(a[bc]=function(b){return a.handleEvent(b)})};var G=function(a){this.Db=a;this.La={}};p(G,Jb);var cc=[];G.prototype.c=function(a,b,c,d){da(b)||(cc[0]=b,b=cc);for(var e=0;e<b.length;e++){var g=E(a,b[e],c||this.handleEvent,d||!1,this.Db||this);if(!g)break;this.La[g.key]=g}return this};G.prototype.u=function(a,b,c,d,e){if(da(b))for(var g=0;g<b.length;g++)this.u(a,b[g],c,d,e);else c=c||this.handleEvent,e=e||this.Db||this,c=Vb(c),d=!!d,b=Nb(a)?a.wa(b,c,d,e):a?(a=Wb(a))?a.wa(b,c,d,e):null:null,b&&(F(b),delete this.La[b.key]);return this};
+G.prototype.Za=function(){Ka(this.La,F);this.La={}};G.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var H=function(){this.Z=new C(this);this.bc=this};p(H,Jb);H.prototype[Mb]=!0;f=H.prototype;f.mb=null;f.eb=function(a){this.mb=a};f.addEventListener=function(a,b,c,d){E(this,a,b,c,d)};f.removeEventListener=function(a,b,c,d){Zb(this,a,b,c,d)};
+f.dispatchEvent=function(a){dc(this);var b,c=this.mb;if(c){b=[];for(var d=1;c;c=c.mb)b.push(c),r(1E3>++d,"infinite loop")}c=this.bc;d=a.type||a;if(m(a))a=new A(a,c);else if(a instanceof A)a.target=a.target||c;else{var e=a;a=new A(d,c);Pa(a,e)}var e=!0,g;if(b)for(var h=b.length-1;!a.aa&&0<=h;h--)g=a.currentTarget=b[h],e=ec(g,d,!0,a)&&e;a.aa||(g=a.currentTarget=c,e=ec(g,d,!0,a)&&e,a.aa||(e=ec(g,d,!1,a)&&e));if(b)for(h=0;!a.aa&&h<b.length;h++)g=a.currentTarget=b[h],e=ec(g,d,!1,a)&&e;return e};
+f.c=function(a,b,c,d){dc(this);return this.Z.add(String(a),b,!1,c,d)};f.u=function(a,b,c,d){return this.Z.remove(String(a),b,c,d)};var ec=function(a,b,c,d){b=a.Z.m[String(b)];if(!b)return!0;b=Ha(b);for(var e=!0,g=0;g<b.length;++g){var h=b[g];if(h&&!h.ga&&h.capture==c){var k=h.X,q=h.Ia||h.src;h.Ga&&Sb(a.Z,h);e=!1!==k.call(q,d)&&e}}return e&&!1!=d.xb};H.prototype.wa=function(a,b,c,d){return this.Z.wa(String(a),b,c,d)};var dc=function(a){r(a.Z,"Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?")};var I=function(a,b){a.style.display=b?"":"none"},fc=w?"MozUserSelect":x?"WebkitUserSelect":null,gc=function(a,b,c){c=c?null:a.getElementsByTagName("*");if(fc){if(b=b?"none":"",a.style[fc]=b,c){a=0;for(var d;d=c[a];a++)d.style[fc]=b}}else if(v||Wa)if(b=b?"on":"",a.setAttribute("unselectable",b),c)for(a=0;d=c[a];a++)d.setAttribute("unselectable",b)};var hc=function(){};ba(hc);hc.prototype.ec=0;var J=function(a){H.call(this);this.A=a||qb();this.sa=ic};p(J,H);J.prototype.dc=hc.fa();var ic=null,jc=function(a,b){switch(a){case 1:return b?"disable":"enable";case 2:return b?"highlight":"unhighlight";case 4:return b?"activate":"deactivate";case 8:return b?"select":"unselect";case 16:return b?"check":"uncheck";case 32:return b?"focus":"blur";case 64:return b?"open":"close"}throw Error("Invalid component state");};f=J.prototype;f.ha=null;f.f=!1;f.d=null;f.sa=null;f.p=null;f.q=null;f.F=null;
+var kc=function(a){return a.ha||(a.ha=":"+(a.dc.ec++).toString(36))},lc=function(a,b){if(a.p&&a.p.F){var c=a.p.F,d=a.ha;d in c&&delete c[d];Ma(a.p.F,b,a)}a.ha=b};J.prototype.a=function(){return this.d};var mc=function(a){return a.jb||(a.jb=new G(a))},oc=function(a,b){if(a==b)throw Error("Unable to set parent component");if(b&&a.p&&a.ha&&nc(a.p,a.ha)&&a.p!=b)throw Error("Unable to set parent component");a.p=b;J.e.eb.call(a,b)};f=J.prototype;f.getParent=function(){return this.p};
+f.eb=function(a){if(this.p&&this.p!=a)throw Error("Method not supported");J.e.eb.call(this,a)};f.kb=function(){return this.A};f.o=function(){this.d=this.A.createElement("div")};f.K=function(a){if(this.f)throw Error("Component already rendered");if(a&&this.Y(a)){var b=pb(a);this.A&&this.A.Q==b||(this.A=qb(a));this.Xa(a);this.D()}else throw Error("Invalid element to decorate");};f.Y=function(){return!0};f.Xa=function(a){this.d=a};f.D=function(){this.f=!0;pc(this,function(a){!a.f&&a.a()&&a.D()})};
+f.ca=function(){pc(this,function(a){a.f&&a.ca()});this.jb&&this.jb.Za();this.f=!1};f.Ca=function(a,b){this.Ta(a,qc(this),b)};
+f.Ta=function(a,b,c){r(!!a,"Provided element must not be null.");if(a.f&&(c||!this.f))throw Error("Component already rendered");if(0>b||b>qc(this))throw Error("Child component index out of bounds");this.F&&this.q||(this.F={},this.q=[]);if(a.getParent()==this){var d=kc(a);this.F[d]=a;Ga(this.q,a)}else Ma(this.F,kc(a),a);oc(a,this);Ja(this.q,b,0,a);if(a.f&&this.f&&a.getParent()==this)c=this.B(),c.insertBefore(a.a(),c.childNodes[b]||null);else if(c){this.d||this.o();c=K(this,b+1);b=this.B();c=c?c.d:
+null;if(a.f)throw Error("Component already rendered");a.d||a.o();b?b.insertBefore(a.d,c||null):a.A.Q.body.appendChild(a.d);a.p&&!a.p.f||a.D()}else this.f&&!a.f&&a.d&&a.d.parentNode&&1==a.d.parentNode.nodeType&&a.D()};f.B=function(){return this.d};
+var rc=function(a){if(null==a.sa){var b=a.f?a.d:a.A.Q.body,c;t:{c=pb(b);if(c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(b,null))){c=c.direction||c.getPropertyValue("direction")||"";break t}c=""}a.sa="rtl"==(c||(b.currentStyle?b.currentStyle.direction:null)||b.style&&b.style.direction)}return a.sa};J.prototype.pa=function(a){if(this.f)throw Error("Component already rendered");this.sa=a};
+var qc=function(a){return a.q?a.q.length:0},nc=function(a,b){var c;a.F&&b?(c=a.F,c=(b in c?c[b]:void 0)||null):c=null;return c},K=function(a,b){return a.q?a.q[b]||null:null},pc=function(a,b,c){a.q&&Da(a.q,b,c)},sc=function(a,b){return a.q&&b?Ca(a.q,b):-1};
+J.prototype.removeChild=function(a,b){if(a){var c=m(a)?a:kc(a);a=nc(this,c);if(c&&a){var d=this.F;c in d&&delete d[c];Ga(this.q,a);b&&(a.ca(),a.d&&(c=a.d)&&c.parentNode&&c.parentNode.removeChild(c));oc(a,null)}}if(!a)throw Error("Child is not in parent component");return a};var tc,uc={lc:"activedescendant",qc:"atomic",rc:"autocomplete",tc:"busy",wc:"checked",Bc:"controls",Dc:"describedby",Gc:"disabled",Ic:"dropeffect",Jc:"expanded",Kc:"flowto",Mc:"grabbed",Qc:"haspopup",Sc:"hidden",Uc:"invalid",Vc:"label",Wc:"labelledby",Xc:"level",bd:"live",md:"multiline",nd:"multiselectable",rd:"orientation",sd:"owns",td:"posinset",vd:"pressed",zd:"readonly",Bd:"relevant",Cd:"required",Id:"selected",Kd:"setsize",Md:"sort",Zd:"valuemax",$d:"valuemin",ae:"valuenow",be:"valuetext"};var vc={mc:"alert",nc:"alertdialog",oc:"application",pc:"article",sc:"banner",uc:"button",vc:"checkbox",xc:"columnheader",yc:"combobox",zc:"complementary",Ac:"contentinfo",Cc:"definition",Ec:"dialog",Fc:"directory",Hc:"document",Lc:"form",Nc:"grid",Oc:"gridcell",Pc:"group",Rc:"heading",Tc:"img",Yc:"link",Zc:"list",$c:"listbox",ad:"listitem",cd:"log",dd:"main",ed:"marquee",fd:"math",gd:"menu",hd:"menubar",jd:"menuitem",kd:"menuitemcheckbox",ld:"menuitemradio",od:"navigation",pd:"note",qd:"option",
+ud:"presentation",wd:"progressbar",xd:"radio",yd:"radiogroup",Ad:"region",Dd:"row",Ed:"rowgroup",Fd:"rowheader",Gd:"scrollbar",Hd:"search",Jd:"separator",Ld:"slider",Nd:"spinbutton",Od:"status",Pd:"tab",Qd:"tablist",Rd:"tabpanel",Sd:"textbox",Td:"timer",Ud:"toolbar",Vd:"tooltip",Wd:"tree",Xd:"treegrid",Yd:"treeitem"};var wc=function(a,b){b?(r(La(vc,b),"No such ARIA role "+b),a.setAttribute("role",b)):a.removeAttribute("role")},yc=function(a,b,c){ea(c)&&(c=c.join(" "));var d=xc(b);""===c||void 0==c?(tc||(tc={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=tc,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d,
+c)},xc=function(a){r(a,"ARIA attribute cannot be empty.");r(La(uc,a),"No such ARIA attribute "+a);return"aria-"+a};var Bc=function(a,b,c,d,e){if(!(v||x&&z("525")))return!0;if(y&&e)return zc(a);if(e&&!d)return!1;"number"==typeof b&&(b=Ac(b));if(!c&&(17==b||18==b||y&&91==b))return!1;if(x&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(v&&d&&b==a)return!1;switch(a){case 13:return!(v&&v&&9<=hb);case 27:return!x}return zc(a)},zc=function(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||x&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;
+default:return!1}},Ac=function(a){if(w)a=Cc(a);else if(y&&x)t:switch(a){case 93:a=91;break t}return a},Cc=function(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};var L=function(a,b){H.call(this);a&&Dc(this,a,b)};p(L,H);f=L.prototype;f.d=null;f.Ea=null;f.Wa=null;f.Fa=null;f.r=-1;f.N=-1;f.ib=!1;
+var Ec={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},Fc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Gc=v||x&&z("525"),Hc=y&&w;
+L.prototype.Qb=function(a){x&&(17==this.r&&!a.ctrlKey||18==this.r&&!a.altKey||y&&91==this.r&&!a.metaKey)&&(this.N=this.r=-1);-1==this.r&&(a.ctrlKey&&17!=a.keyCode?this.r=17:a.altKey&&18!=a.keyCode?this.r=18:a.metaKey&&91!=a.keyCode&&(this.r=91));Gc&&!Bc(a.keyCode,this.r,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.N=Ac(a.keyCode),Hc&&(this.ib=a.altKey))};L.prototype.Rb=function(a){this.N=this.r=-1;this.ib=a.altKey};
+L.prototype.handleEvent=function(a){var b=a.O,c,d,e=b.altKey;v&&"keypress"==a.type?(c=this.N,d=13!=c&&27!=c?b.keyCode:0):x&&"keypress"==a.type?(c=this.N,d=0<=b.charCode&&63232>b.charCode&&zc(c)?b.charCode:0):Wa?(c=this.N,d=zc(c)?b.keyCode:0):(c=b.keyCode||this.N,d=b.charCode||0,Hc&&(e=this.ib),y&&63==d&&224==c&&(c=191));var g=c=Ac(c),h=b.keyIdentifier;c?63232<=c&&c in Ec?g=Ec[c]:25==c&&a.shiftKey&&(g=9):h&&h in Fc&&(g=Fc[h]);a=g==this.r;this.r=g;b=new Ic(g,d,a,b);b.altKey=e;this.dispatchEvent(b)};
+L.prototype.a=function(){return this.d};var Dc=function(a,b,c){a.Fa&&a.detach();a.d=b;a.Ea=E(a.d,"keypress",a,c);a.Wa=E(a.d,"keydown",a.Qb,c,a);a.Fa=E(a.d,"keyup",a.Rb,c,a)};L.prototype.detach=function(){this.Ea&&(F(this.Ea),F(this.Wa),F(this.Fa),this.Fa=this.Wa=this.Ea=null);this.d=null;this.N=this.r=-1};var Ic=function(a,b,c,d){B.call(this,d);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};p(Ic,B);var Jc=!!l.DOMTokenList,Kc=Jc?function(a){return a.classList}:function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},Lc=Jc?function(a,b){r(!!a.classList);return a.classList.contains(b)}:function(a,b){return t(Kc(a),b)},Mc=Jc?function(a,b){a.classList.add(b)}:function(a,b){Lc(a,b)||(a.className+=0<a.className.length?" "+b:b)},Nc=Jc?function(a,b){a.classList.remove(b)}:function(a,b){Lc(a,b)&&(a.className=Ea(Kc(a),function(a){return a!=b}).join(" "))};var Pc=function(a,b){if(!a)throw Error("Invalid class name "+a);if(!n(b))throw Error("Invalid decorator function "+b);Oc[a]=b},Qc={},Oc={};var N=function(){};ba(N);N.prototype.T=function(){};var Rc=function(a,b){a&&(a.tabIndex=b?0:-1)};f=N.prototype;f.o=function(a){return a.kb().o("div",this.ta(a).join(" "))};f.B=function(a){return a};f.Y=function(a){return"DIV"==a.tagName};f.K=function(a,b){b.id&&lc(a,b.id);var c=this.v(),d=!1,e=Kc(b);e&&Da(e,function(b){b==c?d=!0:b&&this.$a(a,b,c)},this);d||Mc(b,c);Sc(a,this.B(b));return b};
+f.$a=function(a,b,c){b==c+"-disabled"?a.qa(!1):b==c+"-horizontal"?Tc(a,"horizontal"):b==c+"-vertical"&&Tc(a,"vertical")};var Sc=function(a,b){if(b)for(var c=b.firstChild,d;c&&c.parentNode==b;){d=c.nextSibling;if(1==c.nodeType){var e;t:{var g=void 0;e=Kc(c);for(var h=0,k=e.length;h<k;h++)if(g=e[h],g=g in Oc?Oc[g]():null){e=g;break t}e=null}e&&(e.d=c,a.isEnabled()||e.qa(!1),a.Ca(e),e.K(c))}else c.nodeValue&&""!=qa(c.nodeValue)||b.removeChild(c);c=d}};
+N.prototype.Ma=function(a){a=a.a();r(a,"The container DOM element cannot be null.");gc(a,!0,w);v&&(a.hideFocus=!0);var b=this.T();b&&wc(a,b)};N.prototype.j=function(a){return a.a()};N.prototype.v=function(){return"goog-container"};N.prototype.ta=function(a){var b=this.v(),c=[b,"horizontal"==a.L?b+"-horizontal":b+"-vertical"];a.isEnabled()||c.push(b+"-disabled");return c};var O=function(){},Uc;ba(O);f=O.prototype;f.T=function(){};f.o=function(a){var b=a.kb().o("div",this.ta(a).join(" "),a.Ba);Vc(a,b);return b};f.B=function(a){return a};f.ra=function(a,b,c){if(a=a.a?a.a():a)if(v&&!z("7")){var d=Wc(kb(a),b);d.push(b);la(c?lb:nb,a).apply(null,d)}else c?lb(a,b):nb(a,b)};f.Y=function(){return!0};
+f.K=function(a,b){b.id&&lc(a,b.id);var c=this.B(b);c&&c.firstChild?Xc(a,c.firstChild.nextSibling?Ha(c.childNodes):c.firstChild):a.Ba=null;var d=0,e=this.v(),g=this.v(),h=!1,k=!1,c=!1,q=kb(b);Da(q,function(a){if(h||a!=e)if(k||a!=g){var b=d;this.tb||(this.Ha||Yc(this),this.tb=Na(this.Ha));a=parseInt(this.tb[a],10);d=b|(isNaN(a)?0:a)}else k=!0;else h=!0,g==e&&(k=!0)},this);a.g=d;h||(q.push(e),g==e&&(k=!0));k||q.push(g);var ha=a.G;ha&&q.push.apply(q,ha);if(v&&!z("7")){var M=Wc(q);0<M.length&&(q.push.apply(q,
+M),c=!0)}if(!h||!k||ha||c)b.className=q.join(" ");Vc(a,b);return b};f.Ma=function(a){rc(a)&&this.pa(a.a(),!0);a.isEnabled()&&this.na(a,a.s())};var Zc=function(a,b,c){if(a=c||a.T())r(b,"The element passed as a first parameter cannot be null."),wc(b,a)},Vc=function(a,b){r(a);r(b);a.s()||yc(b,"hidden",!a.s());a.isEnabled()||$c(b,1,!a.isEnabled());a.l&8&&$c(b,8,!!(a.g&8));a.l&16&&$c(b,16,!!(a.g&16));a.l&64&&$c(b,64,!!(a.g&64))};f=O.prototype;f.za=function(a,b){gc(a,!b,!v&&!Wa)};
+f.pa=function(a,b){this.ra(a,this.v()+"-rtl",b)};f.I=function(a){var b;return a.l&32&&(b=a.j())?Cb(b)&&Db(b):!1};f.na=function(a,b){var c;if(a.l&32&&(c=a.j())){if(!b&&a.g&32){try{c.blur()}catch(d){}a.g&32&&a.la(null)}(Cb(c)&&Db(c))!=b&&(b?c.tabIndex=0:(c.tabIndex=-1,c.removeAttribute("tabIndex")))}};f.ja=function(a,b){I(a,b);a&&yc(a,"hidden",!b)};f.t=function(a,b,c){var d=a.a();if(d){var e=ad(this,b);e&&this.ra(a,e,c);$c(d,b,c)}};
+var $c=function(a,b,c){Uc||(Uc={1:"disabled",8:"selected",16:"checked",64:"expanded"});if(b=Uc[b])r(a,"The element passed as a first parameter cannot be null."),yc(a,b,c)};O.prototype.j=function(a){return a.a()};O.prototype.v=function(){return"goog-control"};O.prototype.ta=function(a){var b=this.v(),c=[b],d=this.v();d!=b&&c.push(d);b=a.g;for(d=[];b;){var e=b&-b;d.push(ad(this,e));b&=~e}c.push.apply(c,d);(a=a.G)&&c.push.apply(c,a);v&&!z("7")&&c.push.apply(c,Wc(c));return c};
+var Wc=function(a,b){var c=[];b&&(a=a.concat([b]));Da([],function(d){!Fa(d,la(t,a))||b&&!t(d,b)||c.push(d.join("_"))});return c},ad=function(a,b){a.Ha||Yc(a);return a.Ha[b]},Yc=function(a){var b=a.v();a.Ha={1:b+"-disabled",2:b+"-hover",4:b+"-active",8:b+"-selected",16:b+"-checked",32:b+"-focused",64:b+"-open"}};var P=function(a,b,c){J.call(this,c);if(!b){b=this.constructor;for(var d;b;){d=ka(b);if(d=Qc[d])break;b=b.e?b.e.constructor:null}b=d?n(d.fa)?d.fa():new d:null}this.b=b;this.Ba=void 0!==a?a:null};p(P,J);f=P.prototype;f.Ba=null;f.g=0;f.l=39;f.cc=255;f.U=0;f.n=!0;f.G=null;f.$=!0;f.xa=!1;f.qb=null;f.ob=function(){return this.$};f.Na=function(a){this.f&&a!=this.$&&bd(this,a);this.$=a};f.j=function(){return this.b.j(this)};f.ya=function(){return this.ea||(this.ea=new L)};f.zb=function(){return this.b};
+f.ra=function(a,b){b?a&&(this.G?t(this.G,a)||this.G.push(a):this.G=[a],this.b.ra(this,a,!0)):a&&this.G&&Ga(this.G,a)&&(0==this.G.length&&(this.G=null),this.b.ra(this,a,!1))};f.o=function(){var a=this.b.o(this);this.d=a;Zc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.s()||this.b.ja(a,!1)};f.B=function(){return this.b.B(this.a())};f.Y=function(a){return this.b.Y(a)};f.Xa=function(a){this.d=a=this.b.K(this,a);Zc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.n="none"!=a.style.display};
+f.D=function(){P.e.D.call(this);this.b.Ma(this);if(this.l&-2&&(this.ob()&&bd(this,!0),this.l&32)){var a=this.j();if(a){var b=this.ya();Dc(b,a);mc(this).c(b,"key",this.J).c(a,"focus",this.ma).c(a,"blur",this.la)}}};
+var bd=function(a,b){var c=mc(a),d=a.a();b?(c.c(d,"mouseover",a.Qa).c(d,"mousedown",a.ka).c(d,"mouseup",a.Ra).c(d,"mouseout",a.Pa),a.oa!=aa&&c.c(d,"contextmenu",a.oa),v&&c.c(d,"dblclick",a.sb)):(c.u(d,"mouseover",a.Qa).u(d,"mousedown",a.ka).u(d,"mouseup",a.Ra).u(d,"mouseout",a.Pa),a.oa!=aa&&c.u(d,"contextmenu",a.oa),v&&c.u(d,"dblclick",a.sb))};P.prototype.ca=function(){P.e.ca.call(this);this.ea&&this.ea.detach();this.s()&&this.isEnabled()&&this.b.na(this,!1)};var Xc=function(a,b){a.Ba=b};f=P.prototype;
+f.pa=function(a){P.e.pa.call(this,a);var b=this.a();b&&this.b.pa(b,a)};f.za=function(a){this.xa=a;var b=this.a();b&&this.b.za(b,a)};f.s=function(){return this.n};f.ja=function(a,b){if(b||this.n!=a&&this.dispatchEvent(a?"show":"hide")){var c=this.a();c&&this.b.ja(c,a);this.isEnabled()&&this.b.na(this,a);this.n=a;return!0}return!1};f.isEnabled=function(){return!(this.g&1)};
+f.qa=function(a){var b=this.getParent();b&&"function"==typeof b.isEnabled&&!b.isEnabled()||!Q(this,1,!a)||(a||(this.setActive(!1),this.C(!1)),this.s()&&this.b.na(this,a),this.t(1,!a))};f.C=function(a){Q(this,2,a)&&this.t(2,a)};f.setActive=function(a){Q(this,4,a)&&this.t(4,a)};var cd=function(a,b){Q(a,8,b)&&a.t(8,b)},R=function(a,b){Q(a,64,b)&&a.t(64,b)};P.prototype.t=function(a,b){this.l&a&&b!=!!(this.g&a)&&(this.b.t(this,a,b),this.g=b?this.g|a:this.g&~a)};
+var dd=function(a,b,c){if(a.f&&a.g&b&&!c)throw Error("Component already rendered");!c&&a.g&b&&a.t(b,!1);a.l=c?a.l|b:a.l&~b},S=function(a,b){return!!(a.cc&b)&&!!(a.l&b)},Q=function(a,b,c){return!!(a.l&b)&&!!(a.g&b)!=c&&(!(a.U&b)||a.dispatchEvent(jc(b,c)))&&!a.Sb};f=P.prototype;f.Qa=function(a){(!a.relatedTarget||!yb(this.a(),a.relatedTarget))&&this.dispatchEvent("enter")&&this.isEnabled()&&S(this,2)&&this.C(!0)};
+f.Pa=function(a){a.relatedTarget&&yb(this.a(),a.relatedTarget)||!this.dispatchEvent("leave")||(S(this,4)&&this.setActive(!1),S(this,2)&&this.C(!1))};f.oa=aa;f.ka=function(a){this.isEnabled()&&(S(this,2)&&this.C(!0),!Lb(a)||x&&y&&a.ctrlKey||(S(this,4)&&this.setActive(!0),this.b.I(this)&&this.j().focus()));this.xa||!Lb(a)||x&&y&&a.ctrlKey||a.preventDefault()};f.Ra=function(a){this.isEnabled()&&(S(this,2)&&this.C(!0),this.g&4&&ed(this,a)&&S(this,4)&&this.setActive(!1))};
+f.sb=function(a){this.isEnabled()&&ed(this,a)};var ed=function(a,b){if(S(a,16)){var c=!(a.g&16);Q(a,16,c)&&a.t(16,c)}S(a,8)&&cd(a,!0);S(a,64)&&R(a,!(a.g&64));c=new A("action",a);b&&(c.altKey=b.altKey,c.ctrlKey=b.ctrlKey,c.metaKey=b.metaKey,c.shiftKey=b.shiftKey,c.hb=b.hb);return a.dispatchEvent(c)};P.prototype.ma=function(){S(this,32)&&Q(this,32,!0)&&this.t(32,!0)};P.prototype.la=function(){S(this,4)&&this.setActive(!1);S(this,32)&&Q(this,32,!1)&&this.t(32,!1)};
+P.prototype.J=function(a){return this.s()&&this.isEnabled()&&this.lb(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};P.prototype.lb=function(a){return 13==a.keyCode&&ed(this,a)};if(!n(P))throw Error("Invalid component class "+P);if(!n(O))throw Error("Invalid renderer class "+O);var fd=ka(P);Qc[fd]=O;Pc("goog-control",function(){return new P(null)});var T=function(a,b,c){J.call(this,c);this.b=b||N.fa();this.L=a||"vertical"};p(T,J);f=T.prototype;f.ub=null;f.ea=null;f.b=null;f.L=null;f.n=!0;f.V=!0;f.Ya=!0;f.h=-1;f.i=null;f.ba=!1;f.Pb=!1;f.Ob=!0;f.M=null;f.j=function(){return this.ub||this.b.j(this)};f.ya=function(){return this.ea||(this.ea=new L(this.j()))};f.zb=function(){return this.b};f.o=function(){this.d=this.b.o(this)};f.B=function(){return this.b.B(this.a())};f.Y=function(a){return this.b.Y(a)};
+f.Xa=function(a){this.d=this.b.K(this,a);"none"==a.style.display&&(this.n=!1)};f.D=function(){T.e.D.call(this);pc(this,function(a){a.f&&gd(this,a)},this);var a=this.a();this.b.Ma(this);this.ja(this.n,!0);mc(this).c(this,"enter",this.Ib).c(this,"highlight",this.Jb).c(this,"unhighlight",this.Lb).c(this,"open",this.Kb).c(this,"close",this.Gb).c(a,"mousedown",this.ka).c(pb(a),"mouseup",this.Hb).c(a,["mousedown","mouseup","mouseover","mouseout","contextmenu"],this.Fb);this.I()&&hd(this,!0)};
+var hd=function(a,b){var c=mc(a),d=a.j();b?c.c(d,"focus",a.ma).c(d,"blur",a.la).c(a.ya(),"key",a.J):c.u(d,"focus",a.ma).u(d,"blur",a.la).u(a.ya(),"key",a.J)};f=T.prototype;f.ca=function(){id(this,-1);this.i&&R(this.i,!1);this.ba=!1;T.e.ca.call(this)};f.Ib=function(){return!0};
+f.Jb=function(a){var b=sc(this,a.target);if(-1<b&&b!=this.h){var c=K(this,this.h);c&&c.C(!1);this.h=b;c=K(this,this.h);this.ba&&c.setActive(!0);this.Ob&&this.i&&c!=this.i&&(c.l&64?R(c,!0):R(this.i,!1))}b=this.a();r(b,"The DOM element for the container cannot be null.");null!=a.target.a()&&yc(b,"activedescendant",a.target.a().id)};f.Lb=function(a){a.target==K(this,this.h)&&(this.h=-1);a=this.a();r(a,"The DOM element for the container cannot be null.");a.removeAttribute(xc("activedescendant"))};
+f.Kb=function(a){(a=a.target)&&a!=this.i&&a.getParent()==this&&(this.i&&R(this.i,!1),this.i=a)};f.Gb=function(a){a.target==this.i&&(this.i=null)};f.ka=function(a){this.V&&(this.ba=!0);var b=this.j();b&&Cb(b)&&Db(b)?b.focus():a.preventDefault()};f.Hb=function(){this.ba=!1};
+f.Fb=function(a){var b;t:{b=a.target;if(this.M)for(var c=this.a();b&&b!==c;){var d=b.id;if(d in this.M){b=this.M[d];break t}b=b.parentNode}b=null}if(b)switch(a.type){case "mousedown":b.ka(a);break;case "mouseup":b.Ra(a);break;case "mouseover":b.Qa(a);break;case "mouseout":b.Pa(a);break;case "contextmenu":b.oa(a)}};f.ma=function(){};f.la=function(){id(this,-1);this.ba=!1;this.i&&R(this.i,!1)};
+f.J=function(a){return this.isEnabled()&&this.s()&&(0!=qc(this)||this.ub)&&this.lb(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};
+f.lb=function(a){var b=K(this,this.h);if(b&&"function"==typeof b.J&&b.J(a)||this.i&&this.i!=b&&"function"==typeof this.i.J&&this.i.J(a))return!0;if(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)return!1;switch(a.keyCode){case 27:if(this.I())this.j().blur();else return!1;break;case 36:jd(this);break;case 35:kd(this);break;case 38:if("vertical"==this.L)ld(this);else return!1;break;case 37:if("horizontal"==this.L)rc(this)?md(this):ld(this);else return!1;break;case 40:if("vertical"==this.L)md(this);else return!1;
+break;case 39:if("horizontal"==this.L)rc(this)?ld(this):md(this);else return!1;break;default:return!1}return!0};var gd=function(a,b){var c=b.a(),c=c.id||(c.id=kc(b));a.M||(a.M={});a.M[c]=b};T.prototype.Ca=function(a,b){Ba(a,P,"The child of a container must be a control");T.e.Ca.call(this,a,b)};T.prototype.Ta=function(a,b,c){a.U|=2;a.U|=64;!this.I()&&this.Pb||dd(a,32,!1);a.Na(!1);T.e.Ta.call(this,a,b,c);a.f&&this.f&&gd(this,a);b<=this.h&&this.h++};
+T.prototype.removeChild=function(a,b){if(a=m(a)?nc(this,a):a){var c=sc(this,a);-1!=c&&(c==this.h?(a.C(!1),this.h=-1):c<this.h&&this.h--);var d=a.a();d&&d.id&&this.M&&(c=this.M,d=d.id,d in c&&delete c[d])}a=T.e.removeChild.call(this,a,b);a.Na(!0);return a};var Tc=function(a,b){if(a.a())throw Error("Component already rendered");a.L=b};f=T.prototype;f.s=function(){return this.n};
+f.ja=function(a,b){if(b||this.n!=a&&this.dispatchEvent(a?"show":"hide")){this.n=a;var c=this.a();c&&(I(c,a),this.I()&&Rc(this.j(),this.V&&this.n),b||this.dispatchEvent(this.n?"aftershow":"afterhide"));return!0}return!1};f.isEnabled=function(){return this.V};f.qa=function(a){this.V!=a&&this.dispatchEvent(a?"enable":"disable")&&(a?(this.V=!0,pc(this,function(a){a.vb?delete a.vb:a.qa(!0)})):(pc(this,function(a){a.isEnabled()?a.qa(!1):a.vb=!0}),this.ba=this.V=!1),this.I()&&Rc(this.j(),a&&this.n))};
+f.I=function(){return this.Ya};f.na=function(a){a!=this.Ya&&this.f&&hd(this,a);this.Ya=a;this.V&&this.n&&Rc(this.j(),a)};var id=function(a,b){var c=K(a,b);c?c.C(!0):-1<a.h&&K(a,a.h).C(!1)};T.prototype.C=function(a){id(this,sc(this,a))};
+var jd=function(a){nd(a,function(a,c){return(a+1)%c},qc(a)-1)},kd=function(a){nd(a,function(a,c){a--;return 0>a?c-1:a},0)},md=function(a){nd(a,function(a,c){return(a+1)%c},a.h)},ld=function(a){nd(a,function(a,c){a--;return 0>a?c-1:a},a.h)},nd=function(a,b,c){c=0>c?sc(a,a.i):c;var d=qc(a);c=b.call(a,c,d);for(var e=0;e<=d;){var g=K(a,c);if(g&&g.s()&&g.isEnabled()&&g.l&2){a.Ua(c);break}e++;c=b.call(a,c,d)}};T.prototype.Ua=function(a){id(this,a)};var U=function(){};p(U,O);ba(U);f=U.prototype;f.v=function(){return"goog-tab"};f.T=function(){return"tab"};f.o=function(a){var b=U.e.o.call(this,a);(a=a.Sa())&&this.Va(b,a);return b};f.K=function(a,b){b=U.e.K.call(this,a,b);var c=this.Sa(b);c&&(a.rb=c);a.g&8&&(c=a.getParent())&&n(c.W)&&(a.t(8,!1),c.W(a));return b};f.Sa=function(a){return a.title||""};f.Va=function(a,b){a&&(a.title=b||"")};var od=function(a,b,c){P.call(this,a,b||U.fa(),c);dd(this,8,!0);this.U|=9};p(od,P);od.prototype.Sa=function(){return this.rb};od.prototype.Va=function(a){this.zb().Va(this.a(),a);this.rb=a};Pc("goog-tab",function(){return new od(null)});var V=function(){};p(V,N);ba(V);V.prototype.v=function(){return"goog-tab-bar"};V.prototype.T=function(){return"tablist"};V.prototype.$a=function(a,b,c){this.Ab||(this.Ja||pd(this),this.Ab=Na(this.Ja));var d=this.Ab[b];d?(Tc(a,qd(d)),a.wb=d):V.e.$a.call(this,a,b,c)};V.prototype.ta=function(a){var b=V.e.ta.call(this,a);this.Ja||pd(this);b.push(this.Ja[a.wb]);return b};var pd=function(a){var b=a.v();a.Ja={top:b+"-top",bottom:b+"-bottom",start:b+"-start",end:b+"-end"}};var W=function(a,b,c){a=a||"top";Tc(this,qd(a));this.wb=a;T.call(this,this.L,b||V.fa(),c);rd(this)};p(W,T);f=W.prototype;f.Zb=!0;f.H=null;f.D=function(){W.e.D.call(this);rd(this)};f.removeChild=function(a,b){sd(this,a);return W.e.removeChild.call(this,a,b)};f.Ua=function(a){W.e.Ua.call(this,a);this.Zb&&this.W(K(this,a))};f.W=function(a){a?cd(a,!0):this.H&&cd(this.H,!1)};
+var sd=function(a,b){if(b&&b==a.H){for(var c=sc(a,b),d=c-1;b=K(a,d);d--)if(b.s()&&b.isEnabled()){a.W(b);return}for(c+=1;b=K(a,c);c++)if(b.s()&&b.isEnabled()){a.W(b);return}a.W(null)}};f=W.prototype;f.Xb=function(a){this.H&&this.H!=a.target&&cd(this.H,!1);this.H=a.target};f.Yb=function(a){a.target==this.H&&(this.H=null)};f.Vb=function(a){sd(this,a.target)};f.Wb=function(a){sd(this,a.target)};f.ma=function(){K(this,this.h)||this.C(this.H||K(this,0))};
+var rd=function(a){mc(a).c(a,"select",a.Xb).c(a,"unselect",a.Yb).c(a,"disable",a.Vb).c(a,"hide",a.Wb)},qd=function(a){return"start"==a||"end"==a?"vertical":"horizontal"};Pc("goog-tab-bar",function(){return new W});var X=function(a,b,c,d,e){function g(a){a&&(a.tabIndex=0,wc(a,h.T()),Mc(a,"goog-zippy-header"),td(h,a),a&&h.Mb.c(a,"keydown",h.Nb))}H.call(this);this.A=e||qb();this.R=this.A.a(a)||null;this.Aa=this.A.a(d||null);this.da=(this.Oa=n(b)?b:null)||!b?null:this.A.a(b);this.k=!0==c;this.Mb=new G(this);this.pb=new G(this);var h=this;g(this.R);g(this.Aa);this.S(this.k)};p(X,H);f=X.prototype;f.$=!0;f.T=function(){return"tab"};f.B=function(){return this.da};f.toggle=function(){this.S(!this.k)};
+f.S=function(a){this.da?I(this.da,a):a&&this.Oa&&(this.da=this.Oa());this.da&&Mc(this.da,"goog-zippy-content");if(this.Aa)I(this.R,!a),I(this.Aa,a);else if(this.R){var b=this.R;a?Mc(b,"goog-zippy-expanded"):Nc(b,"goog-zippy-expanded");b=this.R;a?Nc(b,"goog-zippy-collapsed"):Mc(b,"goog-zippy-collapsed");yc(this.R,"expanded",a)}this.k=a;this.dispatchEvent(new ud("toggle",this))};f.ob=function(){return this.$};f.Na=function(a){this.$!=a&&((this.$=a)?(td(this,this.R),td(this,this.Aa)):this.pb.Za())};
+var td=function(a,b){b&&a.pb.c(b,"click",a.$b)};X.prototype.Nb=function(a){if(13==a.keyCode||32==a.keyCode)this.toggle(),this.dispatchEvent(new A("action",this)),a.preventDefault(),a.stopPropagation()};X.prototype.$b=function(){this.toggle();this.dispatchEvent(new A("action",this))};var ud=function(a,b){A.call(this,a,b)};p(ud,A);var Z=function(a,b){this.nb=[];for(var c=sb("span","ae-zippy",rb(document,a)),d=0,e;e=c[d];d++){var g=e.parentNode.parentNode.parentNode;if(void 0!=g.nextElementSibling)g=g.nextElementSibling;else for(g=g.nextSibling;g&&1!=g.nodeType;)g=g.nextSibling;e=new X(e,g,!1);this.nb.push(e)}this.fc=new Y(this.nb,rb(document,b))};Z.prototype.ic=function(){return this.fc};Z.prototype.jc=function(){return this.nb};
+var Y=function(a,b){this.ua=a;if(this.ua.length)for(var c=0,d;d=this.ua[c];c++)E(d,"toggle",this.Ub,!1,this);this.Ka=0;this.k=!1;c="ae-toggle ae-plus ae-action";this.ua.length||(c+=" ae-disabled");this.P=wb("span",{className:c},"Expand All");E(this.P,"click",this.Tb,!1,this);b&&b.appendChild(this.P)};Y.prototype.Tb=function(){this.ua.length&&this.S(!this.k)};
+Y.prototype.Ub=function(a){a=a.currentTarget;this.Ka=a.k?this.Ka+1:this.Ka-1;a.k!=this.k&&(a.k?(this.k=!0,vd(this,!0)):0==this.Ka&&(this.k=!1,vd(this,!1)))};Y.prototype.S=function(a){this.k=a;a=0;for(var b;b=this.ua[a];a++)b.k!=this.k&&b.S(this.k);vd(this)};
+var vd=function(a,b){(void 0!==b?b:a.k)?(nb(a.P,"ae-plus"),lb(a.P,"ae-minus"),zb(a.P,"Collapse All")):(nb(a.P,"ae-minus"),lb(a.P,"ae-plus"),zb(a.P,"Expand All"))},wd=function(a){this.ac=a;this.Cb={};var b,c=wb("div",{},b=wb("div",{id:"ae-stats-details-tabs",className:"goog-tab-bar goog-tab-bar-top"}),wb("div",{className:"goog-tab-bar-clear"}),a=wb("div",{id:"ae-stats-details-tabs-content",className:"goog-tab-content"})),d=new W;d.K(b);E(d,"select",this.Bb,!1,this);E(d,"unselect",this.Bb,!1,this);
+b=0;for(var e;e=this.ac[b];b++)if(e=rb(document,"ae-stats-details-"+e)){var g=sb("h2",null,e)[0],h;h=g;var k=void 0;jb&&"innerText"in h?k=h.innerText.replace(/(\r\n|\r|\n)/g,"\n"):(k=[],Eb(h,k,!0),k=k.join(""));k=k.replace(/ \xAD /g," ").replace(/\xAD/g,"");k=k.replace(/\u200B/g,"");jb||(k=k.replace(/ +/g," "));" "!=k&&(k=k.replace(/^\s*/,""));h=k;g&&g.parentNode&&g.parentNode.removeChild(g);g=new od(h);this.Cb[ka(g)]=e;d.Ca(g,!0);a.appendChild(e);0==b?d.W(g):I(e,!1)}rb(document,"bd").appendChild(c)};
+wd.prototype.Bb=function(a){var b=this.Cb[ka(a.target)];I(b,"select"==a.type)};ma("ae.Stats.Details.Tabs",wd);ma("goog.ui.Zippy",X);X.prototype.setExpanded=X.prototype.S;ma("ae.Stats.MakeZippys",Z);Z.prototype.getExpandCollapse=Z.prototype.ic;Z.prototype.getZippys=Z.prototype.jc;Y.prototype.setExpanded=Y.prototype.S;var $=function(){this.ab=[];this.gb=[]},xd=[[5,0.2,1],[6,0.2,1.2],[5,0.25,1.25],[6,0.25,1.5],[4,0.5,2],[5,0.5,2.5],[6,0.5,3],[4,1,4],[5,1,5],[6,1,6],[4,2,8],[5,2,10]],yd=function(a){if(0>=a)return[2,0.5,1];for(var b=1;1>a;)a*=10,b/=10;for(;10<=a;)a/=10,b*=10;for(var c=0;c<xd.length;c++)if(a<=xd[c][2])return[xd[c][0],xd[c][1]*b,xd[c][2]*b];return[5,2*b,10*b]};$.prototype.fb="stats/static/pix.gif";$.prototype.w="ae-stats-gantt-";$.prototype.cb=0;$.prototype.write=function(a){this.gb.push(a)};
+var zd=function(a,b,c,d){a.write('<tr class="'+a.w+'axisrow"><td width="20%"></td><td>');a.write('<div class="'+a.w+'axis">');for(var e=0;e<=b;e++)a.write('<img class="'+a.w+'tick" src="'+a.fb+'" alt="" '),a.write('style="left:'+e*c*d+'%"\n>'),a.write('<span class="'+a.w+'scale" style="left:'+e*c*d+'%">'),a.write("&nbsp;"+e*c+"</span>");a.write("</div></td></tr>\n")};
+$.prototype.hc=function(){this.gb=[];var a=yd(this.cb),b=a[0],c=a[1],a=100/a[2];this.write('<table class="'+this.w+'table">\n');zd(this,b,c,a);for(var d=0;d<this.ab.length;d++){var e=this.ab[d];this.write('<tr class="'+this.w+'datarow"><td width="20%">');0<e.label.length&&(0<e.ia.length&&this.write('<a class="'+this.w+'link" href="'+e.ia+'">'),this.write(e.label),0<e.ia.length&&this.write("</a>"));this.write("</td>\n<td>");this.write('<div class="'+this.w+'container">');0<e.ia.length&&this.write('<a class="'+
+this.w+'link" href="'+e.ia+'"\n>');this.write('<img class="'+this.w+'bar" src="'+this.fb+'" alt="" ');this.write('style="left:'+e.start*a+"%;width:"+e.duration*a+'%;min-width:1px"\n>');0<e.bb&&(this.write('<img class="'+this.w+'extra" src="'+this.fb+'" alt="" '),this.write('style="left:'+e.start*a+"%;width:"+e.bb*a+'%"\n>'));0<e.yb.length&&(this.write('<span class="'+this.w+'inline" style="left:'+(e.start+Math.max(e.duration,e.bb))*a+'%">&nbsp;'),this.write(e.yb),this.write("</span>"));0<e.ia.length&&
+this.write("</a>");this.write("</div></td></tr>\n")}zd(this,b,c,a);this.write("</table>\n");return this.gb.join("")};$.prototype.gc=function(a,b,c,d,e,g){this.cb=Math.max(this.cb,Math.max(b+c,b+d));this.ab.push({label:a,start:b,duration:c,bb:d,yb:e,ia:g})};ma("Gantt",$);$.prototype.add_bar=$.prototype.gc;$.prototype.draw=$.prototype.hc;})();
diff --git a/google/appengine/ext/cloudstorage/cloudstorage_stub.py b/google/appengine/ext/cloudstorage/cloudstorage_stub.py
index a1fbf01..2332cae 100644
--- a/google/appengine/ext/cloudstorage/cloudstorage_stub.py
+++ b/google/appengine/ext/cloudstorage/cloudstorage_stub.py
@@ -35,6 +35,11 @@
 _GCS_DEFAULT_CONTENT_TYPE = 'binary/octet-stream'
 
 
+
+
+
+
+
 class _AE_GCSFileInfo_(db.Model):
   """Store GCS specific info.
 
@@ -288,14 +293,17 @@
       namespace_manager.set_namespace(ns)
 
   @db.non_transactional
-  def put_copy(self, src, dst):
+  def put_copy(self, src, dst, options):
     """Copy file from src to dst.
 
     Metadata is copied.
 
     Args:
       src: /bucket/filename. This file must exist.
-      dst: /bucket/filename
+      dst: /bucket/filename.
+      options: a dict containing all user specified request headers.
+        e.g. {'content-type': 'foo', 'x-goog-meta-bar': 'bar'}. If None,
+        old metadata is copied.
     """
     common.validate_file_path(src)
     common.validate_file_path(dst)
@@ -310,17 +318,22 @@
       new_file = _AE_GCSFileInfo_(key_name=token,
                                   filename=dst,
                                   finalized=True)
-      new_file.options = source.options
+      if options:
+        new_file.options = options
+      else:
+        new_file.options = source.options
       new_file.etag = source.etag
       new_file.size = source.size
-      new_file.creation = datetime.datetime.utcnow()
+      new_file.creation = source.creation
       new_file.put()
     finally:
       namespace_manager.set_namespace(ns)
 
 
-    local_file = self.blob_storage.OpenBlob(src_blobkey)
-    self.blob_storage.StoreBlob(token, local_file)
+    if src_blobkey != token:
+
+      local_file = self.blob_storage.OpenBlob(src_blobkey)
+      self.blob_storage.StoreBlob(token, local_file)
 
   @db.non_transactional
   def _end_creation(self, token, _upload_filename):
@@ -481,12 +494,14 @@
                                           is_dir=True))
           continue
 
-      first = False
-      result.add(common.GCSFileStat(
-          filename=name,
-          st_size=info.size,
-          st_ctime=calendar.timegm(info.creation.utctimetuple()),
-          etag=info.etag))
+
+      if info.finalized:
+        first = False
+        result.add(common.GCSFileStat(
+            filename=name,
+            st_size=info.size,
+            st_ctime=calendar.timegm(info.creation.utctimetuple()),
+            etag=info.etag))
 
     def is_truncated():
       """Check if there are more results satisfying the query."""
diff --git a/google/appengine/ext/cloudstorage/common.py b/google/appengine/ext/cloudstorage/common.py
index 68bef83..e72b8ab 100644
--- a/google/appengine/ext/cloudstorage/common.py
+++ b/google/appengine/ext/cloudstorage/common.py
@@ -316,7 +316,7 @@
       raise ValueError('option %s is not supported.' % k)
     if not isinstance(v, basestring):
       raise TypeError('value %r for option %s should be of type basestring.' %
-                      v, k)
+                      (v, k))
 
 
 def http_time_to_posix(http_time):
diff --git a/google/appengine/ext/cloudstorage/stub_dispatcher.py b/google/appengine/ext/cloudstorage/stub_dispatcher.py
index 1f946b8..54da2b1 100644
--- a/google/appengine/ext/cloudstorage/stub_dispatcher.py
+++ b/google/appengine/ext/cloudstorage/stub_dispatcher.py
@@ -16,6 +16,8 @@
 #
 """Dispatcher to handle Google Cloud Storage stub requests."""
 
+from __future__ import with_statement
+
 
 
 
@@ -26,6 +28,7 @@
 
 import httplib
 import re
+import threading
 import urllib
 import urlparse
 import xml.etree.ElementTree as ET
@@ -35,6 +38,11 @@
 from google.appengine.ext.cloudstorage import common
 
 
+BUCKET_ONLY_PATH = re.compile('(/[a-z0-9-_.]+)/?$')
+
+GCS_STUB_LOCK = threading.RLock()
+
+
 class _FakeUrlFetchResult(object):
   def __init__(self, status, headers, content):
     self.status_code = status
@@ -68,18 +76,19 @@
   gcs_stub = cloudstorage_stub.CloudStorageStub(
       apiproxy_stub_map.apiproxy.GetStub('blobstore').storage)
 
-  if method == 'POST':
-    return _handle_post(gcs_stub, filename, headers)
-  elif method == 'PUT':
-    return _handle_put(gcs_stub, filename, param_dict, headers, payload)
-  elif method == 'GET':
-    return _handle_get(gcs_stub, filename, param_dict, headers)
-  elif method == 'HEAD':
-    return _handle_head(gcs_stub, filename)
-  elif method == 'DELETE':
-    return _handle_delete(gcs_stub, filename)
-  raise ValueError('Unrecognized request method %r.' % method,
-                   httplib.METHOD_NOT_ALLOWED)
+  with GCS_STUB_LOCK:
+    if method == 'POST':
+      return _handle_post(gcs_stub, filename, headers)
+    elif method == 'PUT':
+      return _handle_put(gcs_stub, filename, param_dict, headers, payload)
+    elif method == 'GET':
+      return _handle_get(gcs_stub, filename, param_dict, headers)
+    elif method == 'HEAD':
+      return _handle_head(gcs_stub, filename)
+    elif method == 'DELETE':
+      return _handle_delete(gcs_stub, filename)
+    raise ValueError('Unrecognized request method %r.' % method,
+                     httplib.METHOD_NOT_ALLOWED)
 
 
 def _preprocess(method, headers, url):
@@ -153,6 +162,13 @@
 
 
 
+
+  if (headers.get('x-goog-if-generation-match', None) == '0' and
+      gcs_stub.head_object(filename) is not None):
+    return _FakeUrlFetchResult(httplib.PRECONDITION_FAILED, {}, '')
+
+
+
   if not token:
 
     if content_range.length is None:
@@ -222,15 +238,20 @@
   result = _handle_head(gcs_stub, source)
   if result.status_code == httplib.NOT_FOUND:
     return result
-  gcs_stub.put_copy(source, filename)
+  directive = headers.pop('x-goog-metadata-directive', 'COPY')
+  if directive == 'REPLACE':
+    gcs_stub.put_copy(source, filename, headers)
+  else:
+    gcs_stub.put_copy(source, filename, None)
   return _FakeUrlFetchResult(httplib.OK, {}, '')
 
 
 def _handle_get(gcs_stub, filename, param_dict, headers):
   """Handle GET object and GET bucket."""
-  if filename.rfind('/') == 0:
+  mo = re.match(BUCKET_ONLY_PATH, filename)
+  if mo is not None:
 
-    return _handle_get_bucket(gcs_stub, filename, param_dict)
+    return _handle_get_bucket(gcs_stub, mo.group(1), param_dict)
   else:
 
     result = _handle_head(gcs_stub, filename)
diff --git a/google/appengine/ext/datastore_admin/backup_handler.py b/google/appengine/ext/datastore_admin/backup_handler.py
index 1a6cca5..8c23f76 100644
--- a/google/appengine/ext/datastore_admin/backup_handler.py
+++ b/google/appengine/ext/datastore_admin/backup_handler.py
@@ -70,7 +70,7 @@
 from google.appengine.ext.mapreduce import context
 from google.appengine.ext.mapreduce import datastore_range_iterators as db_iters
 from google.appengine.ext.mapreduce import input_readers
-from google.appengine.ext.mapreduce import model
+from google.appengine.ext.mapreduce import json_util
 from google.appengine.ext.mapreduce import operation as op
 from google.appengine.ext.mapreduce import output_writers
 from google.appengine.runtime import apiproxy_errors
@@ -1206,7 +1206,7 @@
       f.close(finalize=True)
 
 
-class PropertyTypeInfo(model.JsonMixin):
+class PropertyTypeInfo(json_util.JsonMixin):
   """Type information for an entity property."""
 
   def __init__(self, name, is_repeated=False, primitive_types=None,
@@ -1317,7 +1317,7 @@
                 in json.get('embedded_entities')])
 
 
-class EntityTypeInfo(model.JsonMixin):
+class EntityTypeInfo(json_util.JsonMixin):
   """Type information for an entity."""
 
   def __init__(self, kind=None, properties=None):
@@ -1459,7 +1459,7 @@
   using the model put method.
   """
 
-  entity_type_info = model.JsonProperty(
+  entity_type_info = json_util.JsonProperty(
       EntityTypeInfo, default=EntityTypeInfo(), indexed=False)
   is_partial = db.BooleanProperty(default=False)
 
diff --git a/google/appengine/ext/datastore_admin/backup_pb2.py b/google/appengine/ext/datastore_admin/backup_pb2.py
index eb91617..5d4b8d5 100644
--- a/google/appengine/ext/datastore_admin/backup_pb2.py
+++ b/google/appengine/ext/datastore_admin/backup_pb2.py
@@ -17,6 +17,8 @@
 
 
 
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.net.proto2.python.public import descriptor as _descriptor
 from google.net.proto2.python.public import message as _message
 from google.net.proto2.python.public import reflection as _reflection
@@ -29,7 +31,8 @@
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='apphosting/ext/datastore_admin/backup.proto',
   package='apphosting.ext.datastore_admin',
-  serialized_pb='\n+apphosting/ext/datastore_admin/backup.proto\x12\x1e\x61pphosting.ext.datastore_admin\"\x8c\x01\n\x06\x42\x61\x63kup\x12?\n\x0b\x62\x61\x63kup_info\x18\x01 \x01(\x0b\x32*.apphosting.ext.datastore_admin.BackupInfo\x12\x41\n\tkind_info\x18\x02 \x03(\x0b\x32..apphosting.ext.datastore_admin.KindBackupInfo\"Q\n\nBackupInfo\x12\x13\n\x0b\x62\x61\x63kup_name\x18\x01 \x01(\t\x12\x17\n\x0fstart_timestamp\x18\x02 \x01(\x03\x12\x15\n\rend_timestamp\x18\x03 \x01(\x03\"\x8c\x01\n\x0eKindBackupInfo\x12\x0c\n\x04kind\x18\x01 \x02(\t\x12\x0c\n\x04\x66ile\x18\x02 \x03(\t\x12\x43\n\rentity_schema\x18\x03 \x01(\x0b\x32,.apphosting.ext.datastore_admin.EntitySchema\x12\x19\n\nis_partial\x18\x04 \x01(\x08:\x05\x66\x61lse\"\xfc\x04\n\x0c\x45ntitySchema\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x41\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x32.apphosting.ext.datastore_admin.EntitySchema.Field\x1a\xb2\x01\n\x04Type\x12\x0f\n\x07is_list\x18\x01 \x01(\x08\x12R\n\x0eprimitive_type\x18\x02 \x03(\x0e\x32:.apphosting.ext.datastore_admin.EntitySchema.PrimitiveType\x12\x45\n\x0f\x65mbedded_schema\x18\x03 \x03(\x0b\x32,.apphosting.ext.datastore_admin.EntitySchema\x1aV\n\x05\x46ield\x12\x0c\n\x04name\x18\x01 \x02(\t\x12?\n\x04type\x18\x02 \x03(\x0b\x32\x31.apphosting.ext.datastore_admin.EntitySchema.Type\"\x8d\x02\n\rPrimitiveType\x12\t\n\x05\x46LOAT\x10\x00\x12\x0b\n\x07INTEGER\x10\x01\x12\x0b\n\x07\x42OOLEAN\x10\x02\x12\n\n\x06STRING\x10\x03\x12\r\n\tDATE_TIME\x10\x04\x12\n\n\x06RATING\x10\x05\x12\x08\n\x04LINK\x10\x06\x12\x0c\n\x08\x43\x41TEGORY\x10\x07\x12\x10\n\x0cPHONE_NUMBER\x10\x08\x12\x12\n\x0ePOSTAL_ADDRESS\x10\t\x12\t\n\x05\x45MAIL\x10\n\x12\r\n\tIM_HANDLE\x10\x0b\x12\x0c\n\x08\x42LOB_KEY\x10\x0c\x12\x08\n\x04TEXT\x10\r\x12\x08\n\x04\x42LOB\x10\x0e\x12\x0e\n\nSHORT_BLOB\x10\x0f\x12\x08\n\x04USER\x10\x10\x12\r\n\tGEO_POINT\x10\x11\x12\r\n\tREFERENCE\x10\x12\x42\x14\x10\x02 \x02(\x02\x42\x0c\x42\x61\x63kupProtos')
+  serialized_pb=_b('\n+apphosting/ext/datastore_admin/backup.proto\x12\x1e\x61pphosting.ext.datastore_admin\"\x8c\x01\n\x06\x42\x61\x63kup\x12?\n\x0b\x62\x61\x63kup_info\x18\x01 \x01(\x0b\x32*.apphosting.ext.datastore_admin.BackupInfo\x12\x41\n\tkind_info\x18\x02 \x03(\x0b\x32..apphosting.ext.datastore_admin.KindBackupInfo\"Q\n\nBackupInfo\x12\x13\n\x0b\x62\x61\x63kup_name\x18\x01 \x01(\t\x12\x17\n\x0fstart_timestamp\x18\x02 \x01(\x03\x12\x15\n\rend_timestamp\x18\x03 \x01(\x03\"\x8c\x01\n\x0eKindBackupInfo\x12\x0c\n\x04kind\x18\x01 \x02(\t\x12\x0c\n\x04\x66ile\x18\x02 \x03(\t\x12\x43\n\rentity_schema\x18\x03 \x01(\x0b\x32,.apphosting.ext.datastore_admin.EntitySchema\x12\x19\n\nis_partial\x18\x04 \x01(\x08:\x05\x66\x61lse\"\xfc\x04\n\x0c\x45ntitySchema\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x41\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x32.apphosting.ext.datastore_admin.EntitySchema.Field\x1a\xb2\x01\n\x04Type\x12\x0f\n\x07is_list\x18\x01 \x01(\x08\x12R\n\x0eprimitive_type\x18\x02 \x03(\x0e\x32:.apphosting.ext.datastore_admin.EntitySchema.PrimitiveType\x12\x45\n\x0f\x65mbedded_schema\x18\x03 \x03(\x0b\x32,.apphosting.ext.datastore_admin.EntitySchema\x1aV\n\x05\x46ield\x12\x0c\n\x04name\x18\x01 \x02(\t\x12?\n\x04type\x18\x02 \x03(\x0b\x32\x31.apphosting.ext.datastore_admin.EntitySchema.Type\"\x8d\x02\n\rPrimitiveType\x12\t\n\x05\x46LOAT\x10\x00\x12\x0b\n\x07INTEGER\x10\x01\x12\x0b\n\x07\x42OOLEAN\x10\x02\x12\n\n\x06STRING\x10\x03\x12\r\n\tDATE_TIME\x10\x04\x12\n\n\x06RATING\x10\x05\x12\x08\n\x04LINK\x10\x06\x12\x0c\n\x08\x43\x41TEGORY\x10\x07\x12\x10\n\x0cPHONE_NUMBER\x10\x08\x12\x12\n\x0ePOSTAL_ADDRESS\x10\t\x12\t\n\x05\x45MAIL\x10\n\x12\r\n\tIM_HANDLE\x10\x0b\x12\x0c\n\x08\x42LOB_KEY\x10\x0c\x12\x08\n\x04TEXT\x10\r\x12\x08\n\x04\x42LOB\x10\x0e\x12\x0e\n\nSHORT_BLOB\x10\x0f\x12\x08\n\x04USER\x10\x10\x12\r\n\tGEO_POINT\x10\x11\x12\r\n\tREFERENCE\x10\x12\x42\x14\x10\x02 \x02(\x02\x42\x0c\x42\x61\x63kupProtos')
+)
 
 
 
@@ -168,7 +171,7 @@
     _descriptor.FieldDescriptor(
       name='backup_name', full_name='apphosting.ext.datastore_admin.BackupInfo.backup_name', index=0,
       number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -210,7 +213,7 @@
     _descriptor.FieldDescriptor(
       name='kind', full_name='apphosting.ext.datastore_admin.KindBackupInfo.kind', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -300,7 +303,7 @@
     _descriptor.FieldDescriptor(
       name='name', full_name='apphosting.ext.datastore_admin.EntitySchema.Field.name', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -334,7 +337,7 @@
     _descriptor.FieldDescriptor(
       name='kind', full_name='apphosting.ext.datastore_admin.EntitySchema.kind', index=0,
       number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -364,53 +367,55 @@
 _KINDBACKUPINFO.fields_by_name['entity_schema'].message_type = _ENTITYSCHEMA
 _ENTITYSCHEMA_TYPE.fields_by_name['primitive_type'].enum_type = _ENTITYSCHEMA_PRIMITIVETYPE
 _ENTITYSCHEMA_TYPE.fields_by_name['embedded_schema'].message_type = _ENTITYSCHEMA
-_ENTITYSCHEMA_TYPE.containing_type = _ENTITYSCHEMA;
+_ENTITYSCHEMA_TYPE.containing_type = _ENTITYSCHEMA
 _ENTITYSCHEMA_FIELD.fields_by_name['type'].message_type = _ENTITYSCHEMA_TYPE
-_ENTITYSCHEMA_FIELD.containing_type = _ENTITYSCHEMA;
+_ENTITYSCHEMA_FIELD.containing_type = _ENTITYSCHEMA
 _ENTITYSCHEMA.fields_by_name['field'].message_type = _ENTITYSCHEMA_FIELD
-_ENTITYSCHEMA_PRIMITIVETYPE.containing_type = _ENTITYSCHEMA;
+_ENTITYSCHEMA_PRIMITIVETYPE.containing_type = _ENTITYSCHEMA
 DESCRIPTOR.message_types_by_name['Backup'] = _BACKUP
 DESCRIPTOR.message_types_by_name['BackupInfo'] = _BACKUPINFO
 DESCRIPTOR.message_types_by_name['KindBackupInfo'] = _KINDBACKUPINFO
 DESCRIPTOR.message_types_by_name['EntitySchema'] = _ENTITYSCHEMA
 
-class Backup(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _BACKUP
+Backup = _reflection.GeneratedProtocolMessageType('Backup', (_message.Message,), dict(
+  DESCRIPTOR = _BACKUP,
+  __module__ = 'google.appengine.ext.datastore_admin.backup_pb2'
 
+  ))
 
+BackupInfo = _reflection.GeneratedProtocolMessageType('BackupInfo', (_message.Message,), dict(
+  DESCRIPTOR = _BACKUPINFO,
+  __module__ = 'google.appengine.ext.datastore_admin.backup_pb2'
 
-class BackupInfo(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _BACKUPINFO
+  ))
 
+KindBackupInfo = _reflection.GeneratedProtocolMessageType('KindBackupInfo', (_message.Message,), dict(
+  DESCRIPTOR = _KINDBACKUPINFO,
+  __module__ = 'google.appengine.ext.datastore_admin.backup_pb2'
 
+  ))
 
-class KindBackupInfo(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _KINDBACKUPINFO
+EntitySchema = _reflection.GeneratedProtocolMessageType('EntitySchema', (_message.Message,), dict(
 
+  Type = _reflection.GeneratedProtocolMessageType('Type', (_message.Message,), dict(
+    DESCRIPTOR = _ENTITYSCHEMA_TYPE,
+    __module__ = 'google.appengine.ext.datastore_admin.backup_pb2'
 
+    ))
+  ,
 
-class EntitySchema(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
+  Field = _reflection.GeneratedProtocolMessageType('Field', (_message.Message,), dict(
+    DESCRIPTOR = _ENTITYSCHEMA_FIELD,
+    __module__ = 'google.appengine.ext.datastore_admin.backup_pb2'
 
-  class Type(_message.Message):
-    __metaclass__ = _reflection.GeneratedProtocolMessageType
-    DESCRIPTOR = _ENTITYSCHEMA_TYPE
+    ))
+  ,
+  DESCRIPTOR = _ENTITYSCHEMA,
+  __module__ = 'google.appengine.ext.datastore_admin.backup_pb2'
 
-
-
-  class Field(_message.Message):
-    __metaclass__ = _reflection.GeneratedProtocolMessageType
-    DESCRIPTOR = _ENTITYSCHEMA_FIELD
-
-
-  DESCRIPTOR = _ENTITYSCHEMA
-
-
+  ))
 
 
 DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), '\020\002 \002(\002B\014BackupProtos')
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\020\002 \002(\002B\014BackupProtos'))
 
diff --git a/google/appengine/ext/datastore_admin/static/css/compiled.css b/google/appengine/ext/datastore_admin/static/css/compiled.css
index 2be0a21..631a5c3 100644
--- a/google/appengine/ext/datastore_admin/static/css/compiled.css
+++ b/google/appengine/ext/datastore_admin/static/css/compiled.css
@@ -1,2 +1,2 @@
-/* Copyright 2013 Google Inc. All Rights Reserved. */
-html,body,div,h1,h2,h3,h4,h5,h6,p,img,dl,dt,dd,ol,ul,li,table,caption,tbody,tfoot,thead,tr,th,td,form,fieldset,embed,object,applet{margin:0;padding:0;border:0;}body{font-size:62.5%;font-family:Arial,sans-serif;color:#000;background:#fff}a{color:#00c}a:active{color:#f00}a:visited{color:#551a8b}table{border-collapse:collapse;border-width:0;empty-cells:show}ul{padding:0 0 1em 1em}ol{padding:0 0 1em 1.3em}li{line-height:1.5em;padding:0 0 .5em 0}p{padding:0 0 1em 0}h1,h2,h3,h4,h5{padding:0 0 1em 0}h1,h2{font-size:1.3em}h3{font-size:1.1em}h4,h5,table{font-size:1em}sup,sub{font-size:.7em}input,select,textarea,option{font-family:inherit;font-size:inherit}.g-doc,.g-doc-1024,.g-doc-800{font-size:130%}.g-doc{width:100%;text-align:left}.g-section{width:100%;vertical-align:top;display:inline-block}*:first-child+html .g-section{display:block}* html .g-section{overflow:hidden}@-moz-document url-prefix(''){.g-section{overflow:hidden}}@-moz-document url-prefix(''){.g-section,tt:default{overflow:visible}}.g-section,.g-unit{zoom:1}.g-split .g-unit{text-align:right}.g-split .g-first{text-align:left}.g-doc-1024{width:73.074em;min-width:950px;margin:0 auto;text-align:left}* html .g-doc-1024{width:71.313em}*+html .g-doc-1024{width:71.313em}.g-doc-800{width:57.69em;min-width:750px;margin:0 auto;text-align:left}* html .g-doc-800{width:56.3em}*+html .g-doc-800{width:56.3em}.g-tpl-160 .g-unit,.g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-unit .g-tpl-160 .g-unit{margin:0 0 0 160px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-tpl-160 .g-first,.g-tpl-160 .g-first{margin:0;width:160px;float:left}.g-tpl-160-alt .g-unit,.g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-160-alt .g-unit{margin:0 160px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-tpl-160-alt .g-first,.g-tpl-160-alt .g-first{margin:0;width:160px;float:right}.g-tpl-180 .g-unit,.g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-unit .g-tpl-180 .g-unit{margin:0 0 0 180px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-tpl-180 .g-first,.g-tpl-180 .g-first{margin:0;width:180px;float:left}.g-tpl-180-alt .g-unit,.g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-180-alt .g-unit{margin:0 180px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-tpl-180-alt .g-first,.g-tpl-180-alt .g-first{margin:0;width:180px;float:right}.g-tpl-300 .g-unit,.g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-unit .g-tpl-300 .g-unit{margin:0 0 0 300px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-tpl-300 .g-first,.g-tpl-300 .g-first{margin:0;width:300px;float:left}.g-tpl-300-alt .g-unit,.g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-300-alt .g-unit{margin:0 300px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-tpl-300-alt .g-first,.g-tpl-300-alt .g-first{margin:0;width:300px;float:right}.g-tpl-25-75 .g-unit,.g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75 .g-unit{width:74.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-tpl-25-75 .g-first,.g-tpl-25-75 .g-first{width:24.999%;float:left;margin:0}.g-tpl-25-75-alt .g-unit,.g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-unit{width:24.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-tpl-25-75-alt .g-first,.g-tpl-25-75-alt .g-first{width:74.999%;float:right;margin:0}.g-tpl-75-25 .g-unit,.g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25 .g-unit{width:24.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-tpl-75-25 .g-first,.g-tpl-75-25 .g-first{width:74.999%;float:left;margin:0}.g-tpl-75-25-alt .g-unit,.g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-unit{width:74.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-tpl-75-25-alt .g-first,.g-tpl-75-25-alt .g-first{width:24.999%;float:right;margin:0}.g-tpl-33-67 .g-unit,.g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67 .g-unit{width:66.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-tpl-33-67 .g-first,.g-tpl-33-67 .g-first{width:32.999%;float:left;margin:0}.g-tpl-33-67-alt .g-unit,.g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-unit{width:32.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-tpl-33-67-alt .g-first,.g-tpl-33-67-alt .g-first{width:66.999%;float:right;margin:0}.g-tpl-67-33 .g-unit,.g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33 .g-unit{width:32.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-tpl-67-33 .g-first,.g-tpl-67-33 .g-first{width:66.999%;float:left;margin:0}.g-tpl-67-33-alt .g-unit,.g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-unit{width:66.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-tpl-67-33-alt .g-first,.g-tpl-67-33-alt .g-first{width:32.999%;float:right;margin:0}.g-tpl-50-50 .g-unit,.g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50 .g-unit{width:49.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-tpl-50-50 .g-first,.g-tpl-50-50 .g-first{width:49.999%;float:left;margin:0}.g-tpl-50-50-alt .g-unit,.g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-unit{width:49.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-tpl-50-50-alt .g-first,.g-tpl-50-50-alt .g-first{width:49.999%;float:right;margin:0}.g-tpl-nest{width:auto}.g-tpl-nest .g-section{display:inline}.g-tpl-nest .g-unit,.g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest .g-unit{float:left;width:auto;margin:0}.g-tpl-nest-alt .g-unit,.g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest-alt .g-unit{float:right;width:auto;margin:0}.goog-button{border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px;font:normal normal normal 13px/13px Arial,sans-serif;color:#000;text-align:middle;text-decoration:none;text-shadow:0 1px 1px rgba(255,255,255,1);background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;margin:0;display:inline;display:-moz-inline-box;display:inline-block;*overflow:visible;padding:4px 8px 5px}a.goog-button,span.goog-button,div.goog-button{padding:4px 8px 5px}.goog-button:visited{color:#000}.goog-button{*display:inline}.goog-button:focus,.goog-button:hover{border-color:#000}.goog-button:active,.goog-button-active{color:#000;background-color:#bbb;border-color:#999 #bbb #bbb #999;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ddd),to(#fff));background-image:-moz-linear-gradient(top,#ddd,#fff);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#ffffff',StartColorstr='#dddddd',GradientType=0)}.goog-button[disabled],.goog-button[disabled]:active,.goog-button[disabled]:hover{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.goog-button{padding:5px 8px 4px	}.goog-button{*padding:4px 7px 2px}html>body input.goog-button,x:-moz-any-link,x:default,html>body button.goog-button,x:-moz-any-link,x:default{padding-top:3px;padding-bottom:2px}a.goog-button,x:-moz-any-link,x:default,span.goog-button,x:-moz-any-link,x:default,div.goog-button,x:-moz-any-link,x:default{padding:4px 8px 5px}.goog-button-fixed{padding-left:0!important;padding-right:0!important;width:100%}button.goog-button-icon-c{padding-top:1px;padding-bottom:1px}button.goog-button-icon-c{padding-top:3px	;padding-bottom:2px	}button.goog-button-icon-c{*padding-top:0;*padding-bottom:0}html>body button.goog-button-icon-c,x:-moz-any-link,x:default{padding-top:1px;padding-bottom:1px}.goog-button-icon{display:block;margin:0 auto;height:18px;width:18px}html>body .goog-inline-block{display:-moz-inline-box;display:inline-block;}.goog-inline-block{position:relative;display:inline-block}* html .goog-inline-block{display:inline}*:first-child+html .goog-inline-block{display:inline}.goog-custom-button{margin:0 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0)}.goog-custom-button-outer-box,.goog-custom-button-inner-box{border-style:solid;border-color:#bbb #999 #999 #bbb;vertical-align:top}.goog-custom-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-custom-button-inner-box{margin:0 -1px;border-width:0 1px;padding:3px 4px}* html .goog-custom-button-inner-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-outer-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-inner-box{left:-1px}*:first-child+html .goog-custom-button-collapse-right .goog-custom-button-inner-box{border-left-width:2px}*:first-child+html .goog-custom-button-collapse-left .goog-custom-button-inner-box{border-right-width:2px}*:first-child+html .goog-custom-button-collapse-right.goog-custom-button-collapse-left .goog-custom-button-inner-box{border-width:0 1px}*:first-child+html .goog-custom-button-rtl .goog-custom-button-inner-box{left:1px}::root .goog-custom-button,::root .goog-custom-button-outer-box{line-height:0}::root .goog-custom-button-inner-box{line-height:normal}.goog-custom-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-custom-button-disabled .goog-custom-button-outer-box,.goog-custom-button-disabled .goog-custom-button-inner-box{color:#333!important;border-color:#999!important}* html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-custom-button-hover .goog-custom-button-outer-box,.goog-custom-button-hover .goog-custom-button-inner-box{border-color:#000!important;}.goog-custom-button-active,.goog-custom-button-checked{background-color:#bbb;background-position:bottom left;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ddd),to(#fff));background:-moz-linear-gradient(top,#ddd,#fff);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#ffffff',StartColorstr='#dddddd',GradientType=0)}.goog-custom-button-focused .goog-custom-button-outer-box,.goog-custom-button-focused .goog-custom-button-inner-box,.goog-custom-button-focused.goog-custom-button-collapse-left .goog-custom-button-inner-box,.goog-custom-button-focused.goog-custom-button-collapse-left.goog-custom-button-checked .goog-custom-button-inner-box{border-color:#000}.goog-custom-button-collapse-right,.goog-custom-button-collapse-right .goog-custom-button-outer-box,.goog-custom-button-collapse-right .goog-custom-button-inner-box{margin-right:0}.goog-custom-button-collapse-left,.goog-custom-button-collapse-left .goog-custom-button-outer-box,.goog-custom-button-collapse-left .goog-custom-button-inner-box{margin-left:0}.goog-custom-button-collapse-left .goog-custom-button-inner-box{border-left:1px solid #fff}.goog-custom-button-collapse-left.goog-custom-button-checked .goog-custom-button-inner-box{border-left:1px solid #ddd}* html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}.goog-date-picker th,.goog-date-picker td{font-family:arial,sans-serif;text-align:center}.goog-date-picker th{font-size:.9em;font-weight:bold;color:#666667;background-color:#c3d9ff}.goog-date-picker td{vertical-align:middle;padding:2px 3px}.goog-date-picker{-moz-user-focus:normal;-moz-user-select:none;position:absolute;border:1px solid gray;float:left;font-family:arial,sans-serif;padding-left:1px;background:white}.goog-date-picker-menu{position:absolute;background:threedface;border:1px solid gray;-moz-user-focus:normal}.goog-date-picker-menu ul{list-style:none;margin:0;padding:0}.goog-date-picker-menu ul li{cursor:default}.goog-date-picker-menu-selected{background-color:#aaccee}.goog-date-picker td div{float:left}.goog-date-picker button{padding:0;margin:1px;border:1px outset gray}.goog-date-picker-week{padding:1px 3px}.goog-date-picker-wday{padding:1px 3px}.goog-date-picker-today-cont{text-align:left!important}.goog-date-picker-none-cont{text-align:right!important}.goog-date-picker-head td{text-align:center}.goog-date-picker-month{width:12ex}.goog-date-picker-year{width:6ex}.goog-date-picker table{border-collapse:collapse}.goog-date-picker-selected{background-color:#aaccee!important;color:blue!important}.goog-date-picker-today{font-weight:bold!important}.goog-date-picker-other-month{-moz-opacity:0.3;filter:Alpha(Opacity=30)}.sat,.sun{background:#eee}#button1,#button2{display:block;width:60px;text-align:center;margin:10px;padding:10px;font:normal .8em arial,sans-serif;border:1px solid #000}.goog-menu{position:absolute;color:#000;border:1px solid #b5b6b5;background-color:#f3f3f7;cursor:default;font:normal small arial,helvetica,sans-serif;margin:0;padding:0;outline:none}.goog-menuitem{padding:2px 5px;margin:0;list-style:none}.goog-menuitem-highlight{background-color:#4279a5;color:#fff}.goog-menuitem-disabled{color:#999}.goog-option{padding-left:15px!important}.goog-option-selected{background-image:url('/img/check.gif');background-position:4px 50%;background-repeat:no-repeat}.goog-menuseparator{position:relative;margin:2px 0;border-top:1px solid #999;padding:0;outline:none}.goog-submenu{position:relative}.goog-submenu-arrow{position:absolute;display:block;width:11px;height:11px;right:3px;top:4px;background-image:url('/img/menu-arrows.gif');background-repeat:no-repeat;background-position:0 0;font-size:1px}.goog-menuitem-highlight .goog-submenu-arrow{background-position:0 -11px}.goog-menuitem-disabled .goog-submenu-arrow{display:none}.goog-menu-filter{margin:2px;border:1px solid silver;background:white;overflow:hidden}.goog-menu-filter div{color:gray;position:absolute;padding:1px}.goog-menu-filter input{margin:0;border:0;background:transparent;width:100%}.goog-menuitem-partially-checked{background-image:url('/img/check-outline.gif');background-position:4px 50%;background-repeat:no-repeat}.goog-menuitem-fully-checked{background-image:url('/img/check.gif');background-position:4px 50%;background-repeat:no-repeat}.goog-menu-button{margin:0 2px 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;background:#ddd url("/img/button-bg.gif") repeat-x top left;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none}.goog-menu-button-outer-box,.goog-menu-button-inner-box{border-style:solid;border-color:#aaa;vertical-align:middle}.goog-menu-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-menu-button-inner-box{margin:0 -1px;border-width:0 1px;padding:0 4px 2px 4px}* html .goog-menu-button-inner-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-outer-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-inner-box{left:0}*:first-child+html .goog-menu-button-inner-box{left:-1px}*:first-child+html .goog-menu-button-rtl .goog-menu-button-inner-box{left:1px}::root .goog-menu-button,::root .goog-menu-button-outer-box,::root .goog-menu-button-inner-box{line-height:0}::root .goog-menu-button-caption,::root .goog-menu-button-dropdown{line-height:normal}.goog-menu-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-menu-button-disabled .goog-menu-button-outer-box,.goog-menu-button-disabled .goog-menu-button-inner-box,.goog-menu-button-disabled .goog-menu-button-caption,.goog-menu-button-disabled .goog-menu-button-dropdown{color:#333!important;border-color:#999!important}* html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-menu-button-hover .goog-menu-button-outer-box,.goog-menu-button-hover .goog-menu-button-inner-box{border-color:#9cf #69e #69e #7af!important;}.goog-menu-button-active,.goog-menu-button-open{background-color:#bbb;background-position:bottom left}.goog-menu-button-focused .goog-menu-button-outer-box,.goog-menu-button-focused .goog-menu-button-inner-box{border-color:#3366cc}.goog-menu-button-caption{padding:0 4px 0 0;vertical-align:middle}.goog-menu-button-rtl .goog-menu-button-caption{padding:0 0 0 4px}.goog-menu-button-dropdown{width:7px;background:url('/img/toolbar_icons.gif') no-repeat -176px;vertical-align:middle}.goog-flat-menu-button{margin:0 2px;padding:1px 4px;font:normal 95% Tahoma,Arial,sans-serif;color:#333;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;-moz-outline:none;border-width:1px;border-style:solid;border-color:#c9c9c9;background-color:#fff}.goog-flat-menu-button-disabled *{color:#999;border-color:#ccc;cursor:default}.goog-flat-menu-button-hover,.goog-flat-menu-button-hover{border-color:#9cf #69e #69e #7af!important;}.goog-flat-menu-button-active{background-color:#bbb;background-position:bottom left}.goog-flat-menu-button-focused{border-color:#3366cc}.goog-flat-menu-button-caption{padding-right:10px;vertical-align:middle}.goog-flat-menu-button-dropdown{width:7px;background:url('/img/toolbar_icons.gif') no-repeat -176px;vertical-align:middle}h1{font-size:1.8em}.g-doc{width:auto;margin:0 10px}.g-doc-1024{margin-left:10px}#ae-logo{background:url('//www.google.com/images/logos/app_engine_logo_sm.gif') 0 0 no-repeat;display:block;width:178px;height:30px;margin:4px 0 0 0}.ae-ir span{position:absolute;display:block;width:0;height:0;overflow:hidden}.ae-noscript{position:absolute;left:-5000px}#ae-lhs-nav{border-right:3px solid #e5ecf9}.ae-notification{margin-bottom:.6em;text-align:center}.ae-notification strong{display:block;width:55%;margin:0 auto;text-align:center;padding:.6em;background-color:#fff1a8;font-weight:bold}.ae-alert{font-weight:bold;background:url('/img/icn/warning.png') no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-info{background:url('/img/icn/icn-info.gif') no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-promo{padding:.5em .8em;margin:.6em 0;background-color:#fffbe8;border:1px solid #fff1a9;text-align:left}.ae-promo strong{position:relative;top:.3em}.ae-alert-text,.ae-warning-text{background-color:transparent;background-position:right 1px;padding:0 18px 0 0}.ae-alert-text{color:#c00}.ae-warning-text{color:#f90}.ae-alert-c span{display:inline-block}.ae-message{border:1px solid #e5ecf9;background-color:#f6f9ff;margin-bottom:1em;padding:.5em}.ae-errorbox{border:1px solid #f00;background-color:#fee;margin-bottom:1em;padding:1em}#bd .ae-errorbox ul{padding-bottom:0}.ae-form dt{font-weight:bold}.ae-form dt em,.ae-field-hint{margin-top:.2em;color:#666667;font-size:.85em}.ae-field-hint-inline{color:#666667;font-size:.85em;display:inline}.ae-field-yyyymmdd,.ae-field-hhmmss{width:6em}.ae-field-hint-hhmmss{margin-left:2.3em}.ae-form label{display:block;margin:0 0 .2em 0;font-weight:bold}.ae-radio{margin-bottom:.3em}.ae-radio label{display:inline}.ae-form dd,.ae-input-row{margin-bottom:.6em}.ae-input-row-group{border:1px solid #fff1a9;background:#fffbe8;padding:8px}.ae-btn-row{margin-top:1.4em;margin-bottom:1em}.ae-btn-row-note{padding:5px 0 6px 0}.ae-btn-row-note span{padding-left:18px;padding-right:.5em;background:transparent url('/img/icn/icn-info.gif') 0 0 no-repeat}.ae-btn-primary{font-weight:bold}form .ae-cancel{margin-left:.5em}.ae-submit-inline{margin-left:.8em}.ae-radio-bullet{width:20px;float:left}.ae-label-hanging-indent{margin-left:5px}.ae-divider{margin:0 .6em 0 .5em}.ae-nowrap{white-space:nowrap}.ae-pre-wrap{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;_white-space:pre;}wbr:after{content:"\00200b"}a button{text-decoration:none}.ae-alert ul{margin-bottom:.75em;margin-top:.25em;line-height:1.5em}.ae-alert h4{color:#000;font-weight:bold;padding:0 0 .5em}.ae-form-simple-list{list-style-type:none;padding:0;margin-bottom:1em}.ae-form-simple-list li{padding:.3em 0 .5em .5em;border-bottom:1px solid #c3d9ff}div.ae-datastore-index-to-delete,div.ae-datastore-index-to-build{color:#aaa}#hd p{padding:0}#hd li{display:inline}ul{padding:0 0 1em 1.2em}#ae-userinfo{text-align:right;white-space:nowrap;}#ae-userinfo ul{padding-bottom:0;padding-top:5px}#ae-appbar-lrg{margin:0 0 1.25em 0;padding:.25em .5em;background-color:#e5ecf9;border-top:1px solid #36c}#ae-appbar-lrg h1{font-size:1.2em;padding:0}#ae-appbar-lrg h1 span{font-size:80%;font-weight:normal}#ae-appbar-lrg form{display:inline;padding-right:.1em;margin-right:.5em}#ae-appbar-lrg strong{white-space:nowrap}#ae-appbar-sml{margin:0 0 1.25em 0;height:8px;padding:0 .5em;background:#e5ecf9}.ae-rounded-sml{border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px}#ae-appbar-lrg a{margin-top:.3em}#ae-engine-version-bar{margin:0 0 1em}#ae-engine-version-bar>.g-unit{width:auto}a.ae-ext-link,a span.ae-ext-link{background:url('/img/icn/icn-open-in-new-window.png') no-repeat right;padding-right:18px;margin-right:8px}.ae-no-pad{padding-left:1em}.ae-message h4{margin-bottom:.3em;padding-bottom:0}#ft{text-align:center;margin:2.5em 0 1em;padding-top:.5em;border-top:2px solid #c3d9ff}#bd h3{font-weight:bold;font-size:1.4em}#bd h3 .ae-apps-switch{font-weight:normal;font-size:.7em;margin-left:2em}#bd p{padding:0 0 1em 0}#ae-content{padding-left:1em}.ae-unimportant{color:#666}.ae-new-usr td{border-top:1px solid #ccccce;background-color:#ffe}.ae-error-td td{border:2px solid #f00;background-color:#fee}.ae-delete{cursor:pointer;border:none;background:transparent;}.ae-btn-large{background:#039 url('/img/icn/button_back.png') repeat-x;color:#fff;font-weight:bold;font-size:1.2em;padding:.5em;border:2px outset #000;cursor:pointer}.ae-breadcrumb{margin:0 0 1em}.ae-disabled,a.ae-disabled,a.ae-disabled:hover,a.ae-disabled:active{color:#666!important;text-decoration:none!important;cursor:default!important;opacity:.4!important;-moz-opacity:.4!important;filter:alpha(opacity=40)!important}input.ae-readonly{border:2px solid transparent;border-left:0;background-color:transparent}span.ae-text-input-clone{padding:5px 5px 5px 0}.ae-loading{opacity:.4;-moz-opacity:.4;filter:alpha(opacity=40)}.ae-tip{margin:1em 0;background:url('/img/tip.png') top left no-repeat;padding:2px 0 0 25px}sup.ae-new-sup{color:red}sup.ae-new-sup a{border-bottom:1px solid red;color:red;text-decoration:none}.ae-action{color:#00c;cursor:pointer;text-decoration:underline}.ae-toggle{padding-left:16px;background-position:left center;background-repeat:no-repeat;cursor:pointer}.ae-minus{background-image:url('/img/wgt/minus.gif')}.ae-plus{background-image:url('/img/wgt/plus.gif')}.ae-print{background-image:url('/img/print.gif');padding-left:19px}.ae-download{background:url('/img/download.png') left center no-repeat;padding-left:22px}.ae-currency,.ae-table thead th.ae-currency{text-align:right;white-space:nowrap}#ae-loading{font-size:1.2em;position:absolute;text-align:center;top:0;width:100%}#ae-loading div{margin:0 auto;background:#fff1a9;width:5em;font-weight:bold;padding:4px 10px;-moz-border-radius-bottomleft:3px;-moz-border-radius-bottomright:3px;-webkit-border-radius-bottomleft:3px;-webkit-border-radius-bottomright:3px}.ae-occlude{filter:alpha(opacity=0);position:absolute}.g-tpl-66-34 .g-unit,.g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-unit .g-tpl-66-34 .g-unit{display:inline;margin:0;width:33.999%;float:right}.g-unit .g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-tpl-66-34 .g-first,.g-tpl-66-34 .g-first{display:inline;margin:0;width:65.999%;float:left}.ae-ie6-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}h2.ae-section-header{background:#e5ecf9;padding:.2em .4em;margin-bottom:.5em}.ae-field-span{padding:3px 0}ul.ae-admin-list li{margin:0 0;padding:.1em 0}#ae-feedback-bar{background-color:#f9edbe;border:1px solid #f0c36d;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.2);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.2);box-shadow:0 2px 4px rgba(0,0,0,0.2);left:280px;margin-top:-5px;overflow:hidden;padding:10px 16px;position:fixed;text-align:center;z-index:999}.ae-feedback-info{margin-bottom:7px}.ae-feedback-option{color:#222;display:inline-block;font-size:0.87em;vertical-align:bottom;width:75px}.ae-feedback-close-icon{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAARUlEQVR42mMon7n7fwWZmAFEkAOorxmXYdjUoWhG9g+6QnRxnDYjK8RnIFY/YwtZkgIMn0ba2Ey2n6kS2mTFM/2TJ7kYAJSLDRhvVX1GAAAAAElFTkSuQmCC') no-repeat 0 0;float:right;height:15px;margin-right:-11px;width:15px}select{font:13px/13px Arial,sans-serif;color:#000;border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;-webkit-border-radius:2px;-moz-border-radius:2px;background:#eee;background:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;padding:2px 1px;margin:0}select:hover{border-color:#000}select[disabled],select[disabled]:active{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.ae-table-plain{border-collapse:collapse;width:100%}.ae-table{border:1px solid #c5d7ef;border-collapse:collapse;width:100%}#bd h2.ae-table-title{background:#e5ecf9;margin:0;color:#000;font-size:1em;padding:3px 0 3px 5px;border-left:1px solid #c5d7ef;border-right:1px solid #c5d7ef;border-top:1px solid #c5d7ef}.ae-table-caption,.ae-table caption{border:1px solid #c5d7ef;background:#e5ecf9;-moz-margin-start:-1px}.ae-table caption{padding:3px 5px;text-align:left}.ae-table th,.ae-table td{background-color:#fff;padding:.35em 1em .25em .35em;margin:0}.ae-table thead th{font-weight:bold;text-align:left;background:#c5d7ef;vertical-align:bottom}.ae-table thead th .ae-no-bold{font-weight:normal}.ae-table tfoot tr td{border-top:1px solid #c5d7ef;background-color:#e5ecf9}.ae-table td{border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even>td,.ae-even th,.ae-even-top td,.ae-even-tween td,.ae-even-bottom td,ol.ae-even{background-color:#e9e9e9;border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even-top td{border-bottom:0}.ae-even-bottom td{border-top:0}.ae-even-tween td{border:0}.ae-table .ae-tween td{border:0}.ae-table .ae-tween-top td{border-bottom:0}.ae-table .ae-tween-bottom td{border-top:0}#bd .ae-table .cbc{width:1.5em;padding-right:0}.ae-table #ae-live td{background-color:#ffeac0}.ae-table-fixed{table-layout:fixed}.ae-table-fixed td,.ae-table-nowrap{overflow:hidden;white-space:nowrap}.ae-paginate strong{margin:0 .5em}tfoot .ae-paginate{text-align:right}.ae-table-caption .ae-paginate,.ae-table-caption .ae-orderby{padding:2px 5px}.modal-dialog{background:#c1d9ff;border:1px solid #3a5774;color:#000;padding:4px;position:absolute;font-size:1.3em;-moz-box-shadow:0 1px 4px #333;-webkit-box-shadow:0 1px 4px #333;box-shadow:0 1px 4px #333}.modal-dialog a,.modal-dialog a:link,.modal-dialog a:visited{color:#06c;cursor:pointer}.modal-dialog-bg{background:#666;left:0;position:absolute;top:0}.modal-dialog-title{background:#e0edfe;color:#000;cursor:pointer;font-size:120%;font-weight:bold;padding:8px 15px 8px 8px;position:relative;_zoom:1;}.modal-dialog-title-close{background:#e0edfe url('https://ssl.gstatic.com/editor/editortoolbar.png') no-repeat -528px 0;cursor:default;height:15px;position:absolute;right:10px;top:8px;width:15px;vertical-align:middle}.modal-dialog-buttons,.modal-dialog-content{background-color:#fff;padding:8px}.modal-dialog-buttons button{margin-right:.75em}.goog-buttonset-default{font-weight:bold}.goog-tab{position:relative;border:1px solid #8ac;padding:4px 9px;color:#000;background:#e5ecf9;border-top-left-radius:2px;border-top-right-radius:2px;-moz-border-radius-topleft:2px;-webkit-border-top-left-radius:2px;-moz-border-radius-topright:2px;-webkit-border-top-right-radius:2px}.goog-tab-bar-top .goog-tab{margin:1px 4px 0 0;border-bottom:0;float:left}.goog-tab-bar-bottom .goog-tab{margin:0 4px 1px 0;border-top:0;float:left}.goog-tab-bar-start .goog-tab{margin:0 0 4px 1px;border-right:0}.goog-tab-bar-end .goog-tab{margin:0 1px 4px 0;border-left:0}.goog-tab-hover{text-decoration:underline;cursor:pointer}.goog-tab-disabled{color:#fff;background:#ccc;border-color:#ccc}.goog-tab-selected{background:#fff!important;color:black;font-weight:bold}.goog-tab-bar-top .goog-tab-selected{top:1px;margin-top:0;padding-bottom:5px}.goog-tab-bar-bottom .goog-tab-selected{top:-1px;margin-bottom:0;padding-top:5px}.goog-tab-bar-start .goog-tab-selected{left:1px;margin-left:0;padding-right:9px}.goog-tab-bar-end .goog-tab-selected{left:-1px;margin-right:0;padding-left:9px}.goog-tab-content{padding:.1em .8em .8em .8em;border:1px solid #8ac;border-top:none}.goog-tab-bar{position:relative;margin:0 0 0 5px;border:0;padding:0;list-style:none;cursor:default;outline:none}.goog-tab-bar-clear{border-top:1px solid #8ac;clear:both;height:0;overflow:hidden}.goog-tab-bar-start{float:left}.goog-tab-bar-end{float:right}* html .goog-tab-bar-start{margin-right:-3px}* html .goog-tab-bar-end{margin-left:-3px}#ae-nav ul{list-style-type:none;margin:0;padding:1em 0}#ae-nav ul li{padding-left:.5em}#ae-nav .ae-nav-selected{color:#000;display:block;font-weight:bold;background-color:#e5ecf9;margin-right:-1px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px}#ae-nav .ae-nav-bold{font-weight:bold}#ae-nav ul li span.ae-nav-disabled{color:#666}#ae-nav ul ul{margin:0;padding:0 0 0 .5em}#ae-nav ul ul li{padding-left:.5em}#ae-nav ul li a,#ae-nav ul li span,#ae-nav ul ul li a{padding-left:.5em}#ae-nav li a:link,#ae-nav li a:visited{color:#00c}.ae-nav-group{padding:.5em;margin:0 .75em 0 0;background-color:#fffbe8;border:1px solid #fff1a9}.ae-nav-group h4{font-weight:bold;padding:auto auto .5em .5em;padding-left:.4em;margin-bottom:.5em;padding-bottom:0}.ae-nav-group ul{margin:0 0 .5em 0;padding:0 0 0 1.3em;list-style-type:none}.ae-nav-group ul li{padding-bottom:.5em}.ae-nav-group li a:link,.ae-nav-group li a:visited{color:#00c}.ae-nav-group li a:hover{color:#00c}@media print{body{font-size:13px;width:8.5in;background:#fff}table,.ae-table-fixed{table-layout:automatic}tr{display:table-row!important}.g-doc-1024{width:8.5in}#ae-appbar-lrg,.ae-table-caption,.ae-table-nowrap,.ae-nowrap,th,td{overflow:visible!important;white-space:normal!important;background:#fff!important}.ae-print,.ae-toggle{display:none}#ae-lhs-nav-c{display:none}#ae-content{margin:0;padding:0}.goog-zippy-collapsed,.goog-zippy-expanded{background:none!important;padding:0!important}}#ae-admin-dev-table{margin:0 0 1em 0}.ae-admin-dev-tip,.ae-admin-dev-tip.ae-tip{margin:-0.31em 0 2.77em}#ae-sms-countryselect{margin-right:.5em}#ae-admin-enable-form{margin-bottom:1em}#ae-admin-services-c{margin-top:2em}#ae-admin-services{padding:0 0 0 3em;margin-bottom:1em;font-weight:bold}#ae-admin-logs-table-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-admin-logs-table{margin:0;padding:0}#ae-admin-logs-filters{padding:3px 0 3px 5px}#ae-admin-logs-pagination{padding:6px 5px 0 0;text-align:right;width:45%}#ae-admin-logs-pagination span.ae-disabled{color:#666;background-color:transparent}#ae-admin-logs-table td{white-space:nowrap}#ae-admin-logs-timezone{float:right;margin-bottom:7px}#ae-storage-content div.ae-alert{padding-bottom:5px}#ae-admin-performance-form input[type=text]{width:2em}.ae-admin-performance-value{font-weight:normal}.ae-admin-performance-static-value{color:#666}#ae-admin-performance-frontend-class{margin-left:0.5em}.goog-slider-horizontal,.goog-twothumbslider-horizontal{position:relative;width:502px;height:7px;display:block;outline:0;margin:1.0em 0 0.9em 3em}.ae-slider-rail:before{position:relative;top:-0.462em;float:left;content:'Min';margin:0 0 0 -3em;color:#999}.ae-slider-rail{position:absolute;background-color:#d9d9d9;top:0;right:8px;bottom:0;left:8px;border:solid 1px;border-color:#a6a6a6 #b3b3b3 #bfbfbf;border-radius:5px}.ae-slider-rail:after{position:relative;top:-0.462em;float:right;content:'Max';margin:0 -3em 0 0;color:#999}.goog-slider-horizontal .goog-slider-thumb,.goog-twothumbslider-horizontal .goog-twothumbslider-value-thumb,.goog-twothumbslider-horizontal .goog-twothumbslider-extent-thumb{position:absolute;width:17px;height:17px;background:transparent url('/img/slider_thumb-down.png') no-repeat;outline:0}.goog-slider-horizontal .goog-slider-thumb{top:-5px}.goog-twothumbslider-horizontal .goog-twothumbslider-value-thumb{top:-11px}.goog-twothumbslider-horizontal .goog-twothumbslider-extent-thumb{top:2px;background-image:url('/img/slider_thumb-up.png')}.ae-admin-performance-scale{position:relative;display:inline-block;width:502px;margin:0 0 2.7em 3em}.ae-admin-performance-scale .ae-admin-performance-scale-start{position:absolute;display:inline-block;top:0;width:100%;text-align:left}.ae-admin-performance-scale .ae-admin-performance-scale-mid{position:absolute;display:inline-block;top:0;width:100%;text-align:center}.ae-admin-performance-scale .ae-admin-performance-scale-end{position:absolute;display:inline-block;top:0;width:100%;text-align:right}.ae-pagespeed-controls{margin:0 0 1em 8px}.ae-pagespeed-controls label{display:inline;font-weight:normal}#ae-pagespeed-flush-cache{margin-left:1em}#ae-pagespeed-flush-cache-status{margin-left:1em;font-weight:bold}.ae-absolute-container{display:inline-block;width:100%}.ae-hidden-range{display:none}.ae-default-version-radio-column{width:1em}#ae-settings-builtins-change{margin-bottom:1em}#ae-memcache-full-flush-warning{color:#c00}#ae-memcache-partial-flush-warning{color:#c00}.prettyprint{background-color:#fafafa;font:1em "Droid Sans Mono",monospace;margin-right:20px}#formatted-performance-settings>div{float:left}#formatted-performance-settings:after{clear:both;content:"";display:table}#dispatch-table{table-layout:fixed}.modules-message{display:inline-block}.modules-dismiss{background-image:url("/img/dismiss.png");cursor:pointer;border:0;background-color:transparent;background-repeat:no-repeat;background-size:14px;margin-top:-2px;margin-right:-2px}.ae-message>.modules-form>.modules-message>.g-unit{width:10%}.ae-message>.modules-form>.modules-message>.g-first{width:90%}#ae-billing-form-c{_margin-right:-3000px;_position:relative;_width:100%}.ae-rounded-top-small{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px}.ae-progress-content{height:400px}#ae-billing-tos{text-align:left;width:100%;margin-bottom:.5em}.ae-billing-budget-section{margin-bottom:1.5em}.ae-billing-budget-section .g-unit,.g-unit .ae-billing-budget-section .g-unit,.g-unit .g-unit .ae-billing-budget-section .g-unit{margin:0 0 0 11em;width:auto;float:none}.g-unit .g-unit .ae-billing-budget-section .g-first,.g-unit .ae-billing-budget-section .g-first,.ae-billing-budget-section .g-first{margin:0;width:11em;float:left}#ae-billing-form .ae-btn-row{margin-left:11em}#ae-billing-form .ae-btn-row .ae-info{margin-top:10px}#ae-billing-checkout{width:150px;float:left}#ae-billing-alloc-table{border:1px solid #c5d7ef;border-bottom:none;width:100%;margin-top:.5em}#ae-billing-alloc-table th,#ae-billing-alloc-table td{padding:.35em 1em .25em .35em;border-bottom:1px solid #c5d7ef;color:#000;white-space:nowrap}.ae-billing-resource{background-color:transparent;font-weight:normal}#ae-billing-alloc-table tr th span{font-weight:normal}#ae-billing-alloc-table tr{vertical-align:baseline}#ae-billing-alloc-table th{white-space:nowrap}#ae-billing-alloc-table .ae-editable span.ae-text-input-clone,#ae-billing-alloc-table .ae-readonly input{display:none}#ae-billing-alloc-table .ae-readonly span.ae-text-input-clone,#ae-billing-alloc-table .ae-editable input{display:inline}#ae-billing-alloc-table td span.ae-billing-warn-note,#ae-billing-table-errors .ae-billing-warn-note{margin:0;background-repeat:no-repeat;display:inline-block;background-image:url('/img/icn/warning.png');text-align:right;padding-left:16px;padding-right:.1em;height:16px;font-weight:bold}#ae-billing-alloc-table td span.ae-billing-warn-note span,#ae-billing-table-errors .ae-billing-warn-note span{vertical-align:super;font-size:80%}#ae-billing-alloc-table td span.ae-billing-error-hidden,#ae-billing-table-errors .ae-billing-error-hidden{display:none}.ae-billing-percent{font-size:80%;color:#666;margin-left:3px}#ae-billing-week-info{margin-top:5px;line-height:1.4}#ae-billing-table-errors{margin-top:.3em}#ae-billing-allocation-noscript{margin-top:1.5em}#ae-billing-allocation-custom-opts{margin-left:2.2em}#ae-billing-settings h2{font-size:1em;display:inline}#ae-billing-settings p{padding:.3em 0 .5em}#ae-billing-settings-table{margin:.4em 0 .5em}#ae-settings-resource-col{width:19%}#ae-settings-budget-col{width:11%}#ae-billing-settings-table .ae-settings-budget-col{padding-right:2em}.ae-table th.ae-settings-unit-cell,.ae-table td.ae-settings-unit-cell,.ae-table th.ae-total-unit-cell,.ae-table td.ae-total-unit-cell{padding-left:1.2em}#ae-settings-unit-col{width:18%}#ae-settings-paid-col{width:15%}#ae-settings-free-col{width:15%}#ae-settings-total-col{width:22%}.ae-billing-inline-link{margin-left:.5em}.ae-billing-settings-section{margin-bottom:2em}#ae-billing-settings form{display:inline-block}#ae-billing-settings .ae-btn-row{margin-top:0.5em}#ae-billing-budget-setup-checkout{margin-bottom:0}#ae-billing-vat-c .ae-field-hint{width:85%}#ae-billing-checkout-note{margin-top:.8em}.ae-drachma-preset{background-color:#f6f9ff;margin-left:11em}.ae-drachma-preset p{margin-top:.5em}.ae-table thead th.ae-currency-th{text-align:right}#ae-billing-logs-date{width:15%}#ae-billing-logs-event{width:69%}#ae-billing-logs-amount{text-align:right;width:8%}#ae-billing-logs-balance{text-align:right;width:8%}#ae-billing-history-expand .ae-action{margin-left:1em}.ae-table .ae-billing-usage-premier,.ae-table .ae-billing-usage-report{width:100%;*width:auto;margin:0 0 1em 0}.ae-table .ae-billing-usage-report th,.ae-table .ae-billing-usage-premier th,.ae-billing-charges th{color:#666;border-top:0}.ae-table .ae-billing-usage-report th,.ae-table .ae-billing-usage-report td,.ae-table .ae-billing-usage-premier th,.ae-table .ae-billing-usage-premier td,.ae-billing-charges th,.ae-billing-charges td{background-color:transparent;padding:.4em 0;border-bottom:1px solid #ddd}.ae-table .ae-billing-usage-report tfoot td,.ae-billing-charges tfoot td{border-bottom:none}table.ae-billing-usage-report col.ae-billing-report-resource{width:30%}table.ae-billing-usage-report col.ae-billing-report-used{width:20%}table.ae-billing-usage-report col.ae-billing-report-free{width:16%}table.ae-billing-usage-report col.ae-billing-report-paid{width:17%}table.ae-billing-usage-report col.ae-billing-report-charge{width:17%}table.ae-billing-usage-premier col.ae-billing-report-resource{width:50%}table.ae-billing-usage-premier col.ae-billing-report-used{width:30%}table.ae-billing-usage-premier col.ae-billing-report-unit{width:20%}.ae-billing-change-resource{width:85%}.ae-billing-change-budget{width:15%}#ae-billing-always-on-label{display:inline}#ae-billing-budget-buffer-label{display:inline}.ae-billing-charges{width:50%}.ae-billing-charges-charge{text-align:right}.ae-billing-usage-report-container{padding:1em 1em 0 1em}#ae-billing-new-usage{background-color:#f6f9ff}.goog-zippy-expanded{background-image:url('/img/wgt/minus.gif');cursor:pointer;background-repeat:no-repeat;padding-left:17px}.goog-zippy-collapsed{background-image:url('/img/wgt/plus.gif');cursor:pointer;background-repeat:no-repeat;padding-left:17px}#ae-admin-logs-pagination{width:auto}.ae-usage-cycle-note{color:#555}#ae-billing-budget-widget .g-content{margin-bottom:0.5em;margin-right:0.5em}#ae-request-billing-dialog{width:800px}#ae-manage-billing-admins-form-cancel{color:#000}.ae-grace-period-resignup-outstanding-balance{color:red;font-weight:bold}.ae-billing-admins-table{width:100%}.ae-billing-admins-table td{width:33%;word-break:break-all;padding-bottom:5px}.b3iframe{width:100%}iframe{border:none}#ae-createapp-start{background-color:#c6d5f1;padding:1em;padding-bottom:2em;text-align:center}#ae-admin-app_id_alias-check,#ae-createapp-id-check{margin:0 0 0 1em}#ae-admin-app_id_alias-message{display:block;margin:.4em 0}#ae-createapp-id-content{width:100%}#ae-createapp-id-content td{vertical-align:top}#ae-createapp-id-td{white-space:nowrap;width:1%}#ae-createapp-id-td #ae-createapp-id-error{position:absolute;width:24em;padding-left:1em;white-space:normal}#ae-createapp-id-error-td{padding-left:1em}#ae-admin-dev-invite label{float:left;width:3.6em;position:relative;top:.3em}#ae-admin-dev-invite .ae-radio{margin-left:3.6em}#ae-admin-dev-invite .ae-radio label{float:none;width:auto;font-weight:normal;position:static}#ae-admin-dev-invite .goog-button{margin-left:3.6em}#ae-admin-dev-invite .ae-field-hint{margin-left:4.2em}#ae-admin-dev-invite .ae-radio .ae-field-hint{margin-left:0}.ae-you{color:#008000}#ae-authdomain-opts{margin-bottom:1em}#ae-authdomain-content .ae-input-text,#ae-authdomain-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-authdomain-opts a{margin-left:1em}#ae-authdomain-opts-hint{color:#666667;font-size:.85em;margin-top:.2em}#ae-authdomain-content #ae-authdomain-desc .ae-field-hint{margin-left:0}#ae-storage-opts{margin-bottom:1em}#ae-storage-content .ae-input-text,#ae-storage-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-storage-opts a{margin-left:1em}#ae-storage-opts-hint{color:#666667;font-size:.85em;margin-top:.2em}#ae-storage-content #ae-storage-desc .ae-field-hint{margin-left:0}#ae-location-opts{margin-bottom:1em}#ae-location-content .ae-input-text,#ae-location-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-location-opts a{margin-left:1em}#ae-location-opts-hint{color:#666667;font-size:.85em;margin-top:.2em}#ae-location-content #ae-storage-desc .ae-field-hint{margin-left:0}#ae-dash .g-section{margin:0 0 1em}#ae-dash * .g-section{margin:0}#ae-dash-quota .ae-alert{padding-left:1.5em}.ae-dash-email-disabled{background:url('/img/icn/exclamation_circle.png') no-repeat;margin-bottom:.5em;margin-top:.5em;min-height:16px;padding-left:1.5em}#ae-dash-email-disabled-footnote{font-weight:normal;margin:5px 0 0;padding-left:1.5em}#ae-dash-graph-c{border:1px solid #c5d7ef;padding:5px 0}#ae-dash-graph-change{margin:0 0 0 5px}#ae-dash-graph-img{background-color:#fff;display:block;margin-top:.5em;padding:5px}#ae-dash-graph-nodata{text-align:center}#ae-dash .ae-logs-severity{margin-right:.5em}#ae-dash .g-c{padding:0 0 0 .1em}#ae-dash .g-tpl-50-50 .g-unit .g-c{padding:0 0 0 1em}#ae-dash .g-tpl-50-50 .g-first .g-c{padding:0 1em 0 .1em}.ae-quota-warnings{background-color:#fffbe8;margin:0;padding:.5em .5em 0;text-align:left}.ae-quota-warnings div{padding:0 0 .5em}#ae-dash-quota-refresh-info{font-size:85%}#ae-dash #ae-dash-dollar-bucket-c #ae-dash-dollar-bucket{width:100%;float:none}#ae-dash #ae-dash-quota-bar-col,#ae-dash .ae-dash-quota-bar-col,#ae-dash .ae-dash-quota-bar{width:100px}#ae-dash-quotadetails #ae-dash-quota-bar-col,#ae-dash-quotadetails .ae-dash-quota-bar-col,#ae-dash-quotadetails .ae-dash-quota-bar{width:200px}#ae-dash-quota-percent-col,.ae-dash-quota-percent-col{width:3.5em}#ae-dash-quota-cost-col{width:15%}#ae-dash-quota-alert-col{width:3.5em}#ae-dash .ae-dash-quota-alert-td{padding:0}.ae-dash-quota-alert-td a{display:block;width:16px;height:16px}#ae-dash .ae-dash-quota-alert-td .ae-alert{display:block;width:16px;height:16px;margin:0;padding:0}#ae-dash .ae-dash-quota-alert-td .ae-dash-email-disabled{display:block;width:16px;height:16px;margin:0;padding:0}#ae-dash-quota tbody th{font-weight:normal}#ae-dash-quota caption{padding:0}#ae-dash-quota caption .g-c{padding:3px}.ae-dash-quota-bar{float:left;background-color:#c0c0c0;height:13px;margin:.1em 0 0 0;position:relative}.ae-dash-quota-footnote{margin:5px 0 0;font-weight:normal}.ae-quota-warning{background-color:#f90}.ae-quota-alert{background-color:#c00}.ae-quota-normal{background-color:#0b0}.ae-quota-alert-text{color:#c00}.ae-favicon-text{font-size:.85em}#ae-dash-popular{width:97%}#ae-dash-popular-reqsec-col{width:6.5em}#ae-dash-popular-req-col{width:7em}#ae-dash-popular-mcycles-col{width:9.5em}#ae-dash-popular-latency-col{width:7em}#ae-dash-popular .ae-unimportant{font-size:80%}#ae-dash-popular .ae-nowrap,#ae-dash-errors .ae-nowrap{margin-right:5px;overflow:hidden}#ae-dash-popular th span,#ae-dash-errors th span{font-size:.8em;font-weight:normal;display:block}#ae-dash-errors caption .g-unit{width:9em}#ae-dash-errors-count-col{width:5em}#ae-dash-errors-percent-col{width:7em}#ae-dash-graph-chart-type{float:left;margin-right:1em}#ae-apps-all strong.ae-disabled{color:#000;background:#eee}.ae-quota-resource{width:30%}.ae-quota-safety-limit{width:10%}#ae-quota-details h3{padding-bottom:0;margin-bottom:.25em}#ae-quota-details table{margin-bottom:1.75em}#ae-quota-details table.ae-quota-requests{margin-bottom:.5em}#ae-quota-refresh-note p{text-align:right;padding-top:.5em;padding-bottom:0;margin-bottom:0}#ae-quota-first-api.g-section{padding-bottom:0;margin-bottom:.25em}#ae-instances-summary-table,#ae-instances-details-table{margin-bottom:1em}.ae-instances-details-availability-image{float:left;margin-right:.5em}.ae-instances-vm-health-image{float:left;margin-right:.5em}.ae-instances-agent-warning-image{float:right}.ae-instances-small-text{font-size:80%}.ae-instances-small-text .ae-separator{color:#666}.ae-instances-highlight td{background-color:#fff1a8}.ae-instances-release-col{width:6em}.ae-appbar-superuser-message strong{color:red}#ae-backends-table tr{vertical-align:baseline}.ae-backends-class-reminder{font-size:80%;color:#666;margin-left:3px}#ae-appbar-engines,#ae-appbar-versions{display:inline}#ae-update{background-color:#fffbe8;border:1px solid}.ae-appbar-logs-invite{background-color:#eef66c;border-width:2px;border-color:yellow;border-radius:2px;float:right;padding:3px 16px}.ac-renderer{font:normal 13px Arial,sans-serif;position:absolute;background:#fff;border:1px solid #666;-moz-box-shadow:2px 2px 2px rgba(102,102,102,.4);-webkit-box-shadow:2px 2px 2px rgba(102,102,102,.4);width:202px}.ac-row{cursor:pointer;padding:.4em}.ac-highlighted{font-weight:bold}.ac-active{background-color:#b2b4bf}#ae-datastore-explorer-c{_margin-right:-3000px;_position:relative;_width:100%}#ae-datastore-explorer form dt{margin:1em 0 0 0}#ae-datastore-explorer #ae-datastore-explorer-labels{margin:0 0 3px}#ae-datastore-explorer-header .ae-action{margin-left:1em}#ae-datastore-explorer .id{white-space:nowrap}#ae-datastore-explorer caption{text-align:right;padding:5px}#ae-datastore-explorer-submit{margin-top:5px}#ae-datastore-explorer-namespace{margin-top:7px;margin-right:5px}#ae-datastore-stats-namespace-input,#ae-datastore-explorer-namespace-query,#ae-datastore-explorer-namespace-create{width:200px}#ae-datastore-explorer-gql-spacer{margin-top:22px}h4 #ae-datastore-explorer-gql-label{font-weight:normal}#ae-datastore-form em{font-style:normal;font-weight:normal;margin:0 0 0 .2em;color:#666}#ae-datastore-form dt{font-weight:bold}#ae-datastore-form dd{margin:.4em 0 .3em 1.5em;overflow:auto;zoom:1}#ae-datastore-form dd em{width:4em;float:left}#ae-datastore-form dd.ae-last{margin-bottom:1em}#ae-datastore-explorer-tabs-content{margin-bottom:1em}#ae-datastore-explorer-list .ae-label-row,#ae-datastore-explorer-new .ae-label-row{float:left;padding-top:.2em}#ae-datastore-explorer-list .ae-input-row,#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-input-row,#ae-datastore-explorer-new .ae-btn-row{margin-left:6em}#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-btn-row{margin-bottom:0}.ae-datastore-index-name{font-size:1.2em;font-weight:bold}.ae-table .ae-datastore-index-defs{padding-left:20px}.ae-datastore-index-defs-row{border-top:1px solid #ddd}.ae-datastore-index-defs .ae-unimportant{font-size:.8em}.ae-datastore-index-status{border:1px solid #c0dfbf;background:#f3f7f3;margin:0 25px 0 0;padding:3px}#ae-datastore-index-status-col{width:20%}#ae-datastore-index-stat-col{width:20%}.ae-datastore-index-status-Building{border-color:#edebcd;background:#fefdec}.ae-datastore-index-status-Deleting{border-color:#ccc;background:#eee}.ae-datastore-index-status-Error{border-color:#ffd3b4;background:#ffeae0}.ae-datastore-pathlink{font-size:.9em}#ae-datastore-explorer-max-datastore-viewer-columns-form{float:right}#ae-datastore-explorer-max-datastore-viewer-columns-hint{position:absolute;width:14em;visibility:hidden;box-shadow:0 15px 50px #777;z-index:2}#ae-datastore-stats-top-level-c{padding-bottom:1em;margin-bottom:1em;border-bottom:1px solid #e5ecf9}#ae-datastore-stats-top-level{width:100%}#ae-datastore-stats-piecharts-c{margin-bottom:1em}.ae-datastore-stats-piechart-label{font-size:.85em;font-weight:normal;text-align:center;padding:0}#ae-datastore-stats-property-type{width:60%}#ae-datastore-stats-size-all{width:20%}#ae-datastore-stats-index-size-all{width:20%}#ae-datastore-stats-property-name{width:40%}#ae-datastore-stats-type{width:10%}#ae-datastore-stats-size-entity{width:15%}#ae-datastore-stats-index-size-entity{width:15%}#ae-datastore-stats-percentage-size-entity{width:20%}#ae-datastore-blob-filter-form{margin-bottom:1em}#ae-datastore-blob-query-filter-label{padding-right:.5em}#ae-datastore-blob-filter-contents{padding-top:.5em}#ae-datastore-blob-date-after,#ae-datastore-blob-date-before{float:left}#ae-datastore-blob-date-after{margin-right:1em}#ae-datastore-blob-order label{font-weight:normal}#ae-datastore-blob-col-check{width:2%}#ae-datastore-blob-col-file{width:45%}#ae-datastore-blob-col-type{width:14%}#ae-datastore-blob-col-size{width:16%}#ae-blobstore-col-date{width:18%}#ae-blob-detail-filename{padding-bottom:0}#ae-blob-detail-filename span{font-weight:normal}#ae-blob-detail-key{font-size:85%}#ae-blob-detail-preview{margin-top:1em}#ae-blob-detail-dl{text-align:right}.ae-deployment-add-labels{padding:0 5px 0 20px}.ae-deployment-button-cell{width:95px}#ae-deployment-dm-dialog{width:400px}.ae-deployment-dm-selector{margin:20px 2px 20px 5px}#ae-deployment-exp-add{margin-top:5px}#ae-deployment-exp-contents{margin-top:5px;overflow:hidden}#ae-deployment-exp-desc{margin-bottom:15px}#ae-deployment-exp-div{background-color:#e5ecf9;border:1px solid #c5d7ef;margin:20px 0;padding:7px 4px}#ae-deployment-exp-hdr{font-weight:bold;margin:5px 0 5px}#ae-deployment-exp-tbl{width:400px}#ae-deployment-exp-toggle{font-weight:bold}.ae-deployment-set-button{width:22px}.ae-deployment-traffic-input{width:30px}.ae-deployment-change-state-form{display:inline}.ae-deployment-change-state-submit{background:transparent;text-decoration:underline;border:none;outline:none;cursor:pointer;color:#00c;padding:0 0 0 .1em}#ae-domain-admins-list li{margin-bottom:.3em}#ae-domain-admins-list button{margin-left:.5em}#ae-new-app-dialog-c{width:500px}#ae-new-app-dialog-c .g-section{margin-bottom:1em}p.light-note{color:#555}.ae-bottom-message{margin-top:1em}#domsettings-form div.ae-radio{margin-left:1.7em}#domsettings-form div.ae-radio input{margin-left:-1.47em;float:left}#ae-logs-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-logs{background-color:#c5d7ef;padding:1px;line-height:1.65}#ae-logs .ae-table-caption{border:0}#ae-logs-c ol,#ae-logs-c li{list-style:none;padding:0;margin:0}#ae-logs-c li li{margin:0 0 0 3px;padding:0 0 0 17px}.ae-log-noerror{padding-left:23px}#ae-logs-form .goog-inline-block{margin-top:0}.ae-logs-usage-info{padding-left:.5em}.ae-logs-reqlog .snippet{margin:.1em}.ae-logs-applog .snippet{color:#666}.ae-logs-severity{display:block;float:left;height:1.2em;width:1.2em;line-height:1.2;text-align:center;text-transform:capitalize;font-weight:bold;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px}.ae-logs-severity-4{background-color:#f22;color:#000}.ae-logs-severity-3{background-color:#f90;color:#000}.ae-logs-severity-2{background-color:#fd0}.ae-logs-severity-1{background-color:#3c0;color:#000}.ae-logs-severity-0{background-color:#09f;color:#000}#ae-logs-legend{margin:1em 0 0 0}#ae-logs-legend ul{list-style:none;margin:0;padding:0}#ae-logs-legend li,#ae-logs-legend strong{float:left;margin:0 1em 0 0}#ae-logs-legend li span{margin-right:.3em}.ae-logs-timestamp{padding:0 5px;font-size:85%}#ae-logs-form-c{margin-bottom:5px;padding-bottom:.5em;padding-left:1em}#ae-logs-form{padding:.3em 0 0}#ae-logs-form .ae-label-row{float:left;padding-top:.2em;margin-right:0.539em}#ae-logs-form .ae-input-row,#ae-logs-form .ae-btn-row{margin-left:4em}#ae-logs-form .ae-btn-row{margin-bottom:0}#ae-logs-requests-c{margin-bottom:.1em}#ae-logs-requests-c input{margin:0}#ae-logs-requests-all-label{margin-right:0.539em}#ae-logs-form-options{margin-top:8px}#ae-logs-tip{margin:.2em 0}#ae-logs-expand{margin-right:.2em}#ae-logs-severity-level-label{margin-top:.3em;display:block}#ae-logs-filter-hint-labels-list{margin:2px 0}#ae-logs-filter-hint-labels-list span{position:absolute}#ae-logs-filter-hint-labels-list ul{margin-left:5.5em;padding:0}#ae-logs-filter-hint-labels-list li{float:left;margin-right:.4em;line-height:1.2}.ae-toggle .ae-logs-getdetails,.ae-toggle pre{display:none}.ae-log-expanded .ae-toggle pre{display:block}#ae-logs-c .ae-log .ae-toggle{cursor:default;background:none;padding-left:0}#ae-logs-c .ae-log .ae-toggle h5{cursor:pointer;background-position:0 .55em;background-repeat:no-repeat;padding-left:17px}.ae-log .ae-plus h5{background-image:url('/img/wgt/plus.gif')}.ae-log .ae-minus h5{background-image:url('/img/wgt/minus.gif')}.ae-log{overflow:hidden;background-color:#fff;padding:.3em 0;line-height:1.65;border-bottom:1px solid #c5d7ef}.ae-log .ae-even{background-color:#e9e9e9;border:0}.ae-log h5{font-weight:normal;white-space:nowrap;padding:.4em 0 0 0}.ae-log span,.ae-log strong{margin:0 .3em}.ae-log .ae-logs-snippet{color:#666}.ae-log pre,.ae-logs-expanded{padding:.3em 0 .5em 1.5em;margin:0;font-family:"Courier New"}.ae-log .file{font-weight:bold}.ae-log.ae-log-expanded .file{white-space:pre-wrap;word-wrap:break-word}.ae-logs-app .ae-logs-req{display:none}.ae-logs-req .ae-app,.ae-logs-both .ae-app{padding-left:1em}#ae-dos-blacklist-rejects-table{text-align:left}#ae-dash-quota-percent-col{width:3.5em}.ae-cron-status-ok{color:#008000;font-size:90%;font-weight:bold}.ae-cron-status-error{color:#a03;font-size:90%;font-weight:bold}#ae-cronjobs-table .ae-table td{vertical-align:top}#ae-tasks-table td{vertical-align:top}#ae-tasks-quota{margin:0 0 1em 0}#ae-tasks-quota .ae-dash-quota-bar{width:150px}#ae-tasks-quota #ae-dash-quota-bar-col,#ae-tasks-quota .ae-dash-quota-bar{width:200px}.ae-tasks-paused-row{color:#666;font-style:italic;font-weight:bold}#ae-tasks-quota .ae-quota-safety-limit{width:30%}#ae-tasks-table{margin-top:1em}#ae-tasks-queuecontrols{margin-top:1em;margin-bottom:1em}#ae-tasks-delete-col{width:1em}#ae-tasks-eta-col,#ae-tasks-creation-col{width:11em}#ae-tasks-actions-col{width:7em}#ae-tasks-retry-col{width:4em}#ae-tasks-execution-col{width:6em}#ae-tasks-body-col{width:6em}#ae-tasks-headers-col{width:7em}.ae-tasks-hex-column,.ae-tasks-ascii-column{width:16em}#ae-tasks-table .ae-tasks-arrow{text-align:center}.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.pln{color:#000}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec{color:#606}@media print{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.pln{color:#000}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
\ No newline at end of file
+/* Copyright 2014 Google Inc. All Rights Reserved. */
+html,body,div,h1,h2,h3,h4,h5,h6,p,img,dl,dt,dd,ol,ul,li,table,caption,tbody,tfoot,thead,tr,th,td,form,fieldset,embed,object,applet{margin:0;padding:0;border:0;}body{font-size:62.5%;font-family:Arial,sans-serif;color:#000;background:#fff}a{color:#00c}a:active{color:#f00}a:visited{color:#551a8b}table{border-collapse:collapse;border-width:0;empty-cells:show}ul{padding:0 0 1em 1em}ol{padding:0 0 1em 1.3em}li{line-height:1.5em;padding:0 0 .5em 0}p{padding:0 0 1em 0}h1,h2,h3,h4,h5{padding:0 0 1em 0}h1,h2{font-size:1.3em}h3{font-size:1.1em}h4,h5,table{font-size:1em}sup,sub{font-size:.7em}input,select,textarea,option{font-family:inherit;font-size:inherit}.g-doc,.g-doc-1024,.g-doc-800{font-size:130%}.g-doc{width:100%;text-align:left}.g-section{width:100%;vertical-align:top;display:inline-block}*:first-child+html .g-section{display:block}* html .g-section{overflow:hidden}@-moz-document url-prefix(''){.g-section{overflow:hidden}}@-moz-document url-prefix(''){.g-section,tt:default{overflow:visible}}.g-section,.g-unit{zoom:1}.g-split .g-unit{text-align:right}.g-split .g-first{text-align:left}.g-doc-1024{width:73.074em;min-width:950px;margin:0 auto;text-align:left}* html .g-doc-1024{width:71.313em}*+html .g-doc-1024{width:71.313em}.g-doc-800{width:57.69em;min-width:750px;margin:0 auto;text-align:left}* html .g-doc-800{width:56.3em}*+html .g-doc-800{width:56.3em}.g-tpl-160 .g-unit,.g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-unit .g-tpl-160 .g-unit{margin:0 0 0 160px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-tpl-160 .g-first,.g-tpl-160 .g-first{margin:0;width:160px;float:left}.g-tpl-160-alt .g-unit,.g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-160-alt .g-unit{margin:0 160px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-tpl-160-alt .g-first,.g-tpl-160-alt .g-first{margin:0;width:160px;float:right}.g-tpl-180 .g-unit,.g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-unit .g-tpl-180 .g-unit{margin:0 0 0 180px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-tpl-180 .g-first,.g-tpl-180 .g-first{margin:0;width:180px;float:left}.g-tpl-180-alt .g-unit,.g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-180-alt .g-unit{margin:0 180px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-tpl-180-alt .g-first,.g-tpl-180-alt .g-first{margin:0;width:180px;float:right}.g-tpl-300 .g-unit,.g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-unit .g-tpl-300 .g-unit{margin:0 0 0 300px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-tpl-300 .g-first,.g-tpl-300 .g-first{margin:0;width:300px;float:left}.g-tpl-300-alt .g-unit,.g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-300-alt .g-unit{margin:0 300px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-tpl-300-alt .g-first,.g-tpl-300-alt .g-first{margin:0;width:300px;float:right}.g-tpl-25-75 .g-unit,.g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75 .g-unit{width:74.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-tpl-25-75 .g-first,.g-tpl-25-75 .g-first{width:24.999%;float:left;margin:0}.g-tpl-25-75-alt .g-unit,.g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-unit{width:24.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-tpl-25-75-alt .g-first,.g-tpl-25-75-alt .g-first{width:74.999%;float:right;margin:0}.g-tpl-75-25 .g-unit,.g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25 .g-unit{width:24.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-tpl-75-25 .g-first,.g-tpl-75-25 .g-first{width:74.999%;float:left;margin:0}.g-tpl-75-25-alt .g-unit,.g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-unit{width:74.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-tpl-75-25-alt .g-first,.g-tpl-75-25-alt .g-first{width:24.999%;float:right;margin:0}.g-tpl-33-67 .g-unit,.g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67 .g-unit{width:66.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-tpl-33-67 .g-first,.g-tpl-33-67 .g-first{width:32.999%;float:left;margin:0}.g-tpl-33-67-alt .g-unit,.g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-unit{width:32.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-tpl-33-67-alt .g-first,.g-tpl-33-67-alt .g-first{width:66.999%;float:right;margin:0}.g-tpl-67-33 .g-unit,.g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33 .g-unit{width:32.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-tpl-67-33 .g-first,.g-tpl-67-33 .g-first{width:66.999%;float:left;margin:0}.g-tpl-67-33-alt .g-unit,.g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-unit{width:66.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-tpl-67-33-alt .g-first,.g-tpl-67-33-alt .g-first{width:32.999%;float:right;margin:0}.g-tpl-50-50 .g-unit,.g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50 .g-unit{width:49.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-tpl-50-50 .g-first,.g-tpl-50-50 .g-first{width:49.999%;float:left;margin:0}.g-tpl-50-50-alt .g-unit,.g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-unit{width:49.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-tpl-50-50-alt .g-first,.g-tpl-50-50-alt .g-first{width:49.999%;float:right;margin:0}.g-tpl-nest{width:auto}.g-tpl-nest .g-section{display:inline}.g-tpl-nest .g-unit,.g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest .g-unit{float:left;width:auto;margin:0}.g-tpl-nest-alt .g-unit,.g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest-alt .g-unit{float:right;width:auto;margin:0}.goog-button{border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px;font:normal normal normal 13px/13px Arial,sans-serif;color:#000;text-align:middle;text-decoration:none;text-shadow:0 1px 1px rgba(255,255,255,1);background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;margin:0;display:inline;display:-moz-inline-box;display:inline-block;*overflow:visible;padding:4px 8px 5px}a.goog-button,span.goog-button,div.goog-button{padding:4px 8px 5px}.goog-button:visited{color:#000}.goog-button{*display:inline}.goog-button:focus,.goog-button:hover{border-color:#000}.goog-button:active,.goog-button-active{color:#000;background-color:#bbb;border-color:#999 #bbb #bbb #999;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ddd),to(#fff));background-image:-moz-linear-gradient(top,#ddd,#fff);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#ffffff',StartColorstr='#dddddd',GradientType=0)}.goog-button[disabled],.goog-button[disabled]:active,.goog-button[disabled]:hover{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.goog-button{padding:5px 8px 4px	}.goog-button{*padding:4px 7px 2px}html>body input.goog-button,x:-moz-any-link,x:default,html>body button.goog-button,x:-moz-any-link,x:default{padding-top:3px;padding-bottom:2px}a.goog-button,x:-moz-any-link,x:default,span.goog-button,x:-moz-any-link,x:default,div.goog-button,x:-moz-any-link,x:default{padding:4px 8px 5px}.goog-button-fixed{padding-left:0!important;padding-right:0!important;width:100%}button.goog-button-icon-c{padding-top:1px;padding-bottom:1px}button.goog-button-icon-c{padding-top:3px	;padding-bottom:2px	}button.goog-button-icon-c{*padding-top:0;*padding-bottom:0}html>body button.goog-button-icon-c,x:-moz-any-link,x:default{padding-top:1px;padding-bottom:1px}.goog-button-icon{display:block;margin:0 auto;height:18px;width:18px}html>body .goog-inline-block{display:-moz-inline-box;display:inline-block;}.goog-inline-block{position:relative;display:inline-block}* html .goog-inline-block{display:inline}*:first-child+html .goog-inline-block{display:inline}.goog-custom-button{margin:0 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0)}.goog-custom-button-outer-box,.goog-custom-button-inner-box{border-style:solid;border-color:#bbb #999 #999 #bbb;vertical-align:top}.goog-custom-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-custom-button-inner-box{margin:0 -1px;border-width:0 1px;padding:3px 4px}* html .goog-custom-button-inner-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-outer-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-inner-box{left:-1px}*:first-child+html .goog-custom-button-collapse-right .goog-custom-button-inner-box{border-left-width:2px}*:first-child+html .goog-custom-button-collapse-left .goog-custom-button-inner-box{border-right-width:2px}*:first-child+html .goog-custom-button-collapse-right.goog-custom-button-collapse-left .goog-custom-button-inner-box{border-width:0 1px}*:first-child+html .goog-custom-button-rtl .goog-custom-button-inner-box{left:1px}::root .goog-custom-button,::root .goog-custom-button-outer-box{line-height:0}::root .goog-custom-button-inner-box{line-height:normal}.goog-custom-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-custom-button-disabled .goog-custom-button-outer-box,.goog-custom-button-disabled .goog-custom-button-inner-box{color:#333!important;border-color:#999!important}* html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-custom-button-hover .goog-custom-button-outer-box,.goog-custom-button-hover .goog-custom-button-inner-box{border-color:#000!important;}.goog-custom-button-active,.goog-custom-button-checked{background-color:#bbb;background-position:bottom left;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ddd),to(#fff));background:-moz-linear-gradient(top,#ddd,#fff);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#ffffff',StartColorstr='#dddddd',GradientType=0)}.goog-custom-button-focused .goog-custom-button-outer-box,.goog-custom-button-focused .goog-custom-button-inner-box,.goog-custom-button-focused.goog-custom-button-collapse-left .goog-custom-button-inner-box,.goog-custom-button-focused.goog-custom-button-collapse-left.goog-custom-button-checked .goog-custom-button-inner-box{border-color:#000}.goog-custom-button-collapse-right,.goog-custom-button-collapse-right .goog-custom-button-outer-box,.goog-custom-button-collapse-right .goog-custom-button-inner-box{margin-right:0}.goog-custom-button-collapse-left,.goog-custom-button-collapse-left .goog-custom-button-outer-box,.goog-custom-button-collapse-left .goog-custom-button-inner-box{margin-left:0}.goog-custom-button-collapse-left .goog-custom-button-inner-box{border-left:1px solid #fff}.goog-custom-button-collapse-left.goog-custom-button-checked .goog-custom-button-inner-box{border-left:1px solid #ddd}* html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}.goog-date-picker th,.goog-date-picker td{font-family:arial,sans-serif;text-align:center}.goog-date-picker th{font-size:.9em;font-weight:bold;color:#666667;background-color:#c3d9ff}.goog-date-picker td{vertical-align:middle;padding:2px 3px}.goog-date-picker{-moz-user-focus:normal;-moz-user-select:none;position:absolute;border:1px solid gray;float:left;font-family:arial,sans-serif;padding-left:1px;background:white}.goog-date-picker-menu{position:absolute;background:threedface;border:1px solid gray;-moz-user-focus:normal}.goog-date-picker-menu ul{list-style:none;margin:0;padding:0}.goog-date-picker-menu ul li{cursor:default}.goog-date-picker-menu-selected{background-color:#aaccee}.goog-date-picker td div{float:left}.goog-date-picker button{padding:0;margin:1px;border:1px outset gray}.goog-date-picker-week{padding:1px 3px}.goog-date-picker-wday{padding:1px 3px}.goog-date-picker-today-cont{text-align:left!important}.goog-date-picker-none-cont{text-align:right!important}.goog-date-picker-head td{text-align:center}.goog-date-picker-month{width:12ex}.goog-date-picker-year{width:6ex}.goog-date-picker table{border-collapse:collapse}.goog-date-picker-selected{background-color:#aaccee!important;color:blue!important}.goog-date-picker-today{font-weight:bold!important}.goog-date-picker-other-month{-moz-opacity:0.3;filter:Alpha(Opacity=30)}.sat,.sun{background:#eee}#button1,#button2{display:block;width:60px;text-align:center;margin:10px;padding:10px;font:normal .8em arial,sans-serif;border:1px solid #000}.goog-menu{position:absolute;color:#000;border:1px solid #b5b6b5;background-color:#f3f3f7;cursor:default;font:normal small arial,helvetica,sans-serif;margin:0;padding:0;outline:none}.goog-menuitem{padding:2px 5px;margin:0;list-style:none}.goog-menuitem-highlight{background-color:#4279a5;color:#fff}.goog-menuitem-disabled{color:#999}.goog-option{padding-left:15px!important}.goog-option-selected{background-image:none}.goog-option-selected .goog-menuitem-checkbox{background-image:url('/img/check.gif');background-position:4px 50%;background-repeat:no-repeat}.goog-menuseparator{position:relative;margin:2px 0;border-top:1px solid #999;padding:0;outline:none}.goog-submenu{position:relative}.goog-submenu-arrow{position:absolute;display:block;width:11px;height:11px;right:3px;top:4px;background-image:url('/img/menu-arrows.gif');background-repeat:no-repeat;background-position:0 0;font-size:1px}.goog-menuitem-highlight .goog-submenu-arrow{background-position:0 -11px}.goog-menuitem-disabled .goog-submenu-arrow{display:none}.goog-menu-filter{margin:2px;border:1px solid silver;background:white;overflow:hidden}.goog-menu-filter div{color:gray;position:absolute;padding:1px}.goog-menu-filter input{margin:0;border:0;background:transparent;width:100%}.goog-menuitem-partially-checked{background-image:url('/img/check-outline.gif');background-position:4px 50%;background-repeat:no-repeat}.goog-menuitem-fully-checked{background-image:url('/img/check.gif');background-position:4px 50%;background-repeat:no-repeat}.goog-menu-button{margin:0 2px 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;background:#ddd url("/img/button-bg.gif") repeat-x top left;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none}.goog-menu-button-outer-box,.goog-menu-button-inner-box{border-style:solid;border-color:#aaa;vertical-align:middle}.goog-menu-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-menu-button-inner-box{margin:0 -1px;border-width:0 1px;padding:0 4px 2px 4px}* html .goog-menu-button-inner-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-outer-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-inner-box{left:0}*:first-child+html .goog-menu-button-inner-box{left:-1px}*:first-child+html .goog-menu-button-rtl .goog-menu-button-inner-box{left:1px}::root .goog-menu-button,::root .goog-menu-button-outer-box,::root .goog-menu-button-inner-box{line-height:0}::root .goog-menu-button-caption,::root .goog-menu-button-dropdown{line-height:normal}.goog-menu-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-menu-button-disabled .goog-menu-button-outer-box,.goog-menu-button-disabled .goog-menu-button-inner-box,.goog-menu-button-disabled .goog-menu-button-caption,.goog-menu-button-disabled .goog-menu-button-dropdown{color:#333!important;border-color:#999!important}* html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-menu-button-hover .goog-menu-button-outer-box,.goog-menu-button-hover .goog-menu-button-inner-box{border-color:#9cf #69e #69e #7af!important;}.goog-menu-button-active,.goog-menu-button-open{background-color:#bbb;background-position:bottom left}.goog-menu-button-focused .goog-menu-button-outer-box,.goog-menu-button-focused .goog-menu-button-inner-box{border-color:#3366cc}.goog-menu-button-caption{padding:0 4px 0 0;vertical-align:middle}.goog-menu-button-rtl .goog-menu-button-caption{padding:0 0 0 4px}.goog-menu-button-dropdown{width:7px;background:url('/img/toolbar_icons.gif') no-repeat -176px;vertical-align:middle}.goog-flat-menu-button{margin:0 2px;padding:1px 4px;font:normal 95% Tahoma,Arial,sans-serif;color:#333;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;-moz-outline:none;border-width:1px;border-style:solid;border-color:#c9c9c9;background-color:#fff}.goog-flat-menu-button-disabled *{color:#999;border-color:#ccc;cursor:default}.goog-flat-menu-button-hover,.goog-flat-menu-button-hover{border-color:#9cf #69e #69e #7af!important;}.goog-flat-menu-button-active{background-color:#bbb;background-position:bottom left}.goog-flat-menu-button-focused{border-color:#3366cc}.goog-flat-menu-button-caption{padding-right:10px;vertical-align:middle}.goog-flat-menu-button-dropdown{width:7px;background:url('/img/toolbar_icons.gif') no-repeat -176px;vertical-align:middle}h1{font-size:1.8em}.g-doc{width:auto;margin:0 10px}.g-doc-1024{margin-left:10px}#ae-logo{background:url('//www.google.com/images/logos/app_engine_logo_sm.gif') 0 0 no-repeat;display:block;width:178px;height:30px;margin:4px 0 0 0}.ae-ir span{position:absolute;display:block;width:0;height:0;overflow:hidden}.ae-noscript{position:absolute;left:-5000px}#ae-lhs-nav{border-right:3px solid #e5ecf9}.ae-notification{margin-bottom:.6em;text-align:center}.ae-notification strong{display:block;width:55%;margin:0 auto;text-align:center;padding:.6em;background-color:#fff1a8;font-weight:bold}.ae-alert{font-weight:bold;background:url('/img/icn/warning.png') no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-info{background:url('/img/icn/icn-info.gif') no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-promo{padding:.5em .8em;margin:.6em 0;background-color:#fffbe8;border:1px solid #fff1a9;text-align:left}.ae-promo strong{position:relative;top:.3em}.ae-alert-text,.ae-warning-text{background-color:transparent;background-position:right 1px;padding:0 18px 0 0}.ae-alert-text{color:#c00}.ae-warning-text{color:#f90}.ae-alert-c span{display:inline-block}.ae-message{border:1px solid #e5ecf9;background-color:#f6f9ff;margin-bottom:1em;padding:.5em}.ae-errorbox{border:1px solid #f00;background-color:#fee;margin-bottom:1em;padding:1em}#bd .ae-errorbox ul{padding-bottom:0}.ae-form dt{font-weight:bold}.ae-form dt em,.ae-field-hint{margin-top:.2em;color:#666667;font-size:.85em}.ae-field-hint-inline{color:#666667;font-size:.85em;display:inline}.ae-field-yyyymmdd,.ae-field-hhmmss{width:6em}.ae-field-hint-hhmmss{margin-left:2.3em}.ae-form label{display:block;margin:0 0 .2em 0;font-weight:bold}.ae-radio{margin-bottom:.3em}.ae-radio label{display:inline}.ae-form dd,.ae-input-row{margin-bottom:.6em}.ae-input-row-group{border:1px solid #fff1a9;background:#fffbe8;padding:8px}.ae-btn-row{margin-top:1.4em;margin-bottom:1em}.ae-btn-row-note{padding:5px 0 6px 0}.ae-btn-row-note span{padding-left:18px;padding-right:.5em;background:transparent url('/img/icn/icn-info.gif') 0 0 no-repeat}.ae-btn-primary{font-weight:bold}form .ae-cancel{margin-left:.5em}.ae-submit-inline{margin-left:.8em}.ae-radio-bullet{width:20px;float:left}.ae-label-hanging-indent{margin-left:5px}.ae-divider{margin:0 .6em 0 .5em}.ae-nowrap{white-space:nowrap}.ae-pre-wrap{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;_white-space:pre;}wbr:after{content:"\00200b"}a button{text-decoration:none}.ae-alert ul{margin-bottom:.75em;margin-top:.25em;line-height:1.5em}.ae-alert h4{color:#000;font-weight:bold;padding:0 0 .5em}.ae-form-simple-list{list-style-type:none;padding:0;margin-bottom:1em}.ae-form-simple-list li{padding:.3em 0 .5em .5em;border-bottom:1px solid #c3d9ff}div.ae-datastore-index-to-delete,div.ae-datastore-index-to-build{color:#aaa}#hd p{padding:0}#hd li{display:inline}ul{padding:0 0 1em 1.2em}#ae-userinfo{text-align:right;white-space:nowrap;}#ae-userinfo ul{padding-bottom:0;padding-top:5px}#ae-appbar-lrg{margin:0 0 1.25em 0;padding:.25em .5em;background-color:#e5ecf9;border-top:1px solid #36c}#ae-appbar-lrg h1{font-size:1.2em;padding:0}#ae-appbar-lrg h1 span{font-size:80%;font-weight:normal}#ae-appbar-lrg form{display:inline;padding-right:.1em;margin-right:.5em}#ae-appbar-lrg strong{white-space:nowrap}#ae-appbar-sml{margin:0 0 1.25em 0;height:8px;padding:0 .5em;background:#e5ecf9}.ae-rounded-sml{border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px}#ae-appbar-lrg a{margin-top:.3em}#ae-engine-version-bar{margin:0 0 1em}#ae-engine-version-bar>.g-unit{width:auto}a.ae-ext-link,a span.ae-ext-link{background:url('/img/icn/icn-open-in-new-window.png') no-repeat right;padding-right:18px;margin-right:8px}.ae-no-pad{padding-left:1em}.ae-message h4{margin-bottom:.3em;padding-bottom:0}#ft{text-align:center;margin:2.5em 0 1em;padding-top:.5em;border-top:2px solid #c3d9ff}#bd h3{font-weight:bold;font-size:1.4em}#bd h3 .ae-apps-switch{font-weight:normal;font-size:.7em;margin-left:2em}#bd p{padding:0 0 1em 0}#ae-content{padding-left:1em}.ae-unimportant{color:#666}.ae-new-usr td{border-top:1px solid #ccccce;background-color:#ffe}.ae-error-td td{border:2px solid #f00;background-color:#fee}.ae-delete{cursor:pointer;border:none;background:transparent;}.ae-btn-large{background:#039 url('/img/icn/button_back.png') repeat-x;color:#fff;font-weight:bold;font-size:1.2em;padding:.5em;border:2px outset #000;cursor:pointer}.ae-breadcrumb{margin:0 0 1em}.ae-disabled,a.ae-disabled,a.ae-disabled:hover,a.ae-disabled:active{color:#666!important;text-decoration:none!important;cursor:default!important;opacity:.4!important;-moz-opacity:.4!important;filter:alpha(opacity=40)!important}input.ae-readonly{border:2px solid transparent;border-left:0;background-color:transparent}span.ae-text-input-clone{padding:5px 5px 5px 0}.ae-loading{opacity:.4;-moz-opacity:.4;filter:alpha(opacity=40)}.ae-tip{margin:1em 0;background:url('/img/tip.png') top left no-repeat;padding:2px 0 0 25px}sup.ae-new-sup{color:red}sup.ae-new-sup a{border-bottom:1px solid red;color:red;text-decoration:none}.ae-action{color:#00c;cursor:pointer;text-decoration:underline}.ae-toggle{padding-left:16px;background-position:left center;background-repeat:no-repeat;cursor:pointer}.ae-minus{background-image:url('/img/wgt/minus.gif')}.ae-plus{background-image:url('/img/wgt/plus.gif')}.ae-print{background-image:url('/img/print.gif');padding-left:19px}.ae-download{background:url('/img/download.png') left center no-repeat;padding-left:22px}.ae-currency,.ae-table thead th.ae-currency{text-align:right;white-space:nowrap}#ae-loading{font-size:1.2em;position:absolute;text-align:center;top:0;width:100%}#ae-loading div{margin:0 auto;background:#fff1a9;width:5em;font-weight:bold;padding:4px 10px;-moz-border-radius-bottomleft:3px;-moz-border-radius-bottomright:3px;-webkit-border-radius-bottomleft:3px;-webkit-border-radius-bottomright:3px}.ae-occlude{filter:alpha(opacity=0);position:absolute}.g-tpl-66-34 .g-unit,.g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-unit .g-tpl-66-34 .g-unit{display:inline;margin:0;width:33.999%;float:right}.g-unit .g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-tpl-66-34 .g-first,.g-tpl-66-34 .g-first{display:inline;margin:0;width:65.999%;float:left}.ae-ie6-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}h2.ae-section-header{background:#e5ecf9;padding:.2em .4em;margin-bottom:.5em}.ae-field-span{padding:3px 0}ul.ae-admin-list li{margin:0 0;padding:.1em 0}#ae-feedback-bar{background-color:#f9edbe;border:1px solid #f0c36d;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.2);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.2);box-shadow:0 2px 4px rgba(0,0,0,0.2);left:280px;margin-top:-5px;overflow:hidden;padding:10px 16px;position:fixed;text-align:center;z-index:999}.ae-feedback-info{margin-bottom:7px}.ae-feedback-option{color:#222;display:inline-block;font-size:0.87em;vertical-align:bottom;width:75px}.ae-feedback-close-icon{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAARUlEQVR42mMon7n7fwWZmAFEkAOorxmXYdjUoWhG9g+6QnRxnDYjK8RnIFY/YwtZkgIMn0ba2Ey2n6kS2mTFM/2TJ7kYAJSLDRhvVX1GAAAAAElFTkSuQmCC') no-repeat 0 0;float:right;height:15px;margin-right:-11px;width:15px}select{font:13px/13px Arial,sans-serif;color:#000;border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;-webkit-border-radius:2px;-moz-border-radius:2px;background:#eee;background:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;padding:2px 1px;margin:0}select:hover{border-color:#000}select[disabled],select[disabled]:active{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.ae-table-plain{border-collapse:collapse;width:100%}.ae-table{border:1px solid #c5d7ef;border-collapse:collapse;width:100%}#bd h2.ae-table-title{background:#e5ecf9;margin:0;color:#000;font-size:1em;padding:3px 0 3px 5px;border-left:1px solid #c5d7ef;border-right:1px solid #c5d7ef;border-top:1px solid #c5d7ef}.ae-table-caption,.ae-table caption{border:1px solid #c5d7ef;background:#e5ecf9;-moz-margin-start:-1px}.ae-table caption{padding:3px 5px;text-align:left}.ae-table th,.ae-table td{background-color:#fff;padding:.35em 1em .25em .35em;margin:0}.ae-table thead th{font-weight:bold;text-align:left;background:#c5d7ef;vertical-align:bottom}.ae-table thead th .ae-no-bold{font-weight:normal}.ae-table tfoot tr td{border-top:1px solid #c5d7ef;background-color:#e5ecf9}.ae-table td{border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even>td,.ae-even th,.ae-even-top td,.ae-even-tween td,.ae-even-bottom td,ol.ae-even{background-color:#e9e9e9;border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even-top td{border-bottom:0}.ae-even-bottom td{border-top:0}.ae-even-tween td{border:0}.ae-table .ae-tween td{border:0}.ae-table .ae-tween-top td{border-bottom:0}.ae-table .ae-tween-bottom td{border-top:0}#bd .ae-table .cbc{width:1.5em;padding-right:0}.ae-table #ae-live td{background-color:#ffeac0}.ae-table-fixed{table-layout:fixed}.ae-table-fixed td,.ae-table-nowrap{overflow:hidden;white-space:nowrap}.ae-paginate strong{margin:0 .5em}tfoot .ae-paginate{text-align:right}.ae-table-caption .ae-paginate,.ae-table-caption .ae-orderby{padding:2px 5px}.modal-dialog{background:#c1d9ff;border:1px solid #3a5774;color:#000;padding:4px;position:absolute;font-size:1.3em;-moz-box-shadow:0 1px 4px #333;-webkit-box-shadow:0 1px 4px #333;box-shadow:0 1px 4px #333}.modal-dialog a,.modal-dialog a:link,.modal-dialog a:visited{color:#06c;cursor:pointer}.modal-dialog-bg{background:#666;left:0;position:absolute;top:0}.modal-dialog-title{background:#e0edfe;color:#000;cursor:pointer;font-size:120%;font-weight:bold;padding:8px 15px 8px 8px;position:relative;_zoom:1;}.modal-dialog-title-close{background:#e0edfe url('https://ssl.gstatic.com/editor/editortoolbar.png') no-repeat -528px 0;cursor:default;height:15px;position:absolute;right:10px;top:8px;width:15px;vertical-align:middle}.modal-dialog-buttons,.modal-dialog-content{background-color:#fff;padding:8px}.modal-dialog-buttons button{margin-right:.75em}.goog-buttonset-default{font-weight:bold}.goog-tab{position:relative;border:1px solid #8ac;padding:4px 9px;color:#000;background:#e5ecf9;border-top-left-radius:2px;border-top-right-radius:2px;-moz-border-radius-topleft:2px;-webkit-border-top-left-radius:2px;-moz-border-radius-topright:2px;-webkit-border-top-right-radius:2px}.goog-tab-bar-top .goog-tab{margin:1px 4px 0 0;border-bottom:0;float:left}.goog-tab-bar-bottom .goog-tab{margin:0 4px 1px 0;border-top:0;float:left}.goog-tab-bar-start .goog-tab{margin:0 0 4px 1px;border-right:0}.goog-tab-bar-end .goog-tab{margin:0 1px 4px 0;border-left:0}.goog-tab-hover{text-decoration:underline;cursor:pointer}.goog-tab-disabled{color:#fff;background:#ccc;border-color:#ccc}.goog-tab-selected{background:#fff!important;color:black;font-weight:bold}.goog-tab-bar-top .goog-tab-selected{top:1px;margin-top:0;padding-bottom:5px}.goog-tab-bar-bottom .goog-tab-selected{top:-1px;margin-bottom:0;padding-top:5px}.goog-tab-bar-start .goog-tab-selected{left:1px;margin-left:0;padding-right:9px}.goog-tab-bar-end .goog-tab-selected{left:-1px;margin-right:0;padding-left:9px}.goog-tab-content{padding:.1em .8em .8em .8em;border:1px solid #8ac;border-top:none}.goog-tab-bar{position:relative;margin:0 0 0 5px;border:0;padding:0;list-style:none;cursor:default;outline:none}.goog-tab-bar-clear{border-top:1px solid #8ac;clear:both;height:0;overflow:hidden}.goog-tab-bar-start{float:left}.goog-tab-bar-end{float:right}* html .goog-tab-bar-start{margin-right:-3px}* html .goog-tab-bar-end{margin-left:-3px}#ae-nav ul{list-style-type:none;margin:0;padding:1em 0}#ae-nav ul li{padding-left:.5em}#ae-nav .ae-nav-selected{color:#000;display:block;font-weight:bold;background-color:#e5ecf9;margin-right:-1px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px}#ae-nav .ae-nav-bold{font-weight:bold}#ae-nav ul li span.ae-nav-disabled{color:#666}#ae-nav ul ul{margin:0;padding:0 0 0 .5em}#ae-nav ul ul li{padding-left:.5em}#ae-nav ul li a,#ae-nav ul li span,#ae-nav ul ul li a{padding-left:.5em}#ae-nav li a:link,#ae-nav li a:visited{color:#00c}.ae-nav-group{padding:.5em;margin:0 .75em 0 0;background-color:#fffbe8;border:1px solid #fff1a9}.ae-nav-group h4{font-weight:bold;padding:auto auto .5em .5em;padding-left:.4em;margin-bottom:.5em;padding-bottom:0}.ae-nav-group ul{margin:0 0 .5em 0;padding:0 0 0 1.3em;list-style-type:none}.ae-nav-group ul li{padding-bottom:.5em}.ae-nav-group li a:link,.ae-nav-group li a:visited{color:#00c}.ae-nav-group li a:hover{color:#00c}@media print{body{font-size:13px;width:8.5in;background:#fff}table,.ae-table-fixed{table-layout:automatic}tr{display:table-row!important}.g-doc-1024{width:8.5in}#ae-appbar-lrg,.ae-table-caption,.ae-table-nowrap,.ae-nowrap,th,td{overflow:visible!important;white-space:normal!important;background:#fff!important}.ae-print,.ae-toggle{display:none}#ae-lhs-nav-c{display:none}#ae-content{margin:0;padding:0}.goog-zippy-collapsed,.goog-zippy-expanded{background:none!important;padding:0!important}}#ae-admin-dev-table{margin:0 0 1em 0}.ae-admin-dev-tip,.ae-admin-dev-tip.ae-tip{margin:-0.31em 0 2.77em}#ae-sms-countryselect{margin-right:.5em}#ae-admin-enable-form{margin-bottom:1em}#ae-admin-services-c{margin-top:2em}#ae-admin-services{padding:0 0 0 3em;margin-bottom:1em;font-weight:bold}#ae-admin-logs-table-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-admin-logs-table{margin:0;padding:0}#ae-admin-logs-filters{padding:3px 0 3px 5px}#ae-admin-logs-pagination{padding:6px 5px 0 0;text-align:right;width:45%}#ae-admin-logs-pagination span.ae-disabled{color:#666;background-color:transparent}#ae-admin-logs-table td{white-space:nowrap}#ae-admin-logs-timezone{float:right;margin-bottom:7px}#ae-storage-content div.ae-alert{padding-bottom:5px}#ae-admin-performance-form input[type=text]{width:2em}.ae-admin-performance-value{font-weight:normal}.ae-admin-performance-static-value{color:#666}#ae-admin-performance-frontend-class{margin-left:0.5em}.goog-slider-horizontal,.goog-twothumbslider-horizontal{position:relative;width:502px;height:7px;display:block;outline:0;margin:1.0em 0 0.9em 3em}.ae-slider-rail:before{position:relative;top:-0.462em;float:left;content:'Min';margin:0 0 0 -3em;color:#999}.ae-slider-rail{position:absolute;background-color:#d9d9d9;top:0;right:8px;bottom:0;left:8px;border:solid 1px;border-color:#a6a6a6 #b3b3b3 #bfbfbf;border-radius:5px}.ae-slider-rail:after{position:relative;top:-0.462em;float:right;content:'Max';margin:0 -3em 0 0;color:#999}.goog-slider-horizontal .goog-slider-thumb,.goog-twothumbslider-horizontal .goog-twothumbslider-value-thumb,.goog-twothumbslider-horizontal .goog-twothumbslider-extent-thumb{position:absolute;width:17px;height:17px;background:transparent url('/img/slider_thumb-down.png') no-repeat;outline:0}.goog-slider-horizontal .goog-slider-thumb{top:-5px}.goog-twothumbslider-horizontal .goog-twothumbslider-value-thumb{top:-11px}.goog-twothumbslider-horizontal .goog-twothumbslider-extent-thumb{top:2px;background-image:url('/img/slider_thumb-up.png')}.ae-admin-performance-scale{position:relative;display:inline-block;width:502px;margin:0 0 2.7em 3em}.ae-admin-performance-scale .ae-admin-performance-scale-start{position:absolute;display:inline-block;top:0;width:100%;text-align:left}.ae-admin-performance-scale .ae-admin-performance-scale-mid{position:absolute;display:inline-block;top:0;width:100%;text-align:center}.ae-admin-performance-scale .ae-admin-performance-scale-end{position:absolute;display:inline-block;top:0;width:100%;text-align:right}.ae-pagespeed-controls{margin:0 0 1em 8px}.ae-pagespeed-controls label{display:inline;font-weight:normal}#ae-pagespeed-flush-cache{margin-left:1em}#ae-pagespeed-flush-cache-status{margin-left:1em;font-weight:bold}.ae-absolute-container{display:inline-block;width:100%}.ae-hidden-range{display:none}.ae-default-version-radio-column{width:1em}#ae-settings-builtins-change{margin-bottom:1em}#ae-memcache-full-flush-warning{color:#c00}#ae-memcache-partial-flush-warning{color:#c00}.prettyprint{background-color:#fafafa;font:1em "Droid Sans Mono",monospace;margin-right:20px}#formatted-performance-settings>div{float:left}#formatted-performance-settings:after{clear:both;content:"";display:table}#dispatch-table{table-layout:fixed}#ae-billing-form-c{_margin-right:-3000px;_position:relative;_width:100%}.ae-rounded-top-small{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px}.ae-progress-content{height:400px}#ae-billing-tos{text-align:left;width:100%;margin-bottom:.5em}.ae-billing-budget-section{margin-bottom:1.5em}.ae-billing-budget-section .g-unit,.g-unit .ae-billing-budget-section .g-unit,.g-unit .g-unit .ae-billing-budget-section .g-unit{margin:0 0 0 11em;width:auto;float:none}.g-unit .g-unit .ae-billing-budget-section .g-first,.g-unit .ae-billing-budget-section .g-first,.ae-billing-budget-section .g-first{margin:0;width:11em;float:left}#ae-billing-form .ae-btn-row{margin-left:11em}#ae-billing-form .ae-btn-row .ae-info{margin-top:10px}#ae-billing-checkout{width:150px;float:left}#ae-billing-alloc-table{border:1px solid #c5d7ef;border-bottom:none;width:100%;margin-top:.5em}#ae-billing-alloc-table th,#ae-billing-alloc-table td{padding:.35em 1em .25em .35em;border-bottom:1px solid #c5d7ef;color:#000;white-space:nowrap}.ae-billing-resource{background-color:transparent;font-weight:normal}#ae-billing-alloc-table tr th span{font-weight:normal}#ae-billing-alloc-table tr{vertical-align:baseline}#ae-billing-alloc-table th{white-space:nowrap}#ae-billing-alloc-table .ae-editable span.ae-text-input-clone,#ae-billing-alloc-table .ae-readonly input{display:none}#ae-billing-alloc-table .ae-readonly span.ae-text-input-clone,#ae-billing-alloc-table .ae-editable input{display:inline}#ae-billing-alloc-table td span.ae-billing-warn-note,#ae-billing-table-errors .ae-billing-warn-note{margin:0;background-repeat:no-repeat;display:inline-block;background-image:url('/img/icn/warning.png');text-align:right;padding-left:16px;padding-right:.1em;height:16px;font-weight:bold}#ae-billing-alloc-table td span.ae-billing-warn-note span,#ae-billing-table-errors .ae-billing-warn-note span{vertical-align:super;font-size:80%}#ae-billing-alloc-table td span.ae-billing-error-hidden,#ae-billing-table-errors .ae-billing-error-hidden{display:none}.ae-billing-percent{font-size:80%;color:#666;margin-left:3px}#ae-billing-week-info{margin-top:5px;line-height:1.4}#ae-billing-table-errors{margin-top:.3em}#ae-billing-allocation-noscript{margin-top:1.5em}#ae-billing-allocation-custom-opts{margin-left:2.2em}#ae-billing-settings h2{font-size:1em;display:inline}#ae-billing-settings p{padding:.3em 0 .5em}#ae-billing-settings-table{margin:.4em 0 .5em}#ae-settings-resource-col{width:19%}#ae-settings-budget-col{width:11%}#ae-billing-settings-table .ae-settings-budget-col{padding-right:2em}.ae-table th.ae-settings-unit-cell,.ae-table td.ae-settings-unit-cell,.ae-table th.ae-total-unit-cell,.ae-table td.ae-total-unit-cell{padding-left:1.2em}#ae-settings-unit-col{width:18%}#ae-settings-paid-col{width:15%}#ae-settings-free-col{width:15%}#ae-settings-total-col{width:22%}.ae-billing-inline-link{margin-left:.5em}.ae-billing-settings-section{margin-bottom:2em}#ae-billing-settings form{display:inline-block}#ae-billing-settings .ae-btn-row{margin-top:0.5em}#ae-billing-budget-setup-checkout{margin-bottom:0}#ae-billing-vat-c .ae-field-hint{width:85%}#ae-billing-checkout-note{margin-top:.8em}.ae-drachma-preset{background-color:#f6f9ff;margin-left:11em}.ae-drachma-preset p{margin-top:.5em}.ae-table thead th.ae-currency-th{text-align:right}#ae-billing-logs-date{width:15%}#ae-billing-logs-event{width:69%}#ae-billing-logs-amount{text-align:right;width:8%}#ae-billing-logs-balance{text-align:right;width:8%}#ae-billing-history-expand .ae-action{margin-left:1em}.ae-table .ae-billing-usage-premier,.ae-table .ae-billing-usage-report{width:100%;*width:auto;margin:0 0 1em 0}.ae-table .ae-billing-usage-report th,.ae-table .ae-billing-usage-premier th,.ae-billing-charges th{color:#666;border-top:0}.ae-table .ae-billing-usage-report th,.ae-table .ae-billing-usage-report td,.ae-table .ae-billing-usage-premier th,.ae-table .ae-billing-usage-premier td,.ae-billing-charges th,.ae-billing-charges td{background-color:transparent;padding:.4em 0;border-bottom:1px solid #ddd}.ae-table .ae-billing-usage-report tfoot td,.ae-billing-charges tfoot td{border-bottom:none}table.ae-billing-usage-report col.ae-billing-report-resource{width:30%}table.ae-billing-usage-report col.ae-billing-report-used{width:20%}table.ae-billing-usage-report col.ae-billing-report-free{width:16%}table.ae-billing-usage-report col.ae-billing-report-paid{width:17%}table.ae-billing-usage-report col.ae-billing-report-charge{width:17%}table.ae-billing-usage-premier col.ae-billing-report-resource{width:50%}table.ae-billing-usage-premier col.ae-billing-report-used{width:30%}table.ae-billing-usage-premier col.ae-billing-report-unit{width:20%}.ae-billing-change-resource{width:85%}.ae-billing-change-budget{width:15%}#ae-billing-always-on-label{display:inline}#ae-billing-budget-buffer-label{display:inline}.ae-billing-charges{width:50%}.ae-billing-charges-charge{text-align:right}.ae-billing-usage-report-container{padding:1em 1em 0 1em}#ae-billing-new-usage{background-color:#f6f9ff}.goog-zippy-expanded{background-image:url('/img/wgt/minus.gif');cursor:pointer;background-repeat:no-repeat;padding-left:17px}.goog-zippy-collapsed{background-image:url('/img/wgt/plus.gif');cursor:pointer;background-repeat:no-repeat;padding-left:17px}#ae-admin-logs-pagination{width:auto}.ae-usage-cycle-note{color:#555}#ae-billing-budget-widget .g-content{margin-bottom:0.5em;margin-right:0.5em}#ae-request-billing-dialog{width:800px}#ae-manage-billing-admins-form-cancel{color:#000}.ae-grace-period-resignup-outstanding-balance{color:red;font-weight:bold}.ae-billing-admins-table{width:100%}.ae-billing-admins-table td{width:33%;word-break:break-all;padding-bottom:5px}.b3iframe{width:100%}iframe{border:none}#ae-createapp-start{background-color:#c6d5f1;padding:1em;padding-bottom:2em;text-align:center}#ae-admin-app_id_alias-check,#ae-createapp-id-check{margin:0 0 0 1em}#ae-admin-app_id_alias-message{display:block;margin:.4em 0}#ae-createapp-id-content{width:100%}#ae-createapp-id-content td{vertical-align:top}#ae-createapp-id-td{white-space:nowrap;width:1%}#ae-createapp-id-td #ae-createapp-id-error{position:absolute;width:24em;padding-left:1em;white-space:normal}#ae-createapp-id-error-td{padding-left:1em}#ae-admin-dev-invite label{float:left;width:3.6em;position:relative;top:.3em}#ae-admin-dev-invite .ae-radio{margin-left:3.6em}#ae-admin-dev-invite .ae-radio label{float:none;width:auto;font-weight:normal;position:static}#ae-admin-dev-invite .goog-button{margin-left:3.6em}#ae-admin-dev-invite .ae-field-hint{margin-left:4.2em}#ae-admin-dev-invite .ae-radio .ae-field-hint{margin-left:0}.ae-you{color:#008000}#ae-authdomain-opts{margin-bottom:1em}#ae-authdomain-content .ae-input-text,#ae-authdomain-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-authdomain-opts a{margin-left:1em}#ae-authdomain-opts-hint{color:#666667;font-size:.85em;margin-top:.2em}#ae-authdomain-content #ae-authdomain-desc .ae-field-hint{margin-left:0}#ae-storage-opts{margin-bottom:1em}#ae-storage-content .ae-input-text,#ae-storage-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-storage-opts a{margin-left:1em}#ae-storage-opts-hint{color:#666667;font-size:.85em;margin-top:.2em}#ae-storage-content #ae-storage-desc .ae-field-hint{margin-left:0}#ae-location-opts{margin-bottom:1em}#ae-location-content .ae-input-text,#ae-location-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-location-opts a{margin-left:1em}#ae-location-opts-hint{color:#666667;font-size:.85em;margin-top:.2em}#ae-location-content #ae-storage-desc .ae-field-hint{margin-left:0}#ae-dash .g-section{margin:0 0 1em}#ae-dash * .g-section{margin:0}#ae-dash-quota .ae-alert{padding-left:1.5em}.ae-dash-email-disabled{background:url('/img/icn/exclamation_circle.png') no-repeat;margin-bottom:.5em;margin-top:.5em;min-height:16px;padding-left:1.5em}#ae-dash-email-disabled-footnote{font-weight:normal;margin:5px 0 0;padding-left:1.5em}#ae-dash-graph-c{border:1px solid #c5d7ef;padding:5px 0}#ae-dash-graph-change{margin:0 0 0 5px}#ae-dash-graph-img{background-color:#fff;display:block;margin-top:.5em;padding:5px}#ae-dash-graph-nodata{text-align:center}#ae-dash .ae-logs-severity{margin-right:.5em}#ae-dash .g-c{padding:0 0 0 .1em}#ae-dash .g-tpl-50-50 .g-unit .g-c{padding:0 0 0 1em}#ae-dash .g-tpl-50-50 .g-first .g-c{padding:0 1em 0 .1em}.ae-quota-warnings{background-color:#fffbe8;margin:0;padding:.5em .5em 0;text-align:left}.ae-quota-warnings div{padding:0 0 .5em}#ae-dash-quota-refresh-info{font-size:85%}#ae-dash #ae-dash-dollar-bucket-c #ae-dash-dollar-bucket{width:100%;float:none}#ae-dash #ae-dash-quota-bar-col,#ae-dash .ae-dash-quota-bar-col,#ae-dash .ae-dash-quota-bar{width:100px}#ae-dash-quotadetails #ae-dash-quota-bar-col,#ae-dash-quotadetails .ae-dash-quota-bar-col,#ae-dash-quotadetails .ae-dash-quota-bar{width:200px}#ae-dash-quota-percent-col,.ae-dash-quota-percent-col{width:3.5em}#ae-dash-quota-cost-col{width:15%}#ae-dash-quota-alert-col{width:3.5em}#ae-dash .ae-dash-quota-alert-td{padding:0}.ae-dash-quota-alert-td a{display:block;width:16px;height:16px}#ae-dash .ae-dash-quota-alert-td .ae-alert{display:block;width:16px;height:16px;margin:0;padding:0}#ae-dash .ae-dash-quota-alert-td .ae-dash-email-disabled{display:block;width:16px;height:16px;margin:0;padding:0}#ae-dash-quota tbody th{font-weight:normal}#ae-dash-quota caption{padding:0}#ae-dash-quota caption .g-c{padding:3px}.ae-dash-quota-bar{float:left;background-color:#c0c0c0;height:13px;margin:.1em 0 0 0;position:relative}.ae-dash-quota-footnote{margin:5px 0 0;font-weight:normal}.ae-quota-warning{background-color:#f90}.ae-quota-alert{background-color:#c00}.ae-quota-normal{background-color:#0b0}.ae-quota-alert-text{color:#c00}.ae-favicon-text{font-size:.85em}#ae-dash-popular{width:97%}#ae-dash-popular-reqsec-col{width:6.5em}#ae-dash-popular-req-col{width:7em}#ae-dash-popular-mcycles-col{width:9.5em}#ae-dash-popular-latency-col{width:7em}#ae-dash-popular .ae-unimportant{font-size:80%}#ae-dash-popular .ae-nowrap,#ae-dash-errors .ae-nowrap{margin-right:5px;overflow:hidden}#ae-dash-popular th span,#ae-dash-errors th span{font-size:.8em;font-weight:normal;display:block}#ae-dash-errors caption .g-unit{width:9em}#ae-dash-errors-count-col{width:5em}#ae-dash-errors-percent-col{width:7em}#ae-dash-graph-chart-type{float:left;margin-right:1em}#ae-apps-all strong.ae-disabled{color:#000;background:#eee}.ae-quota-resource{width:30%}.ae-quota-safety-limit{width:10%}#ae-quota-details h3{padding-bottom:0;margin-bottom:.25em}#ae-quota-details table{margin-bottom:1.75em}#ae-quota-details table.ae-quota-requests{margin-bottom:.5em}#ae-quota-refresh-note p{text-align:right;padding-top:.5em;padding-bottom:0;margin-bottom:0}#ae-quota-first-api.g-section{padding-bottom:0;margin-bottom:.25em}#ae-instances-summary-table,#ae-instances-details-table{margin-bottom:1em}.ae-instances-details-availability-image{float:left;margin-right:.5em}.ae-instances-vm-health-image{float:left;margin-right:.5em}.ae-instances-agent-warning-image{float:right}.ae-instances-small-text{font-size:80%}.ae-instances-small-text .ae-separator{color:#666}.ae-instances-highlight td{background-color:#fff1a8}.ae-instances-release-col{width:6em}.ae-appbar-superuser-message strong{color:red}#ae-backends-table tr{vertical-align:baseline}.ae-backends-class-reminder{font-size:80%;color:#666;margin-left:3px}#ae-appbar-engines,#ae-appbar-versions{display:inline}#ae-update{background-color:#fffbe8;border:1px solid}.ae-appbar-logs-invite{background-color:#eef66c;border-width:2px;border-color:yellow;border-radius:2px;float:right;padding:3px 16px}.ac-renderer{font:normal 13px Arial,sans-serif;position:absolute;background:#fff;border:1px solid #666;-moz-box-shadow:2px 2px 2px rgba(102,102,102,.4);-webkit-box-shadow:2px 2px 2px rgba(102,102,102,.4);width:202px}.ac-row{cursor:pointer;padding:.4em}.ac-highlighted{font-weight:bold}.ac-active{background-color:#b2b4bf}#ae-datastore-explorer-c{_margin-right:-3000px;_position:relative;_width:100%}#ae-datastore-explorer form dt{margin:1em 0 0 0}#ae-datastore-explorer #ae-datastore-explorer-labels{margin:0 0 3px}#ae-datastore-explorer-header .ae-action{margin-left:1em}#ae-datastore-explorer .id{white-space:nowrap}#ae-datastore-explorer caption{text-align:right;padding:5px}#ae-datastore-explorer-submit{margin-top:5px}#ae-datastore-explorer-namespace{margin-top:7px;margin-right:5px}#ae-datastore-stats-namespace-input,#ae-datastore-explorer-namespace-query,#ae-datastore-explorer-namespace-create{width:200px}#ae-datastore-explorer-gql-spacer{margin-top:22px}h4 #ae-datastore-explorer-gql-label{font-weight:normal}#ae-datastore-form em{font-style:normal;font-weight:normal;margin:0 0 0 .2em;color:#666}#ae-datastore-form dt{font-weight:bold}#ae-datastore-form dd{margin:.4em 0 .3em 1.5em;overflow:auto;zoom:1}#ae-datastore-form dd em{width:4em;float:left}#ae-datastore-form dd.ae-last{margin-bottom:1em}#ae-datastore-explorer-tabs-content{margin-bottom:1em}#ae-datastore-explorer-list .ae-label-row,#ae-datastore-explorer-new .ae-label-row{float:left;padding-top:.2em}#ae-datastore-explorer-list .ae-input-row,#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-input-row,#ae-datastore-explorer-new .ae-btn-row{margin-left:6em}#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-btn-row{margin-bottom:0}.ae-datastore-index-name{font-size:1.2em;font-weight:bold}.ae-table .ae-datastore-index-defs{padding-left:20px}.ae-datastore-index-defs-row{border-top:1px solid #ddd}.ae-datastore-index-defs .ae-unimportant{font-size:.8em}.ae-datastore-index-status{border:1px solid #c0dfbf;background:#f3f7f3;margin:0 25px 0 0;padding:3px}#ae-datastore-index-status-col{width:20%}#ae-datastore-index-stat-col{width:20%}.ae-datastore-index-status-Building{border-color:#edebcd;background:#fefdec}.ae-datastore-index-status-Deleting{border-color:#ccc;background:#eee}.ae-datastore-index-status-Error{border-color:#ffd3b4;background:#ffeae0}.ae-datastore-pathlink{font-size:.9em}#ae-datastore-explorer-max-datastore-viewer-columns-form{float:right}#ae-datastore-explorer-max-datastore-viewer-columns-hint{position:absolute;width:14em;visibility:hidden;box-shadow:0 15px 50px #777;z-index:2}#ae-datastore-stats-top-level-c{padding-bottom:1em;margin-bottom:1em;border-bottom:1px solid #e5ecf9}#ae-datastore-stats-top-level{width:100%}#ae-datastore-stats-piecharts-c{margin-bottom:1em}.ae-datastore-stats-piechart-label{font-size:.85em;font-weight:normal;text-align:center;padding:0}#ae-datastore-stats-property-type{width:60%}#ae-datastore-stats-size-all{width:20%}#ae-datastore-stats-index-size-all{width:20%}#ae-datastore-stats-property-name{width:40%}#ae-datastore-stats-type{width:10%}#ae-datastore-stats-size-entity{width:15%}#ae-datastore-stats-index-size-entity{width:15%}#ae-datastore-stats-percentage-size-entity{width:20%}#ae-datastore-blob-filter-form{margin-bottom:1em}#ae-datastore-blob-query-filter-label{padding-right:.5em}#ae-datastore-blob-filter-contents{padding-top:.5em}#ae-datastore-blob-date-after,#ae-datastore-blob-date-before{float:left}#ae-datastore-blob-date-after{margin-right:1em}#ae-datastore-blob-order label{font-weight:normal}#ae-datastore-blob-col-check{width:2%}#ae-datastore-blob-col-file{width:45%}#ae-datastore-blob-col-type{width:14%}#ae-datastore-blob-col-size{width:16%}#ae-blobstore-col-date{width:18%}#ae-blob-detail-filename{padding-bottom:0}#ae-blob-detail-filename span{font-weight:normal}#ae-blob-detail-key{font-size:85%}#ae-blob-detail-preview{margin-top:1em}#ae-blob-detail-dl{text-align:right}.ae-deployment-add-labels{padding:0 5px 0 20px}.ae-deployment-button-cell{width:95px}#ae-deployment-dm-dialog{width:400px}.ae-deployment-dm-selector{margin:20px 2px 20px 5px}#ae-deployment-exp-add{margin-top:5px}#ae-deployment-exp-contents{margin-top:5px;overflow:hidden}#ae-deployment-exp-desc{margin-bottom:15px}#ae-deployment-exp-div{background-color:#e5ecf9;border:1px solid #c5d7ef;margin:20px 0;padding:7px 4px}#ae-deployment-exp-hdr{font-weight:bold;margin:5px 0 5px}#ae-deployment-exp-tbl{width:400px}#ae-deployment-exp-toggle{font-weight:bold}.ae-deployment-set-button{width:22px}.ae-deployment-traffic-input{width:30px}.ae-deployment-change-state-form{display:inline}.ae-deployment-change-state-submit{background:transparent;text-decoration:underline;border:none;outline:none;cursor:pointer;color:#00c;padding:0 0 0 .1em}#ae-domain-admins-list li{margin-bottom:.3em}#ae-domain-admins-list button{margin-left:.5em}#ae-new-app-dialog-c{width:500px}#ae-new-app-dialog-c .g-section{margin-bottom:1em}p.light-note{color:#555}.ae-bottom-message{margin-top:1em}#domsettings-form div.ae-radio{margin-left:1.7em}#domsettings-form div.ae-radio input{margin-left:-1.47em;float:left}#ae-logs-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-logs{background-color:#c5d7ef;padding:1px;line-height:1.65}#ae-logs .ae-table-caption{border:0}#ae-logs-c ol,#ae-logs-c li{list-style:none;padding:0;margin:0}#ae-logs-c li li{margin:0 0 0 3px;padding:0 0 0 17px}.ae-log-noerror{padding-left:23px}#ae-logs-form .goog-inline-block{margin-top:0}.ae-logs-usage-info{padding-left:.5em}.ae-logs-reqlog .snippet{margin:.1em}.ae-logs-applog .snippet{color:#666}.ae-logs-severity{display:block;float:left;height:1.2em;width:1.2em;line-height:1.2;text-align:center;text-transform:capitalize;font-weight:bold;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px}.ae-logs-severity-4{background-color:#f22;color:#000}.ae-logs-severity-3{background-color:#f90;color:#000}.ae-logs-severity-2{background-color:#fd0}.ae-logs-severity-1{background-color:#3c0;color:#000}.ae-logs-severity-0{background-color:#09f;color:#000}#ae-logs-legend{margin:1em 0 0 0}#ae-logs-legend ul{list-style:none;margin:0;padding:0}#ae-logs-legend li,#ae-logs-legend strong{float:left;margin:0 1em 0 0}#ae-logs-legend li span{margin-right:.3em}.ae-logs-timestamp{padding:0 5px;font-size:85%}#ae-logs-form-c{margin-bottom:5px;padding-bottom:.5em;padding-left:1em}#ae-logs-form{padding:.3em 0 0}#ae-logs-form .ae-label-row{float:left;padding-top:.2em;margin-right:0.539em}#ae-logs-form .ae-input-row,#ae-logs-form .ae-btn-row{margin-left:4em}#ae-logs-form .ae-btn-row{margin-bottom:0}#ae-logs-requests-c{margin-bottom:.1em}#ae-logs-requests-c input{margin:0}#ae-logs-requests-all-label{margin-right:0.539em}#ae-logs-form-options{margin-top:8px}#ae-logs-tip{margin:.2em 0}#ae-logs-expand{margin-right:.2em}#ae-logs-severity-level-label{margin-top:.3em;display:block}#ae-logs-filter-hint-labels-list{margin:2px 0}#ae-logs-filter-hint-labels-list span{position:absolute}#ae-logs-filter-hint-labels-list ul{margin-left:5.5em;padding:0}#ae-logs-filter-hint-labels-list li{float:left;margin-right:.4em;line-height:1.2}.ae-toggle .ae-logs-getdetails,.ae-toggle pre{display:none}.ae-log-expanded .ae-toggle pre{display:block}#ae-logs-c .ae-log .ae-toggle{cursor:default;background:none;padding-left:0}#ae-logs-c .ae-log .ae-toggle h5{cursor:pointer;background-position:0 .55em;background-repeat:no-repeat;padding-left:17px}.ae-log .ae-plus h5{background-image:url('/img/wgt/plus.gif')}.ae-log .ae-minus h5{background-image:url('/img/wgt/minus.gif')}.ae-log{overflow:hidden;background-color:#fff;padding:.3em 0;line-height:1.65;border-bottom:1px solid #c5d7ef}.ae-log .ae-even{background-color:#e9e9e9;border:0}.ae-log h5{font-weight:normal;white-space:nowrap;padding:.4em 0 0 0}.ae-log span,.ae-log strong{margin:0 .3em}.ae-log .ae-logs-snippet{color:#666}.ae-log pre,.ae-logs-expanded{padding:.3em 0 .5em 1.5em;margin:0;font-family:"Courier New"}.ae-log .file{font-weight:bold}.ae-log.ae-log-expanded .file{white-space:pre-wrap;word-wrap:break-word}.ae-logs-app .ae-logs-req{display:none}.ae-logs-req .ae-app,.ae-logs-both .ae-app{padding-left:1em}#ae-dos-blacklist-rejects-table{text-align:left}#ae-dash-quota-percent-col{width:3.5em}.ae-cron-status-ok{color:#008000;font-size:90%;font-weight:bold}.ae-cron-status-error{color:#a03;font-size:90%;font-weight:bold}#ae-cronjobs-table .ae-table td{vertical-align:top}#ae-tasks-table td{vertical-align:top}#ae-tasks-quota{margin:0 0 1em 0}#ae-tasks-quota .ae-dash-quota-bar{width:150px}#ae-tasks-quota #ae-dash-quota-bar-col,#ae-tasks-quota .ae-dash-quota-bar{width:200px}.ae-tasks-paused-row{color:#666;font-style:italic;font-weight:bold}#ae-tasks-quota .ae-quota-safety-limit{width:30%}#ae-tasks-table{margin-top:1em}#ae-tasks-queuecontrols{margin-top:1em;margin-bottom:1em}#ae-tasks-delete-col{width:1em}#ae-tasks-eta-col,#ae-tasks-creation-col{width:11em}#ae-tasks-actions-col{width:7em}#ae-tasks-retry-col{width:4em}#ae-tasks-execution-col{width:6em}#ae-tasks-body-col{width:6em}#ae-tasks-headers-col{width:7em}.ae-tasks-hex-column,.ae-tasks-ascii-column{width:16em}#ae-tasks-table .ae-tasks-arrow{text-align:center}.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.pln{color:#000}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec{color:#606}@media print{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.pln{color:#000}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
\ No newline at end of file
diff --git a/google/appengine/ext/datastore_admin/static/js/compiled.js b/google/appengine/ext/datastore_admin/static/js/compiled.js
index a5d91dd..b92385a 100644
--- a/google/appengine/ext/datastore_admin/static/js/compiled.js
+++ b/google/appengine/ext/datastore_admin/static/js/compiled.js
@@ -1,19 +1,19 @@
-var g=document,k=Array,l=Error,m=parseInt,p=String;function aa(a,b){return a.currentTarget=b}function ba(a,b){return a.keyCode=b}function q(a,b){return a.disabled=b}
-var r="push",s="shift",t="slice",u="replace",v="value",ca="preventDefault",w="indexOf",x="keyCode",A="type",da="name",B="length",ea="propertyIsEnumerable",C="prototype",fa="checked",D="split",E="style",ga="target",F="call",ha="apply",G,H=this,I=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof k)return"array";if(a instanceof Object)return b;var c=Object[C].toString[F](a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a[B]&&"undefined"!=typeof a.splice&&
-"undefined"!=typeof a[ea]&&!a[ea]("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a[F]&&"undefined"!=typeof a[ea]&&!a[ea]("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a[F])return"object";return b},ia=function(a){var b=I(a);return"array"==b||"object"==b&&"number"==typeof a[B]},J=function(a){return"string"==typeof a},ja=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ka=function(a,b){var c=k[C][t][F](arguments,1);
-return function(){var b=c[t]();b[r][ha](b,arguments);return a[ha](this,b)}},la=function(a,b){function c(){}c.prototype=b[C];a.s=b[C];a.prototype=new c};var K=function(a){l.captureStackTrace?l.captureStackTrace(this,K):this.stack=l().stack||"";a&&(this.message=p(a))};la(K,l);K[C].name="CustomError";var ma=function(a,b){for(var c=a[D]("%s"),d="",f=k[C][t][F](arguments,1);f[B]&&1<c[B];)d+=c[s]()+f[s]();return d+c.join("%s")},sa=function(a,b){if(b)return a[u](na,"&amp;")[u](oa,"&lt;")[u](pa,"&gt;")[u](qa,"&quot;");if(!ra.test(a))return a;-1!=a[w]("&")&&(a=a[u](na,"&amp;"));-1!=a[w]("<")&&(a=a[u](oa,"&lt;"));-1!=a[w](">")&&(a=a[u](pa,"&gt;"));-1!=a[w]('"')&&(a=a[u](qa,"&quot;"));return a},na=/&/g,oa=/</g,pa=/>/g,qa=/\"/g,ra=/[&<>\"]/,ta=function(a){return p(a)[u](/\-([a-z])/g,function(a,c){return c.toUpperCase()})},
-ua=function(a,b){var c=J(b)?p(b)[u](/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1")[u](/\x08/g,"\\x08"):"\\s";return a[u](RegExp("(^"+(c?"|["+c+"]+":"")+")([a-z])","g"),function(a,b,c){return b+c.toUpperCase()})};var va=function(a,b){b.unshift(a);K[F](this,ma[ha](null,b));b[s]()};la(va,K);va[C].name="AssertionError";var L=function(a,b,c){if(!a){var d=k[C][t][F](arguments,2),f="Assertion failed";if(b)var f=f+(": "+b),e=d;throw new va(""+f,e||[]);}return a};var M=k[C],wa=M[w]?function(a,b,c){L(null!=a[B]);return M[w][F](a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a[B]+c):c;if(J(a))return J(b)&&1==b[B]?a[w](b,c):-1;for(;c<a[B];c++)if(c in a&&a[c]===b)return c;return-1},xa=M.forEach?function(a,b,c){L(null!=a[B]);M.forEach[F](a,b,c)}:function(a,b,c){for(var d=a[B],f=J(a)?a[D](""):a,e=0;e<d;e++)e in f&&b[F](c,f[e],e,a)},ya=function(a){var b=a[B];if(0<b){for(var c=k(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},za=function(a,b,c){L(null!=a[B]);return 2>=
-arguments[B]?M[t][F](a,b):M[t][F](a,b,c)};var Aa=function(a,b,c){for(var d in a)b[F](c,a[d],d,a)},Ba="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Ca=function(a,b){for(var c,d,f=1;f<arguments[B];f++){d=arguments[f];for(c in d)a[c]=d[c];for(var e=0;e<Ba[B];e++)c=Ba[e],Object[C].hasOwnProperty[F](d,c)&&(a[c]=d[c])}};var N,Da,Ea,Fa,Ga=function(){return H.navigator?H.navigator.userAgent:null};Fa=Ea=Da=N=!1;var O;if(O=Ga()){var Ha=H.navigator;N=0==O.lastIndexOf("Opera",0);Da=!N&&(-1!=O[w]("MSIE")||-1!=O[w]("Trident"));Ea=!N&&-1!=O[w]("WebKit");Fa=!N&&!Ea&&!Da&&"Gecko"==Ha.product}var Ia=N,P=Da,Q=Fa,R=Ea,Ja=function(){var a=H.document;return a?a.documentMode:void 0},Ka;
-t:{var La="",S;if(Ia&&H.opera)var Ma=H.opera.version,La="function"==typeof Ma?Ma():Ma;else if(Q?S=/rv\:([^\);]+)(\)|;)/:P?S=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:R&&(S=/WebKit\/(\S+)/),S)var Na=S.exec(Ga()),La=Na?Na[1]:"";if(P){var Oa=Ja();if(Oa>parseFloat(La)){Ka=p(Oa);break t}}Ka=La}
-var Pa=Ka,Qa={},T=function(a){var b;if(!(b=Qa[a])){b=0;for(var c=p(Pa)[u](/^[\s\xa0]+|[\s\xa0]+$/g,"")[D]("."),d=p(a)[u](/^[\s\xa0]+|[\s\xa0]+$/g,"")[D]("."),f=Math.max(c[B],d[B]),e=0;0==b&&e<f;e++){var h=c[e]||"",n=d[e]||"",jb=RegExp("(\\d*)(\\D*)","g"),kb=RegExp("(\\d*)(\\D*)","g");do{var y=jb.exec(h)||["","",""],z=kb.exec(n)||["","",""];if(0==y[0][B]&&0==z[0][B])break;b=((0==y[1][B]?0:m(y[1],10))<(0==z[1][B]?0:m(z[1],10))?-1:(0==y[1][B]?0:m(y[1],10))>(0==z[1][B]?0:m(z[1],10))?1:0)||((0==y[2][B])<
-(0==z[2][B])?-1:(0==y[2][B])>(0==z[2][B])?1:0)||(y[2]<z[2]?-1:y[2]>z[2]?1:0)}while(0==b)}b=Qa[a]=0<=b}return b},Ra=H.document,Sa=Ra&&P?Ja()||("CSS1Compat"==Ra.compatMode?m(Pa,10):5):void 0;var Ta=!P||P&&9<=Sa;!Q&&!P||P&&P&&9<=Sa||Q&&T("1.9.1");P&&T("9");var Ua=function(a,b){var c;c=a.className;c=J(c)&&c.match(/\S+/g)||[];for(var d=za(arguments,1),f=c[B]+d[B],e=c,h=0;h<d[B];h++)0<=wa(e,d[h])||e[r](d[h]);a.className=c.join(" ");return c[B]==f};var U=function(a,b){return J(b)?a.getElementById(b):b},Va=function(a,b,c,d){a=d||a;b=b&&"*"!=b?b.toUpperCase():"";if(a.querySelectorAll&&a.querySelector&&(b||c))return a.querySelectorAll(b+(c?"."+c:""));if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};for(var f=0,e=0,h;h=a[e];e++)b==h.nodeName&&(d[f++]=h);d.length=f;return d}return a}a=a.getElementsByTagName(b||"*");if(c){d={};for(e=f=0;h=a[e];e++)b=h.className,"function"==typeof b[D]&&0<=wa(b[D](/\s+/),c)&&(d[f++]=h);d.length=
-f;return d}return a},Xa=function(a,b){Aa(b,function(b,d){"style"==d?a[E].cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in Wa?a.setAttribute(Wa[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})},Wa={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"},Za=function(a,b,c){var d=arguments,
-f=d[0],e=d[1];if(!Ta&&e&&(e[da]||e[A])){f=["<",f];e[da]&&f[r](' name="',sa(e[da]),'"');if(e[A]){f[r](' type="',sa(e[A]),'"');var h={};Ca(h,e);delete h[A];e=h}f[r](">");f=f.join("")}f=g.createElement(f);e&&(J(e)?f.className=e:"array"==I(e)?Ua[ha](null,[f].concat(e)):Xa(f,e));2<d[B]&&Ya(g,f,d,2);return f},Ya=function(a,b,c,d){function f(c){c&&b.appendChild(J(c)?a.createTextNode(c):c)}for(;d<c[B];d++){var e=c[d];if(!ia(e)||ja(e)&&0<e.nodeType)f(e);else{var h;t:{if(e&&"number"==typeof e[B]){if(ja(e)){h=
-"function"==typeof e.item||"string"==typeof e.item;break t}if("function"==I(e)){h="function"==typeof e.item;break t}}h=!1}xa(h?ya(e):e,f)}}};var $a=function(a){var b=a[A];if(void 0===b)return null;switch(b.toLowerCase()){case "checkbox":case "radio":return a[fa]?a[v]:null;case "select-one":return b=a.selectedIndex,0<=b?a.options[b][v]:null;case "select-multiple":for(var b=[],c,d=0;c=a.options[d];d++)c.selected&&b[r](c[v]);return b[B]?b:null;default:return void 0!==a[v]?a[v]:null}};var ab=function(a){ab[" "](a);return a};ab[" "]=function(){};var bb=!P||P&&9<=Sa,cb=P&&!T("9");!R||T("528");Q&&T("1.9b")||P&&T("8")||Ia&&T("9.5")||R&&T("528");Q&&!T("8")||P&&T("9");var V=function(a,b){this.type=a;this.target=b;aa(this,this[ga])};V[C].m=!1;V[C].defaultPrevented=!1;V[C].preventDefault=function(){this.defaultPrevented=!0};var W=function(a,b){a&&this.t(a,b)};la(W,V);G=W[C];G.target=null;G.relatedTarget=null;G.offsetX=0;G.offsetY=0;G.clientX=0;G.clientY=0;G.screenX=0;G.screenY=0;G.button=0;ba(G,0);G.charCode=0;G.ctrlKey=!1;G.altKey=!1;G.shiftKey=!1;G.metaKey=!1;G.o=null;
-G.t=function(a,b){var c=this.type=a[A];V[F](this,c);this.target=a[ga]||a.srcElement;aa(this,b);var d=a.relatedTarget;if(d){if(Q){var f;t:{try{ab(d.nodeName);f=!0;break t}catch(e){}f=!1}f||(d=null)}}else"mouseover"==c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=R||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=R||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=
-a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;ba(this,a[x]||0);this.charCode=a.charCode||("keypress"==c?a[x]:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.o=a;a.defaultPrevented&&this[ca]();delete this.m};G.preventDefault=function(){W.s[ca][F](this);var a=this.o;if(a[ca])a[ca]();else if(a.returnValue=!1,cb)try{(a.ctrlKey||112<=a[x]&&123>=a[x])&&ba(a,-1)}catch(b){}};var db="closure_listenable_"+(1E6*Math.random()|0),eb=function(a){try{return!(!a||!a[db])}catch(b){return!1}},fb=0;var gb=function(a,b,c,d,f,e){this.c=a;this.g=b;this.src=c;this.type=d;this.capture=!!f;this.j=e;this.key=++fb;this.e=this.k=!1};gb[C].n=function(){this.e=!0;this.j=this.src=this.g=this.c=null};var hb=function(a){this.src=a;this.a={};this.l=0};hb[C].add=function(a,b,c,d,f){var e=this.a[a];e||(e=this.a[a]=[],this.l++);var h;t:{for(h=0;h<e[B];++h){var n=e[h];if(!n.e&&n.c==b&&n.capture==!!d&&n.j==f)break t}h=-1}-1<h?(a=e[h],c||(a.k=!1)):(a=new gb(b,null,this.src,a,!!d,f),a.k=c,e[r](a));return a};hb[C].p=function(a){var b=a[A];if(!(b in this.a))return!1;var c=this.a[b],d=wa(c,a),f;if(f=0<=d)L(null!=c[B]),M.splice[F](c,d,1);f&&(a.n(),0==this.a[b][B]&&(delete this.a[b],this.l--));return f};var ib="closure_lm_"+(1E6*Math.random()|0),X={},lb=0,mb=function(a,b,c,d,f){if("array"==I(b)){for(var e=0;e<b[B];e++)mb(a,b[e],c,d,f);return null}c=nb(c);if(eb(a))a=a.v(b,c,d,f);else{if(!b)throw l("Invalid event type");var e=!!d,h=ob(a);h||(a[ib]=h=new hb(a));c=h.add(b,c,!1,d,f);c.g||(d=pb(),c.g=d,d.src=a,d.c=c,a.addEventListener?a.addEventListener(b,d,e):a.attachEvent(b in X?X[b]:X[b]="on"+b,d),lb++);a=c}return a},pb=function(){var a=qb,b=bb?function(c){return a[F](b.src,b.c,c)}:function(c){c=a[F](b.src,
-b.c,c);if(!c)return c};return b},sb=function(a,b,c,d){var f=1;if(a=ob(a))if(b=a.a[b])for(b=ya(b),a=0;a<b[B];a++){var e=b[a];e&&e.capture==c&&!e.e&&(f&=!1!==rb(e,d))}return Boolean(f)},rb=function(a,b){var c=a.c,d=a.j||a.src;if(a.k&&"number"!=typeof a&&a&&!a.e){var f=a.src;if(eb(f))f.u(a);else{var e=a[A],h=a.g;f.removeEventListener?f.removeEventListener(e,h,a.capture):f.detachEvent&&f.detachEvent(e in X?X[e]:X[e]="on"+e,h);lb--;(e=ob(f))?(e.p(a),0==e.l&&(e.src=null,f[ib]=null)):a.n()}}return c[F](d,
-b)},qb=function(a,b){if(a.e)return!0;if(!bb){var c;if(!(c=b))t:{c=["window","event"];for(var d=H,f;f=c[s]();)if(null!=d[f])d=d[f];else{c=null;break t}c=d}f=c;c=new W(f,this);d=!0;if(!(0>f[x]||void 0!=f.returnValue)){t:{var e=!1;if(0==f[x])try{ba(f,-1);break t}catch(h){e=!0}if(e||void 0==f.returnValue)f.returnValue=!0}f=[];for(e=c.currentTarget;e;e=e.parentNode)f[r](e);for(var e=a[A],n=f[B]-1;!c.m&&0<=n;n--)aa(c,f[n]),d&=sb(f[n],e,!0,c);for(n=0;!c.m&&n<f[B];n++)aa(c,f[n]),d&=sb(f[n],e,!1,c)}return d}return rb(a,
-new W(b,this))},ob=function(a){a=a[ib];return a instanceof hb?a:null},tb="__closure_events_fn_"+(1E9*Math.random()>>>0),nb=function(a){L(a,"Listener can not be null.");if("function"==I(a))return a;L(a.handleEvent,"An object listener must have handleEvent method.");return a[tb]||(a[tb]=function(b){return a.handleEvent(b)})};var ub=function(a,b,c){var d;t:if(d=ta(c),void 0===a[E][d]&&(c=(R?"Webkit":Q?"Moz":P?"ms":Ia?"O":null)+ua(c),void 0!==a[E][c])){d=c;break t}d&&(a[E][d]=b)};var vb=function(a,b){var c=[];1<arguments[B]&&(c=k[C][t][F](arguments)[t](1));var d=Va(g,"th","tct-selectall",a);if(0!=d[B]){var d=d[0],f=0,e=Va(g,"tbody",null,a);e[B]&&(f=e[0].rows[B]);this.d=Za("input",{type:"checkbox"});d.appendChild(this.d);f?mb(this.d,"click",this.r,!1,this):q(this.d,!0);this.f=[];this.h=[];this.i=[];d=Va(g,"input",null,a);for(f=0;e=d[f];f++)"checkbox"==e[A]&&e!=this.d?(this.f[r](e),mb(e,"click",this.q,!1,this)):"action"==e[da]&&(0<=c[w](e[v])?this.i[r](e):this.h[r](e),q(e,!0))}};
-G=vb[C];G.f=null;G.b=0;G.d=null;G.h=null;G.i=null;G.r=function(a){for(var b=a[ga][fa],c=a=0,d;d=this.f[c];c++)d.checked=b,a+=1;this.b=b?this.f[B]:0;for(c=0;b=this.h[c];c++)q(b,!this.b);for(c=0;b=this.i[c];c++)q(b,1!=a?!0:!1)};G.q=function(a){this.b+=a[ga][fa]?1:-1;this.d.checked=this.b==this.f[B];a=0;for(var b;b=this.h[a];a++)q(b,!this.b);for(a=0;b=this.i[a];a++)q(b,1!=this.b?!0:!1)};var wb=function(){var a=U(g,"kinds");a&&new vb(a);(a=U(g,"pending_backups"))&&new vb(a);(a=U(g,"backups"))&&new vb(a,"Restore");var b=U(g,"ae-datastore-admin-filesystem");b&&mb(b,"change",function(){var a="gs"==$a(b);U(g,"gs_bucket_tr")[E].display=a?"":"none"});if(a=U(g,"confirm_delete_form")){var c=U(g,"confirm_readonly_delete");c&&(a.onsubmit=function(){var a=U(g,"confirm_message");J("color")?ub(a,"red","color"):Aa("color",ka(ub,a));return c[fa]})}},Y=["ae","Datastore","Admin","init"],Z=H;
-Y[0]in Z||!Z.execScript||Z.execScript("var "+Y[0]);for(var $;Y[B]&&($=Y[s]());)Y[B]||void 0===wb?Z=Z[$]?Z[$]:Z[$]={}:Z[$]=wb;
+var g=document,k=Array,l=Error,aa=parseInt,n=String;function p(a,b){return a.currentTarget=b}function ba(a,b){return a.keyCode=b}function q(a,b){return a.disabled=b}
+var r="push",s="shift",t="slice",u="replace",v="value",ca="preventDefault",w="indexOf",x="keyCode",y="type",da="name",z="length",ea="propertyIsEnumerable",A="prototype",fa="checked",B="split",C="style",ga="target",D="call",E="apply",F,G=this,H=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof k)return"array";if(a instanceof Object)return b;var c=Object[A].toString[D](a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a[z]&&"undefined"!=typeof a.splice&&
+"undefined"!=typeof a[ea]&&!a[ea]("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a[D]&&"undefined"!=typeof a[ea]&&!a[ea]("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a[D])return"object";return b},ha=function(a){var b=H(a);return"array"==b||"object"==b&&"number"==typeof a[z]},I=function(a){return"string"==typeof a},ia=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ja=function(a,b){var c=k[A][t][D](arguments,1);
+return function(){var b=c[t]();b[r][E](b,arguments);return a[E](this,b)}},ka=function(a,b){function c(){}c.prototype=b[A];a.s=b[A];a.prototype=new c;a.w=function(a,c,e){var h=k[A][t][D](arguments,2);return b[A][c][E](a,h)}};var J=function(a){l.captureStackTrace?l.captureStackTrace(this,J):this.stack=l().stack||"";a&&(this.message=n(a))};ka(J,l);J[A].name="CustomError";var la=function(a,b){for(var c=a[B]("%s"),d="",f=k[A][t][D](arguments,1);f[z]&&1<c[z];)d+=c[s]()+f[s]();return d+c.join("%s")},sa=function(a,b){if(b)return a[u](ma,"&amp;")[u](na,"&lt;")[u](oa,"&gt;")[u](pa,"&quot;")[u](qa,"&#39;");if(!ra.test(a))return a;-1!=a[w]("&")&&(a=a[u](ma,"&amp;"));-1!=a[w]("<")&&(a=a[u](na,"&lt;"));-1!=a[w](">")&&(a=a[u](oa,"&gt;"));-1!=a[w]('"')&&(a=a[u](pa,"&quot;"));-1!=a[w]("'")&&(a=a[u](qa,"&#39;"));return a},ma=/&/g,na=/</g,oa=/>/g,pa=/"/g,qa=/'/g,ra=/[&<>"']/,ta=
+function(a,b){return a<b?-1:a>b?1:0},ua=function(a){return n(a)[u](/\-([a-z])/g,function(a,c){return c.toUpperCase()})},va=function(a,b){var c=I(b)?n(b)[u](/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1")[u](/\x08/g,"\\x08"):"\\s";return a[u](RegExp("(^"+(c?"|["+c+"]+":"")+")([a-z])","g"),function(a,b,c){return b+c.toUpperCase()})};var wa=function(a,b){b.unshift(a);J[D](this,la[E](null,b));b[s]()};ka(wa,J);wa[A].name="AssertionError";var K=function(a,b,c){if(!a){var d=k[A][t][D](arguments,2),f="Assertion failed";if(b)var f=f+(": "+b),e=d;throw new wa(""+f,e||[]);}return a};var L=k[A],xa=L[w]?function(a,b,c){K(null!=a[z]);return L[w][D](a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a[z]+c):c;if(I(a))return I(b)&&1==b[z]?a[w](b,c):-1;for(;c<a[z];c++)if(c in a&&a[c]===b)return c;return-1},ya=L.forEach?function(a,b,c){K(null!=a[z]);L.forEach[D](a,b,c)}:function(a,b,c){for(var d=a[z],f=I(a)?a[B](""):a,e=0;e<d;e++)e in f&&b[D](c,f[e],e,a)},za=function(a){var b=a[z];if(0<b){for(var c=k(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},Aa=function(a,b,c){K(null!=a[z]);return 2>=
+arguments[z]?L[t][D](a,b):L[t][D](a,b,c)};var Ba=function(a,b,c){for(var d in a)b[D](c,a[d],d,a)},Ca="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Da=function(a,b){for(var c,d,f=1;f<arguments[z];f++){d=arguments[f];for(c in d)a[c]=d[c];for(var e=0;e<Ca[z];e++)c=Ca[e],Object[A].hasOwnProperty[D](d,c)&&(a[c]=d[c])}};var M,Ea,Fa,Ga,Ha=function(){return G.navigator?G.navigator.userAgent:null};Ga=Fa=Ea=M=!1;var N;if(N=Ha()){var Ia=G.navigator;M=0==N.lastIndexOf("Opera",0);Ea=!M&&(-1!=N[w]("MSIE")||-1!=N[w]("Trident"));Fa=!M&&-1!=N[w]("WebKit");Ga=!M&&!Fa&&!Ea&&"Gecko"==Ia.product}var Ja=M,O=Ea,P=Ga,Q=Fa,Ka=function(){var a=G.document;return a?a.documentMode:void 0},La;
+t:{var Ma="",R;if(Ja&&G.opera)var Na=G.opera.version,Ma="function"==typeof Na?Na():Na;else if(P?R=/rv\:([^\);]+)(\)|;)/:O?R=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:Q&&(R=/WebKit\/(\S+)/),R)var Oa=R.exec(Ha()),Ma=Oa?Oa[1]:"";if(O){var Pa=Ka();if(Pa>parseFloat(Ma)){La=n(Pa);break t}}La=Ma}
+var Qa=La,Ra={},S=function(a){var b;if(!(b=Ra[a])){b=0;for(var c=n(Qa)[u](/^[\s\xa0]+|[\s\xa0]+$/g,"")[B]("."),d=n(a)[u](/^[\s\xa0]+|[\s\xa0]+$/g,"")[B]("."),f=Math.max(c[z],d[z]),e=0;0==b&&e<f;e++){var h=c[e]||"",m=d[e]||"",lb=RegExp("(\\d*)(\\D*)","g"),mb=RegExp("(\\d*)(\\D*)","g");do{var X=lb.exec(h)||["","",""],Y=mb.exec(m)||["","",""];if(0==X[0][z]&&0==Y[0][z])break;b=ta(0==X[1][z]?0:aa(X[1],10),0==Y[1][z]?0:aa(Y[1],10))||ta(0==X[2][z],0==Y[2][z])||ta(X[2],Y[2])}while(0==b)}b=Ra[a]=0<=b}return b},
+Sa=G.document,Ta=Sa&&O?Ka()||("CSS1Compat"==Sa.compatMode?aa(Qa,10):5):void 0;var Ua=!O||O&&9<=Ta;!P&&!O||O&&O&&9<=Ta||P&&S("1.9.1");O&&S("9");var Va=function(a,b){var c;c=a.className;c=I(c)&&c.match(/\S+/g)||[];for(var d=Aa(arguments,1),f=c[z]+d[z],e=c,h=0;h<d[z];h++)0<=xa(e,d[h])||e[r](d[h]);a.className=c.join(" ");return c[z]==f};var T=function(a,b){return I(b)?a.getElementById(b):b},Wa=function(a,b,c,d){a=d||a;var f=b&&"*"!=b?b.toUpperCase():"";if(a.querySelectorAll&&a.querySelector&&(f||c))return a.querySelectorAll(f+(c?"."+c:""));if(c&&a.getElementsByClassName){b=a.getElementsByClassName(c);if(f){a={};for(var e=d=0,h;h=b[e];e++)f==h.nodeName&&(a[d++]=h);a.length=d;return a}return b}b=a.getElementsByTagName(f||"*");if(c){a={};for(e=d=0;h=b[e];e++){var f=h.className,m;if(m="function"==typeof f[B])m=0<=xa(f[B](/\s+/),c);m&&
+(a[d++]=h)}a.length=d;return a}return b},Ya=function(a,b){Ba(b,function(b,d){"style"==d?a[C].cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in Xa?a.setAttribute(Xa[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})},Xa={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"},$a=function(a,
+b,c){var d=arguments,f=d[0],e=d[1];if(!Ua&&e&&(e[da]||e[y])){f=["<",f];e[da]&&f[r](' name="',sa(e[da]),'"');if(e[y]){f[r](' type="',sa(e[y]),'"');var h={};Da(h,e);delete h[y];e=h}f[r](">");f=f.join("")}f=g.createElement(f);e&&(I(e)?f.className=e:"array"==H(e)?Va[E](null,[f].concat(e)):Ya(f,e));2<d[z]&&Za(g,f,d,2);return f},Za=function(a,b,c,d){function f(c){c&&b.appendChild(I(c)?a.createTextNode(c):c)}for(;d<c[z];d++){var e=c[d];if(!ha(e)||ia(e)&&0<e.nodeType)f(e);else{var h;t:{if(e&&"number"==typeof e[z]){if(ia(e)){h=
+"function"==typeof e.item||"string"==typeof e.item;break t}if("function"==H(e)){h="function"==typeof e.item;break t}}h=!1}ya(h?za(e):e,f)}}};var ab=function(a){var b=a[y];if(void 0===b)return null;switch(b.toLowerCase()){case "checkbox":case "radio":return a[fa]?a[v]:null;case "select-one":return b=a.selectedIndex,0<=b?a.options[b][v]:null;case "select-multiple":for(var b=[],c,d=0;c=a.options[d];d++)c.selected&&b[r](c[v]);return b[z]?b:null;default:return void 0!==a[v]?a[v]:null}};var bb=function(a){bb[" "](a);return a};bb[" "]=function(){};var cb=!O||O&&9<=Ta,db=O&&!S("9");!Q||S("528");P&&S("1.9b")||O&&S("8")||Ja&&S("9.5")||Q&&S("528");P&&!S("8")||O&&S("9");var eb=function(a,b){this.type=a;this.target=b;p(this,this[ga]);this.defaultPrevented=this.n=!1};eb[A].preventDefault=function(){this.defaultPrevented=!0};var U=function(a,b){eb[D](this,a?a[y]:"");this.target=null;p(this,null);this.relatedTarget=null;this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;ba(this,0);this.charCode=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.o=this.state=null;a&&this.t(a,b)};ka(U,eb);
+U[A].t=function(a,b){var c=this.type=a[y];this.target=a[ga]||a.srcElement;p(this,b);var d=a.relatedTarget;if(d){if(P){var f;t:{try{bb(d.nodeName);f=!0;break t}catch(e){}f=!1}f||(d=null)}}else"mouseover"==c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=Q||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=Q||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||
+0;this.screenY=a.screenY||0;this.button=a.button;ba(this,a[x]||0);this.charCode=a.charCode||("keypress"==c?a[x]:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.o=a;a.defaultPrevented&&this[ca]()};U[A].preventDefault=function(){U.s[ca][D](this);var a=this.o;if(a[ca])a[ca]();else if(a.returnValue=!1,db)try{(a.ctrlKey||112<=a[x]&&123>=a[x])&&ba(a,-1)}catch(b){}};var fb="closure_listenable_"+(1E6*Math.random()|0),gb=function(a){try{return!(!a||!a[fb])}catch(b){return!1}},hb=0;var ib=function(a,b,c,d,f,e){this.c=a;this.g=b;this.src=c;this.type=d;this.capture=!!f;this.j=e;this.key=++hb;this.e=this.k=!1};ib[A].m=function(){this.e=!0;this.j=this.src=this.g=this.c=null};var jb=function(a){this.src=a;this.a={};this.l=0};jb[A].add=function(a,b,c,d,f){var e=this.a[a];e||(e=this.a[a]=[],this.l++);var h;t:{for(h=0;h<e[z];++h){var m=e[h];if(!m.e&&m.c==b&&m.capture==!!d&&m.j==f)break t}h=-1}-1<h?(a=e[h],c||(a.k=!1)):(a=new ib(b,null,this.src,a,!!d,f),a.k=c,e[r](a));return a};jb[A].p=function(a){var b=a[y];if(!(b in this.a))return!1;var c=this.a[b],d=xa(c,a),f;if(f=0<=d)K(null!=c[z]),L.splice[D](c,d,1);f&&(a.m(),0==this.a[b][z]&&(delete this.a[b],this.l--));return f};var kb="closure_lm_"+(1E6*Math.random()|0),V={},nb=0,ob=function(a,b,c,d,f){if("array"==H(b)){for(var e=0;e<b[z];e++)ob(a,b[e],c,d,f);return null}c=pb(c);if(gb(a))a=a.v(b,c,d,f);else{if(!b)throw l("Invalid event type");var e=!!d,h=qb(a);h||(a[kb]=h=new jb(a));c=h.add(b,c,!1,d,f);c.g||(d=rb(),c.g=d,d.src=a,d.c=c,a.addEventListener?a.addEventListener(b,d,e):a.attachEvent(b in V?V[b]:V[b]="on"+b,d),nb++);a=c}return a},rb=function(){var a=sb,b=cb?function(c){return a[D](b.src,b.c,c)}:function(c){c=a[D](b.src,
+b.c,c);if(!c)return c};return b},ub=function(a,b,c,d){var f=1;if(a=qb(a))if(b=a.a[b])for(b=za(b),a=0;a<b[z];a++){var e=b[a];e&&e.capture==c&&!e.e&&(f&=!1!==tb(e,d))}return Boolean(f)},tb=function(a,b){var c=a.c,d=a.j||a.src;if(a.k&&"number"!=typeof a&&a&&!a.e){var f=a.src;if(gb(f))f.u(a);else{var e=a[y],h=a.g;f.removeEventListener?f.removeEventListener(e,h,a.capture):f.detachEvent&&f.detachEvent(e in V?V[e]:V[e]="on"+e,h);nb--;(e=qb(f))?(e.p(a),0==e.l&&(e.src=null,f[kb]=null)):a.m()}}return c[D](d,
+b)},sb=function(a,b){if(a.e)return!0;if(!cb){var c;if(!(c=b))t:{c=["window","event"];for(var d=G,f;f=c[s]();)if(null!=d[f])d=d[f];else{c=null;break t}c=d}f=c;c=new U(f,this);d=!0;if(!(0>f[x]||void 0!=f.returnValue)){t:{var e=!1;if(0==f[x])try{ba(f,-1);break t}catch(h){e=!0}if(e||void 0==f.returnValue)f.returnValue=!0}f=[];for(e=c.currentTarget;e;e=e.parentNode)f[r](e);for(var e=a[y],m=f[z]-1;!c.n&&0<=m;m--)p(c,f[m]),d&=ub(f[m],e,!0,c);for(m=0;!c.n&&m<f[z];m++)p(c,f[m]),d&=ub(f[m],e,!1,c)}return d}return tb(a,
+new U(b,this))},qb=function(a){a=a[kb];return a instanceof jb?a:null},vb="__closure_events_fn_"+(1E9*Math.random()>>>0),pb=function(a){K(a,"Listener can not be null.");if("function"==H(a))return a;K(a.handleEvent,"An object listener must have handleEvent method.");return a[vb]||(a[vb]=function(b){return a.handleEvent(b)})};var wb=function(a,b,c){var d;t:if(d=ua(c),void 0===a[C][d]&&(c=(Q?"Webkit":P?"Moz":O?"ms":Ja?"O":null)+va(c),void 0!==a[C][c])){d=c;break t}d&&(a[C][d]=b)};var xb=function(a,b){var c=[];1<arguments[z]&&(c=k[A][t][D](arguments)[t](1));var d=Wa(g,"th","tct-selectall",a);if(0!=d[z]){var d=d[0],f=0,e=Wa(g,"tbody",null,a);e[z]&&(f=e[0].rows[z]);this.d=$a("input",{type:"checkbox"});d.appendChild(this.d);f?ob(this.d,"click",this.r,!1,this):q(this.d,!0);this.f=[];this.h=[];this.i=[];d=Wa(g,"input",null,a);for(f=0;e=d[f];f++)"checkbox"==e[y]&&e!=this.d?(this.f[r](e),ob(e,"click",this.q,!1,this)):"action"==e[da]&&(0<=c[w](e[v])?this.i[r](e):this.h[r](e),q(e,!0))}};
+F=xb[A];F.f=null;F.b=0;F.d=null;F.h=null;F.i=null;F.r=function(a){for(var b=a[ga][fa],c=a=0,d;d=this.f[c];c++)d.checked=b,a+=1;this.b=b?this.f[z]:0;for(c=0;b=this.h[c];c++)q(b,!this.b);for(c=0;b=this.i[c];c++)q(b,1!=a?!0:!1)};F.q=function(a){this.b+=a[ga][fa]?1:-1;this.d.checked=this.b==this.f[z];a=0;for(var b;b=this.h[a];a++)q(b,!this.b);for(a=0;b=this.i[a];a++)q(b,1!=this.b?!0:!1)};var yb=function(){var a=T(g,"kinds");a&&new xb(a);(a=T(g,"pending_backups"))&&new xb(a);(a=T(g,"backups"))&&new xb(a,"Restore");var b=T(g,"ae-datastore-admin-filesystem");b&&ob(b,"change",function(){var a="gs"==ab(b);T(g,"gs_bucket_tr")[C].display=a?"":"none"});if(a=T(g,"confirm_delete_form")){var c=T(g,"confirm_readonly_delete");c&&(a.onsubmit=function(){var a=T(g,"confirm_message");I("color")?wb(a,"red","color"):Ba("color",ja(wb,a));return c[fa]})}},W=["ae","Datastore","Admin","init"],Z=G;
+W[0]in Z||!Z.execScript||Z.execScript("var "+W[0]);for(var $;W[z]&&($=W[s]());)W[z]||void 0===yb?Z=Z[$]?Z[$]:Z[$]={}:Z[$]=yb;
diff --git a/google/appengine/ext/datastore_admin/templates/list_actions.html b/google/appengine/ext/datastore_admin/templates/list_actions.html
index 40088da..44adfc4 100644
--- a/google/appengine/ext/datastore_admin/templates/list_actions.html
+++ b/google/appengine/ext/datastore_admin/templates/list_actions.html
@@ -4,7 +4,7 @@
   {% if run_as_a_service %}
     <div class="ae-message ae-rounded-sml">
       Backup/Restore running as a service.
-      <a href="{{datastore_admin_home}}">Disable</a>
+      <a href="{{datastore_admin_home}}?service=T">Disable</a>
     </div>
   {% else %}
     {% if offer_service %}
diff --git a/google/appengine/ext/mapreduce/base_handler.py b/google/appengine/ext/mapreduce/base_handler.py
index 255638f..e600df5 100644
--- a/google/appengine/ext/mapreduce/base_handler.py
+++ b/google/appengine/ext/mapreduce/base_handler.py
@@ -39,9 +39,9 @@
 
 import httplib
 import logging
-import simplejson
 
 import google
+import simplejson
 
 try:
   from google.appengine.ext.mapreduce import pipeline_base
@@ -57,6 +57,7 @@
 
 from google.appengine.ext import webapp
 from google.appengine.ext.mapreduce import errors
+from google.appengine.ext.mapreduce import json_util
 from google.appengine.ext.mapreduce import model
 from google.appengine.ext.mapreduce import parameters
 
@@ -69,19 +70,7 @@
   """The request path for the handler is invalid."""
 
 
-class BaseHandler(webapp.RequestHandler):
-  """Base class for all mapreduce handlers.
-
-  In Python27 runtime, webapp2 will automatically replace webapp.
-  """
-
-  def base_path(self):
-    """Base path for all mapreduce-related urls."""
-    path = self.request.path
-    return path[:path.rfind("/")]
-
-
-class TaskQueueHandler(BaseHandler):
+class TaskQueueHandler(webapp.RequestHandler):
   """Base class for handlers intended to be run only from the task queue.
 
   Sub-classes should implement
@@ -89,6 +78,8 @@
   2. '_preprocess' method for decoding or validations before handle.
   3. '_drop_gracefully' method if _preprocess fails and the task has to
      be dropped.
+
+  In Python27 runtime, webapp2 will automatically replace webapp.
   """
 
   def __init__(self, *args, **kwargs):
@@ -185,7 +176,7 @@
     self.response.clear()
 
 
-class JsonHandler(BaseHandler):
+class JsonHandler(webapp.RequestHandler):
   """Base class for JSON handlers for user interface.
 
   Sub-classes should implement the 'handle' method. They should put their
@@ -196,7 +187,7 @@
 
   def __init__(self, *args):
     """Initializer."""
-    super(BaseHandler, self).__init__(*args)
+    super(JsonHandler, self).__init__(*args)
     self.json_response = {}
 
   def base_path(self):
@@ -236,7 +227,7 @@
 
     self.response.headers["Content-Type"] = "text/javascript"
     try:
-      output = simplejson.dumps(self.json_response, cls=model.JsonEncoder)
+      output = simplejson.dumps(self.json_response, cls=json_util.JsonEncoder)
     except:
       logging.exception("Could not serialize to JSON")
       self.response.set_status(500, message="Could not serialize to JSON")
diff --git a/google/appengine/ext/mapreduce/control.py b/google/appengine/ext/mapreduce/control.py
index 42946cf..378a2ec 100644
--- a/google/appengine/ext/mapreduce/control.py
+++ b/google/appengine/ext/mapreduce/control.py
@@ -118,8 +118,7 @@
   return handlers.StartJobHandler._start_map(
       name,
       mapper_spec,
-      mapreduce_parameters or {},
-      base_path=base_path,
+      mapreduce_parameters,
       queue_name=util.get_queue_name(queue_name),
       eta=eta,
       countdown=countdown,
diff --git a/google/appengine/ext/mapreduce/datastore_range_iterators.py b/google/appengine/ext/mapreduce/datastore_range_iterators.py
index 7b63209..d1980b2 100644
--- a/google/appengine/ext/mapreduce/datastore_range_iterators.py
+++ b/google/appengine/ext/mapreduce/datastore_range_iterators.py
@@ -23,6 +23,7 @@
 from google.appengine.datastore import datastore_rpc
 from google.appengine.ext import db
 from google.appengine.ext import key_range
+from google.appengine.ext.mapreduce import json_util
 from google.appengine.ext.mapreduce import key_ranges
 from google.appengine.ext.mapreduce import model
 from google.appengine.ext.mapreduce import namespace_range
@@ -89,7 +90,7 @@
     return _RANGE_ITERATORS[json["name"]].from_json(json)
 
 
-class RangeIterator(model.JsonMixin):
+class RangeIterator(json_util.JsonMixin):
   """Interface for DatastoreInputReader helper iterators.
 
   RangeIterator defines Python's generator interface and additional
@@ -176,19 +177,22 @@
 
   def to_json(self):
     """Inherit doc."""
-    cursor_object = False
+    cursor = self._cursor
     if self._query is not None:
       if isinstance(self._query, db.Query):
-        self._cursor = self._query.cursor()
+        cursor = self._query.cursor()
       else:
-        cursor_object = True
-        self._cursor = self._query.cursor_after().to_websafe_string()
+        cursor = self._query.cursor_after()
+
+    if isinstance(cursor, basestring):
+      cursor_object = False
     else:
-      self._cursor = None
+      cursor_object = True
+      cursor = cursor.to_websafe_string()
 
     return {"property_range": self._property_range.to_json(),
             "query_spec": self._query_spec.to_json(),
-            "cursor": self._cursor,
+            "cursor": cursor,
             "ns_range": self._ns_range.to_json_object(),
             "name": self.__class__.__name__,
             "cursor_object": cursor_object}
@@ -286,7 +290,7 @@
     }
 
 
-class AbstractKeyRangeIterator(model.JsonMixin):
+class AbstractKeyRangeIterator(json_util.JsonMixin):
   """Iterates over a single key_range.KeyRange and yields value for each key."""
 
   def __init__(self, k_range, query_spec):
@@ -366,11 +370,13 @@
         yield model_instance
 
   def _get_cursor(self):
-    if self._query is not None:
-      if isinstance(self._query, db.Query):
-        return self._query.cursor()
-      else:
-        return self._query.cursor_after()
+    if self._query is None:
+      return self._cursor
+
+    if isinstance(self._query, db.Query):
+      return self._query.cursor()
+    else:
+      return self._query.cursor_after()
 
 
 class KeyRangeEntityIterator(AbstractKeyRangeIterator):
@@ -388,8 +394,9 @@
       yield entity
 
   def _get_cursor(self):
-    if self._query is not None:
-      return self._query.GetCursor()
+    if self._query is None:
+      return self._cursor
+    return self._query.GetCursor()
 
 
 class KeyRangeKeyIterator(KeyRangeEntityIterator):
@@ -420,8 +427,9 @@
       yield entity_proto
 
   def _get_cursor(self):
-    if self._query is not None:
-      return self._query.cursor()
+    if self._query is None:
+      return self._cursor
+    return self._query.cursor()
 
 
 
diff --git a/google/appengine/ext/mapreduce/file_format_root.py b/google/appengine/ext/mapreduce/file_format_root.py
index f2119c6..f37d475 100644
--- a/google/appengine/ext/mapreduce/file_format_root.py
+++ b/google/appengine/ext/mapreduce/file_format_root.py
@@ -38,11 +38,10 @@
            'split']
 
 import copy
-import google.appengine.ext.mapreduce.file_format_parser as parser
-
 from google.appengine.api.files import file as files
-from google.appengine.ext.mapreduce import model
 from google.appengine.ext.mapreduce import file_formats
+from google.appengine.ext.mapreduce import json_util
+import google.appengine.ext.mapreduce.file_format_parser as parser
 
 
 def split(filenames, format_string, shards):
@@ -142,7 +141,7 @@
   return roots
 
 
-class _FileRange(model.JsonMixin):
+class _FileRange(json_util.JsonMixin):
   """Describe a range of a file to read.
 
   FileFormatRootFactory creates instances of this class and
@@ -178,7 +177,7 @@
     return cls(json[cls.FILENAME], json[cls.RANGE])
 
 
-class FileFormatRoot(model.JsonMixin):
+class FileFormatRoot(json_util.JsonMixin):
   """FileFormatRoot.
 
   FileFormatRoot takes a list of FileFormats as processing units and
diff --git a/google/appengine/ext/mapreduce/handlers.py b/google/appengine/ext/mapreduce/handlers.py
index 1e21012..199af67 100644
--- a/google/appengine/ext/mapreduce/handlers.py
+++ b/google/appengine/ext/mapreduce/handlers.py
@@ -36,7 +36,6 @@
 
 
 import datetime
-import gc
 import logging
 import math
 import os
@@ -78,6 +77,14 @@
 
 
 
+
+
+
+
+
+
+
+
 _TEST_INJECTED_FAULTS = set()
 
 
@@ -108,13 +115,15 @@
 class MapperWorkerCallbackHandler(base_handler.HugeTaskHandler):
   """Callback handler for mapreduce worker task."""
 
-  _TASK_STATE = util._enum(
+  _TASK_DIRECTIVE = util._enum(
+
+      PROCEED_TASK="proceed_task",
 
       RETRY_TASK="retry_task",
 
       DROP_TASK="drop_task",
 
-      PROCEED_TASK="proceed_task",
+      RECOVER_SLICE="recover_slice",
 
       RETRY_SHARD="retry_shard",
 
@@ -153,7 +162,7 @@
       tstate: model.TransientShardState from taskqueue paylod.
 
     Returns:
-      A _TASK_STATE enum. PROCEED_TASK if lock is acquired.
+      A _TASK_DIRECTIVE enum. PROCEED_TASK if lock is acquired.
     RETRY_TASK if task should be retried, DROP_TASK if task should
     be dropped. Only old tasks (comparing to datastore state)
     will be dropped. Future tasks are retried until they naturally
@@ -164,13 +173,13 @@
       logging.warning("State not found for shard %s; Possible spurious task "
                       "execution. Dropping this task.",
                       tstate.shard_id)
-      return self._TASK_STATE.DROP_TASK
+      return self._TASK_DIRECTIVE.DROP_TASK
 
     if not shard_state.active:
       logging.warning("Shard %s is not active. Possible spurious task "
                       "execution. Dropping this task.", tstate.shard_id)
       logging.warning(str(shard_state))
-      return self._TASK_STATE.DROP_TASK
+      return self._TASK_DIRECTIVE.DROP_TASK
 
 
     if shard_state.retries > tstate.retries:
@@ -180,7 +189,7 @@
           tstate.shard_id,
           tstate.retries)
       logging.warning(str(shard_state))
-      return self._TASK_STATE.DROP_TASK
+      return self._TASK_DIRECTIVE.DROP_TASK
     elif shard_state.retries < tstate.retries:
 
 
@@ -188,7 +197,7 @@
       logging.warning(
           "ShardState for %s is behind slice. Waiting for it to catch up",
           shard_state.shard_id)
-      return self._TASK_STATE.RETRY_TASK
+      return self._TASK_DIRECTIVE.RETRY_TASK
 
 
 
@@ -196,7 +205,7 @@
       logging.warning(
           "Task %s-%s is behind ShardState %s. Dropping task.""",
           tstate.shard_id, tstate.slice_id, shard_state.slice_id)
-      return self._TASK_STATE.DROP_TASK
+      return self._TASK_DIRECTIVE.DROP_TASK
 
 
 
@@ -204,7 +213,7 @@
       logging.warning(
           "Task %s-%s is ahead of ShardState %s. Waiting for it to catch up.",
           tstate.shard_id, tstate.slice_id, shard_state.slice_id)
-      return self._TASK_STATE.RETRY_TASK
+      return self._TASK_DIRECTIVE.RETRY_TASK
 
 
 
@@ -221,17 +230,17 @@
 
 
         time.sleep(countdown)
-        return self._TASK_STATE.RETRY_TASK
+        return self._TASK_DIRECTIVE.RETRY_TASK
 
       else:
         if self._wait_time(shard_state,
                            parameters.config._REQUEST_EVENTUAL_TIMEOUT):
-          if not self._old_request_ended(shard_state):
+          if not self._has_old_request_ended(shard_state):
             logging.warning(
                 "Last retry of slice %s-%s is still in flight with request_id "
                 "%s. Will try again later.", tstate.shard_id, tstate.slice_id,
                 shard_state.slice_request_id)
-            return self._TASK_STATE.RETRY_TASK
+            return self._TASK_DIRECTIVE.RETRY_TASK
         else:
           logging.warning(
               "Last retry of slice %s-%s has no log entry and has"
@@ -249,11 +258,11 @@
       the previous validation code). The task would die naturally eventually.
 
       Returns:
-        A _TASK_STATE enum.
+        A _TASK_DIRECTIVE enum.
       """
       fresh_state = model.ShardState.get_by_shard_id(tstate.shard_id)
       if not fresh_state:
-        logging.error("ShardState missing.")
+        logging.warning("ShardState missing.")
         raise db.Rollback()
       if (fresh_state.active and
           fresh_state.slice_id == shard_state.slice_id and
@@ -262,18 +271,18 @@
         shard_state.slice_request_id = os.environ.get("REQUEST_LOG_ID")
         shard_state.acquired_once = True
         shard_state.put(config=config)
-        return self._TASK_STATE.PROCEED_TASK
+        return self._TASK_DIRECTIVE.PROCEED_TASK
       else:
         logging.warning(
             "Contention on slice %s-%s execution. Will retry again.",
             tstate.shard_id, tstate.slice_id)
 
         time.sleep(random.randrange(1, 5))
-        return self._TASK_STATE.RETRY_TASK
+        return self._TASK_DIRECTIVE.RETRY_TASK
 
     return _tx()
 
-  def _old_request_ended(self, shard_state):
+  def _has_old_request_ended(self, shard_state):
     """Whether previous slice retry has ended according to Logs API.
 
     Args:
@@ -369,8 +378,8 @@
     Returns:
       Set HTTP status code and always returns None.
     """
-    self._start_time = self._time()
 
+    self._start_time = self._time()
     shard_id = self.request.headers[util._MR_SHARD_ID_TASK_HEADER]
     mr_id = self.request.headers[util._MR_ID_TASK_HEADER]
     spec = model.MapreduceSpec._get_mapreduce_spec(mr_id)
@@ -386,12 +395,17 @@
 
 
     tstate = model.TransientShardState.from_request(self.request)
-    task_state = self._try_acquire_lease(shard_state, tstate)
-    if task_state == self._TASK_STATE.RETRY_TASK:
+
+
+    if shard_state:
+      is_this_a_retry = shard_state.acquired_once
+    task_directive = self._try_acquire_lease(shard_state, tstate)
+    if task_directive == self._TASK_DIRECTIVE.RETRY_TASK:
       return self.retry_task()
-    if task_state == self._TASK_STATE.DROP_TASK:
+    if task_directive == self._TASK_DIRECTIVE.DROP_TASK:
       return
-    assert task_state == self._TASK_STATE.PROCEED_TASK
+    assert task_directive == self._TASK_DIRECTIVE.PROCEED_TASK
+
 
     if control and control.command == model.MapreduceControl.ABORT:
       logging.info("Abort command received by shard %d of job '%s'",
@@ -411,34 +425,42 @@
               urlfetch_timeout=parameters._GCS_URLFETCH_TIMEOUT_SEC))
 
     try:
-      finished_shard = self.process_inputs(
-          tstate.input_reader, shard_state, tstate, ctx)
+      if is_this_a_retry:
+        task_directive = self._attempt_slice_recovery(shard_state, tstate)
 
-      if finished_shard:
+      if task_directive == self._TASK_DIRECTIVE.PROCEED_TASK:
+        finished_shard = self._process_inputs(
+            tstate.input_reader, shard_state, tstate, ctx)
+        if finished_shard:
 
 
-        if tstate.output_writer:
+          if tstate.output_writer:
 
 
 
 
-          tstate.output_writer.finalize(ctx, shard_state)
-        shard_state.set_for_success()
+            tstate.output_writer.finalize(ctx, shard_state)
+          shard_state.set_for_success()
 
     except Exception, e:
-      task_state = self._retry_logic(
-          e, shard_state, tstate, spec.mapreduce_id)
+      logging.warning("Shard %s got error.", shard_state.shard_id)
+      logging.error(traceback.format_exc())
 
-    if task_state == self._TASK_STATE.RETRY_TASK:
 
-      return self.retry_task()
-    self._save_state_and_schedule_next(shard_state, tstate, task_state)
+      if type(e) is errors.FailJobError:
+        logging.error("Got FailJobError.")
+        task_directive = self._TASK_DIRECTIVE.FAIL_TASK
+      else:
+        task_directive = self._TASK_DIRECTIVE.RETRY_TASK
 
-  def process_inputs(self,
-                     input_reader,
-                     shard_state,
-                     tstate,
-                     ctx):
+    task_directive = self._set_state(shard_state, tstate, task_directive)
+    self._save_state_and_schedule_next(shard_state, tstate, task_directive)
+
+  def _process_inputs(self,
+                      input_reader,
+                      shard_state,
+                      tstate,
+                      ctx):
     """Read inputs, process them, and write out outputs.
 
     This is the core logic of MapReduce. It reads inputs from input reader,
@@ -465,7 +487,24 @@
 
     finished_shard = True
 
-    for entity in input_reader:
+    iterator = iter(input_reader)
+
+    while True:
+      try:
+        entity = iterator.next()
+      except StopIteration:
+        break
+
+
+
+
+
+
+
+
+
+
+
       if isinstance(entity, db.Model):
         shard_state.last_work_item = repr(entity.key())
       elif isinstance(entity, ndb.Model):
@@ -475,7 +514,7 @@
 
       processing_limit -= 1
 
-      if not self.process_data(
+      if not self._process_datum(
           entity, input_reader, ctx, tstate):
         finished_shard = False
         break
@@ -491,7 +530,7 @@
 
     return finished_shard
 
-  def process_data(self, data, input_reader, ctx, transient_shard_state):
+  def _process_datum(self, data, input_reader, ctx, transient_shard_state):
     """Process a single data piece.
 
     Call mapper handler on the data.
@@ -522,7 +561,7 @@
           else:
             output_writer = transient_shard_state.output_writer
             if not output_writer:
-              logging.error(
+              logging.warning(
                   "Handler yielded %s, but no output writer is set.", output)
             else:
               output_writer.write(output)
@@ -531,36 +570,80 @@
       return False
     return True
 
-  def _save_state_and_schedule_next(self, shard_state, tstate, task_state):
-    """Save state to datastore and schedule next task for this shard.
-
-    Update and save shard state. Schedule next slice if needed.
-    This method handles interactions with datastore and taskqueue.
+  def _set_state(self, shard_state, tstate, task_directive):
+    """Set shard_state and tstate based on task_directive.
 
     Args:
       shard_state: model.ShardState for current shard.
       tstate: model.TransientShardState for current shard.
-      task_state: enum _TASK_STATE.
+      task_directive: self._TASK_DIRECTIVE for current shard.
+
+    Returns:
+      A _TASK_DIRECTIVE enum. PROCEED_TASK if task should proceed normally.
+    RETRY_SHARD if shard should be retried. RETRY_TASK if slice should be
+    retried. FAIL_TASK if task should. RECOVER_SLICE if slice should be
+    recovered.
     """
-
-    spec = tstate.mapreduce_spec
-    config = util.create_datastore_write_config(spec)
-
-
-    if task_state == self._TASK_STATE.RETRY_SHARD:
-
-
-      task = self._state_to_task(tstate, shard_state)
-    elif task_state == self._TASK_STATE.PROCEED_TASK:
+    if task_directive == self._TASK_DIRECTIVE.PROCEED_TASK:
       shard_state.advance_for_next_slice()
       tstate.advance_for_next_slice()
+      return task_directive
+
+    if task_directive == self._TASK_DIRECTIVE.RECOVER_SLICE:
+      tstate.advance_for_next_slice(recovery_slice=True)
+      shard_state.advance_for_next_slice(recovery_slice=True)
+      return task_directive
+
+    if task_directive == self._TASK_DIRECTIVE.RETRY_TASK:
+      task_directive = self._attempt_slice_retry(shard_state, tstate)
+    if task_directive == self._TASK_DIRECTIVE.RETRY_SHARD:
+      task_directive = self._attempt_shard_retry(shard_state, tstate)
+    if task_directive == self._TASK_DIRECTIVE.FAIL_TASK:
+      shard_state.set_for_failure()
+    return task_directive
+
+  def _save_state_and_schedule_next(self, shard_state, tstate, task_directive):
+    """Save state and schedule task.
+
+    Save shard state to datastore.
+    Schedule next slice if needed.
+    Set HTTP response code.
+    No modification to any shard_state or tstate.
+
+    Args:
+      shard_state: model.ShardState for current shard.
+      tstate: model.TransientShardState for current shard.
+      task_directive: enum _TASK_DIRECTIVE.
+    """
+    spec = tstate.mapreduce_spec
+
+    if task_directive == self._TASK_DIRECTIVE.RETRY_TASK:
+
+      return self.retry_task()
+    elif task_directive == self._TASK_DIRECTIVE.FAIL_TASK:
+      logging.critical("Shard %s failed permanently.", shard_state.shard_id)
+      task = None
+    elif task_directive == self._TASK_DIRECTIVE.RETRY_SHARD:
+      logging.warning("Shard %s is going to be attempted for the %s time.",
+                      shard_state.shard_id,
+                      shard_state.retries + 1)
+      task = self._state_to_task(tstate, shard_state)
+    elif task_directive == self._TASK_DIRECTIVE.RECOVER_SLICE:
+      logging.warning("Shard %s slice %s is being recovered.",
+                      shard_state.shard_id,
+                      shard_state.slice_id)
+      task = self._state_to_task(tstate, shard_state)
+    else:
+      assert task_directive == self._TASK_DIRECTIVE.PROCEED_TASK
       countdown = self._get_countdown_for_next_slice(spec)
       task = self._state_to_task(tstate, shard_state, countdown=countdown)
-    else:
-      assert task_state == self._TASK_STATE.FAIL_TASK
-      task = None
 
-    queue_name = os.environ.get("HTTP_X_APPENGINE_QUEUENAME", "default")
+
+    queue_name = os.environ.get("HTTP_X_APPENGINE_QUEUENAME",
+
+
+                                "default")
+    config = util.create_datastore_write_config(spec)
 
     @db.transactional(retries=5)
     def _tx():
@@ -569,10 +652,10 @@
         raise db.Rollback()
       if (not fresh_shard_state.active or
           "worker_active_state_collision" in _TEST_INJECTED_FAULTS):
-        logging.error("Shard %s is not active. Possible spurious task "
-                      "execution. Dropping this task.", tstate.shard_id)
-        logging.error("Datastore's %s", str(fresh_shard_state))
-        logging.error("Slice's %s", str(shard_state))
+        logging.warning("Shard %s is not active. Possible spurious task "
+                        "execution. Dropping this task.", tstate.shard_id)
+        logging.warning("Datastore's %s", str(fresh_shard_state))
+        logging.warning("Slice's %s", str(shard_state))
         return
       fresh_shard_state.copy_from(shard_state)
       fresh_shard_state.put(config=config)
@@ -591,7 +674,7 @@
             taskqueue.Error,
             runtime.DeadlineExceededError,
             apiproxy_errors.Error), e:
-      logging.error(
+      logging.warning(
           "Can't transactionally continue shard. "
           "Will retry slice %s %s for the %s time.",
           tstate.shard_id,
@@ -599,45 +682,48 @@
           self.task_retry_count() + 1)
       self._try_free_lease(shard_state)
       raise e
-    finally:
-      gc.collect()
 
-  def _retry_logic(self, e, shard_state, tstate, mr_id):
-    """Handle retry for this slice.
+  def _attempt_slice_recovery(self, shard_state, tstate):
+    """Recover a slice.
 
-    This method may modify shard_state and tstate to prepare for retry or fail.
+    This is run when a slice had been previously attempted and output
+    may have been written. If an output writer requires slice recovery,
+    we run those logic to remove output duplicates. Otherwise we just retry
+    the slice.
+
+    If recovery is needed, then the entire slice will be dedicated
+    to recovery logic. No data processing will take place. Thus we call
+    the slice "recovery slice". This is needed for correctness:
+    An output writer instance can be out of sync from its physical
+    medium only when the slice dies after acquring the shard lock but before
+    committing shard state to db. The worst failure case is when
+    shard state failed to commit after the NAMED task for the next slice was
+    added. Thus, recovery slice has a special logic to increment current
+    slice_id n to n+2. If the task for n+1 had been added, it will be dropped
+    because it is behind shard state.
 
     Args:
-      e: the exception caught.
-      shard_state: model.ShardState for current shard.
-      tstate: model.TransientShardState for current shard.
-      mr_id: mapreduce id.
+      shard_state: an instance of Model.ShardState.
+      tstate: an instance of Model.TransientShardState.
 
     Returns:
-      A _TASK_STATE enum. RETRY_SHARD if shard should be retried.
-    RETRY_TASK if slice should be retried. FAIL_TASK otherwise.
+      _TASK_DIRECTIVE.PROCEED_TASK to continue with this retry.
+      _TASK_DIRECTIVE.RECOVER_SLICE to recover this slice.
+      The next slice will start at the same input as
+      this slice but output to a new instance of output writer.
+      Combining outputs from all writer instances is up to implementation.
     """
-    logging.error("Shard %s got error.", shard_state.shard_id)
+    mapper_spec = tstate.mapreduce_spec.mapper
+    if not (tstate.output_writer and
+            tstate.output_writer._supports_slice_recovery(mapper_spec)):
+      return self._TASK_DIRECTIVE.PROCEED_TASK
 
+    tstate.output_writer = tstate.output_writer._recover(
+        tstate.mapreduce_spec, shard_state.shard_number,
+        shard_state.retries + 1)
+    return self._TASK_DIRECTIVE.RECOVER_SLICE
 
-    logging.error(traceback.format_exc())
-
-
-    if type(e) is errors.FailJobError:
-      logging.error("Got FailJobError. Shard %s failed permanently.",
-                    shard_state.shard_id)
-      shard_state.set_for_failure()
-      return self._TASK_STATE.FAIL_TASK
-
-    task_state = self._attempt_slice_retry(shard_state, tstate)
-    if task_state == self._TASK_STATE.RETRY_SHARD:
-      task_state = self._attempt_shard_retry(shard_state, tstate, mr_id)
-    if task_state == self._TASK_STATE.FAIL_TASK:
-      shard_state.set_for_failure()
-      logging.error("Shard %s failed permanently.", shard_state.shard_id)
-    return task_state
-
-  def _attempt_shard_retry(self, shard_state, tstate, mr_id):
+  def _attempt_shard_retry(self, shard_state, tstate):
     """Whether to retry shard.
 
     This method may modify shard_state and tstate to prepare for retry or fail.
@@ -645,37 +731,35 @@
     Args:
       shard_state: model.ShardState for current shard.
       tstate: model.TransientShardState for current shard.
-      mr_id: mapreduce id.
 
     Returns:
-      A _TASK_STATE enum. RETRY_SHARD if shard should be retried.
+      A _TASK_DIRECTIVE enum. RETRY_SHARD if shard should be retried.
     FAIL_TASK otherwise.
     """
     shard_attempts = shard_state.retries + 1
 
     if shard_attempts >= parameters.config.SHARD_MAX_ATTEMPTS:
-      logging.error(
+      logging.warning(
           "Shard attempt %s exceeded %s max attempts.",
           shard_attempts, parameters.config.SHARD_MAX_ATTEMPTS)
-      return self._TASK_STATE.FAIL_TASK
+      return self._TASK_DIRECTIVE.FAIL_TASK
     if tstate.output_writer and (
-        not tstate.output_writer._can_be_retried(tstate)):
-      logging.error("Output writer %s does not support shard retry.",
-                    tstate.output_writer.__class__.__name__)
-      return self._TASK_STATE.FAIL_TASK
+        not tstate.output_writer._supports_shard_retry(tstate)):
+      logging.warning("Output writer %s does not support shard retry.",
+                      tstate.output_writer.__class__.__name__)
+      return self._TASK_DIRECTIVE.FAIL_TASK
 
     shard_state.reset_for_retry()
-    logging.error("Shard %s attempt %s failed with up to %s attempts.",
-                  shard_state.shard_id,
-                  shard_state.retries,
-                  parameters.config.SHARD_MAX_ATTEMPTS)
+    logging.warning("Shard %s attempt %s failed with up to %s attempts.",
+                    shard_state.shard_id,
+                    shard_state.retries,
+                    parameters.config.SHARD_MAX_ATTEMPTS)
     output_writer = None
     if tstate.output_writer:
-      mr_state = model.MapreduceState.get_by_job_id(mr_id)
       output_writer = tstate.output_writer.create(
-          mr_state, shard_state)
+          tstate.mapreduce_spec, shard_state.shard_number, shard_attempts + 1)
     tstate.reset_for_retry(output_writer)
-    return self._TASK_STATE.RETRY_SHARD
+    return self._TASK_DIRECTIVE.RETRY_SHARD
 
   def _attempt_slice_retry(self, shard_state, tstate):
     """Attempt to retry this slice.
@@ -687,12 +771,12 @@
       tstate: model.TransientShardState for current shard.
 
     Returns:
-      A _TASK_STATE enum. RETRY_TASK if slice should be retried.
+      A _TASK_DIRECTIVE enum. RETRY_TASK if slice should be retried.
     RETRY_SHARD if shard retry should be attempted.
     """
     if (shard_state.slice_retries + 1 <
         parameters.config.TASK_MAX_DATA_PROCESSING_ATTEMPTS):
-      logging.error(
+      logging.warning(
           "Slice %s %s failed for the %s of up to %s attempts "
           "(%s of %s taskqueue execution attempts). "
           "Will retry now.",
@@ -707,13 +791,13 @@
 
       sys.exc_clear()
       self._try_free_lease(shard_state, slice_retry=True)
-      return self._TASK_STATE.RETRY_TASK
+      return self._TASK_DIRECTIVE.RETRY_TASK
 
     if parameters.config.TASK_MAX_DATA_PROCESSING_ATTEMPTS > 0:
-      logging.error("Slice attempt %s exceeded %s max attempts.",
-                    self.task_retry_count() + 1,
-                    parameters.config.TASK_MAX_DATA_PROCESSING_ATTEMPTS)
-    return self._TASK_STATE.RETRY_SHARD
+      logging.warning("Slice attempt %s exceeded %s max attempts.",
+                      self.task_retry_count() + 1,
+                      parameters.config.TASK_MAX_DATA_PROCESSING_ATTEMPTS)
+    return self._TASK_DIRECTIVE.RETRY_SHARD
 
   @staticmethod
   def get_task_name(shard_id, slice_id, retry=0):
@@ -781,7 +865,7 @@
     headers[util._MR_SHARD_ID_TASK_HEADER] = tstate.shard_id
 
     worker_task = model.HugeTask(
-        url=base_path + "/worker_callback",
+        url=base_path + "/worker_callback/" + tstate.shard_id,
         params=tstate.to_dict(),
         name=task_name,
         eta=eta,
@@ -928,7 +1012,7 @@
       logging.warning(
           "MR %r is not active. Looks like spurious controller task execution.",
           spec.mapreduce_id)
-      self._clean_up_mr(spec, self.base_path())
+      self._clean_up_mr(spec)
       return
 
     shard_states = model.ShardState.find_all_by_mapreduce_state(state)
@@ -936,7 +1020,7 @@
 
     if state.active:
       ControllerCallbackHandler.reschedule(
-          state, self.base_path(), spec, self.serial_id() + 1)
+          state, spec, self.serial_id() + 1)
 
   def _update_state_from_shard_states(self, state, shard_states, control):
     """Update mr state by examing shard states.
@@ -997,7 +1081,7 @@
       else:
         state.result_status = model.MapreduceState.RESULT_SUCCESS
       self._finalize_outputs(spec, state)
-      self._finalize_job(spec, state, self.base_path())
+      self._finalize_job(spec, state)
     else:
       @db.transactional(retries=5)
       def _put_state():
@@ -1036,7 +1120,7 @@
       mapreduce_spec.mapper.output_writer_class().finalize_job(mapreduce_state)
 
   @classmethod
-  def _finalize_job(cls, mapreduce_spec, mapreduce_state, base_path):
+  def _finalize_job(cls, mapreduce_spec, mapreduce_state):
     """Finalize job execution.
 
     Invokes done callback and save mapreduce state in a transaction,
@@ -1045,7 +1129,6 @@
     Args:
       mapreduce_spec: an instance of MapreduceSpec
       mapreduce_state: an instance of MapreduceState
-      base_path: handler_base path.
     """
     config = util.create_datastore_write_config(mapreduce_spec)
     queue_name = util.get_queue_name(mapreduce_spec.params.get(
@@ -1081,11 +1164,11 @@
     _put_state()
     logging.info("Final result for job '%s' is '%s'",
                  mapreduce_spec.mapreduce_id, mapreduce_state.result_status)
-    cls._clean_up_mr(mapreduce_spec, base_path)
+    cls._clean_up_mr(mapreduce_spec)
 
   @classmethod
-  def _clean_up_mr(cls, mapreduce_spec, base_path):
-    FinalizeJobHandler.schedule(base_path, mapreduce_spec)
+  def _clean_up_mr(cls, mapreduce_spec):
+    FinalizeJobHandler.schedule(mapreduce_spec)
 
   @staticmethod
   def get_task_name(mapreduce_spec, serial_id):
@@ -1122,7 +1205,6 @@
   @classmethod
   def reschedule(cls,
                  mapreduce_state,
-                 base_path,
                  mapreduce_spec,
                  serial_id,
                  queue_name=None):
@@ -1130,7 +1212,6 @@
 
     Args:
       mapreduce_state: mapreduce state as model.MapreduceState
-      base_path: mapreduce handlers url base path as string.
       mapreduce_spec: mapreduce specification as MapreduceSpec.
       serial_id: id of the invocation as int.
       queue_name: The queue to schedule this task on. Will use the current
@@ -1144,7 +1225,8 @@
       queue_name = os.environ.get("HTTP_X_APPENGINE_QUEUENAME", "default")
 
     controller_callback_task = model.HugeTask(
-        url=base_path + "/controller_callback",
+        url=(mapreduce_spec.params["base_path"] + "/controller_callback/" +
+             mapreduce_spec.mapreduce_id),
         name=task_name, params=task_params,
         countdown=parameters.config._CONTROLLER_PERIOD_SEC,
         parent=mapreduce_state,
@@ -1196,7 +1278,7 @@
       state.active = False
       state.result_status = model.MapreduceState.RESULT_SUCCESS
       ControllerCallbackHandler._finalize_job(
-          state.mapreduce_spec, state, self.base_path())
+          state.mapreduce_spec, state)
       return False
 
 
@@ -1212,11 +1294,12 @@
 
     queue_name = self.request.headers.get("X-AppEngine-QueueName")
     KickOffJobHandler._schedule_shards(state.mapreduce_spec, readers,
-                                       queue_name, self.base_path(), state)
+                                       queue_name,
+                                       state.mapreduce_spec.params["base_path"],
+                                       state)
 
     ControllerCallbackHandler.reschedule(
-        state, self.base_path(), state.mapreduce_spec, serial_id=0,
-        queue_name=queue_name)
+        state, state.mapreduce_spec, serial_id=0, queue_name=queue_name)
 
   def _get_input_readers(self, state):
     """Get input readers.
@@ -1326,6 +1409,7 @@
                               if shard is not None)
 
 
+
     db.put((shard for shard in shard_states
             if shard.key() not in existing_shard_keys),
            config=util.create_datastore_write_config(spec))
@@ -1335,7 +1419,10 @@
     writers = [None] * len(readers)
     if writer_class:
       for shard_number, shard_state in enumerate(shard_states):
-        writers[shard_number] = writer_class.create(mr_state, shard_state)
+        writers[shard_number] = writer_class.create(
+            mr_state.mapreduce_spec,
+            shard_state.shard_number, shard_state.retries + 1,
+            mr_state.writer_state)
 
 
 
@@ -1396,6 +1483,8 @@
         "mapper_params_validator", "mapper_params.")
     params = self._get_params(
         "params_validator", "params.")
+    if "base_path" not in params:
+      params["base_path"] = parameters.config.BASE_PATH
 
 
     mapper_params["processing_rate"] = int(mapper_params.get(
@@ -1415,7 +1504,6 @@
         mapreduce_name,
         mapper_spec,
         params,
-        base_path=self.base_path(),
         queue_name=queue_name,
         _app=mapper_params.get("_app"))
     self.json_response["mapreduce_id"] = mapreduce_id
@@ -1472,7 +1560,6 @@
                  name,
                  mapper_spec,
                  mapreduce_params,
-                 base_path,
                  queue_name,
                  eta=None,
                  countdown=None,
@@ -1527,7 +1614,7 @@
     @db.transactional(propagation=propagation)
     def _txn():
       cls._create_and_save_state(mapreduce_spec, _app)
-      cls._add_kickoff_task(base_path, mapreduce_spec, eta,
+      cls._add_kickoff_task(mapreduce_params["base_path"], mapreduce_spec, eta,
                             countdown, queue_name)
     _txn()
 
@@ -1566,7 +1653,7 @@
     params = {"mapreduce_id": mapreduce_spec.mapreduce_id}
 
     kickoff_task = taskqueue.Task(
-        url=base_path + "/kickoffjob_callback",
+        url=base_path + "/kickoffjob_callback/" + mapreduce_spec.mapreduce_id,
         headers=util._get_task_headers(mapreduce_spec),
         params=params,
         eta=eta,
@@ -1598,7 +1685,7 @@
       db.delete(keys, config=config)
 
   @classmethod
-  def schedule(cls, base_path, mapreduce_spec):
+  def schedule(cls, mapreduce_spec):
     """Schedule finalize task.
 
     Args:
@@ -1607,7 +1694,8 @@
     task_name = mapreduce_spec.mapreduce_id + "-finalize"
     finalize_task = taskqueue.Task(
         name=task_name,
-        url=base_path + "/finalizejob_callback",
+        url=(mapreduce_spec.params["base_path"] + "/finalizejob_callback/" +
+             mapreduce_spec.mapreduce_id),
         params={"mapreduce_id": mapreduce_spec.mapreduce_id},
         headers=util._get_task_headers(mapreduce_spec))
     queue_name = util.get_queue_name(None)
diff --git a/google/appengine/ext/mapreduce/input_readers.py b/google/appengine/ext/mapreduce/input_readers.py
index ccd6def..a624d77 100644
--- a/google/appengine/ext/mapreduce/input_readers.py
+++ b/google/appengine/ext/mapreduce/input_readers.py
@@ -38,7 +38,6 @@
     "BlobstoreLineInputReader",
     "BlobstoreZipInputReader",
     "BlobstoreZipLineInputReader",
-    "ConsistentKeyReader",
     "COUNTER_IO_READ_BYTES",
     "COUNTER_IO_READ_MSEC",
     "DatastoreEntityInputReader",
@@ -73,6 +72,7 @@
 from google.appengine.api import datastore
 from google.appengine.api import files
 from google.appengine.api import logservice
+from google.appengine.api.files import file_service_pb
 from google.appengine.api.logservice import log_service_pb
 from google.appengine.ext import blobstore
 from google.appengine.ext import db
@@ -83,6 +83,7 @@
 from google.appengine.ext.mapreduce import errors
 from google.appengine.ext.mapreduce import file_format_parser
 from google.appengine.ext.mapreduce import file_format_root
+from google.appengine.ext.mapreduce import json_util
 from google.appengine.ext.mapreduce import key_ranges
 from google.appengine.ext.mapreduce import model
 from google.appengine.ext.mapreduce import namespace_range
@@ -119,7 +120,7 @@
 ALLOW_CHECKPOINT = object()
 
 
-class InputReader(model.JsonMixin):
+class InputReader(json_util.JsonMixin):
   """Abstract base class for input readers.
 
   InputReaders have the following properties:
@@ -2727,6 +2728,103 @@
         self._record_reader = None
 
 
+class _ReducerReader(RecordsReader):
+  """Reader to read KeyValues records files from Files API."""
 
+  expand_parameters = True
 
-ConsistentKeyReader = DatastoreKeyInputReader
+  def __init__(self, filenames, position):
+    super(_ReducerReader, self).__init__(filenames, position)
+    self.current_key = None
+    self.current_values = None
+
+  def __iter__(self):
+    ctx = context.get()
+    combiner = None
+
+    if ctx:
+      combiner_spec = ctx.mapreduce_spec.mapper.params.get("combiner_spec")
+      if combiner_spec:
+        combiner = util.handler_for_name(combiner_spec)
+
+    for binary_record in super(_ReducerReader, self).__iter__():
+      proto = file_service_pb.KeyValues()
+      proto.ParseFromString(binary_record)
+
+      if self.current_key is None:
+        self.current_key = proto.key()
+        self.current_values = []
+      else:
+        assert proto.key() == self.current_key, (
+            "inconsistent key sequence. Expected %s but got %s" %
+            (self.current_key, proto.key()))
+
+      if combiner:
+        combiner_result = combiner(
+            self.current_key, proto.value_list(), self.current_values)
+
+        if not util.is_generator(combiner_result):
+          raise errors.BadCombinerOutputError(
+              "Combiner %s should yield values instead of returning them (%s)" %
+              (combiner, combiner_result))
+
+        self.current_values = []
+        for value in combiner_result:
+          if isinstance(value, operation.Operation):
+            value(ctx)
+          else:
+
+            self.current_values.append(value)
+      else:
+
+        self.current_values.extend(proto.value_list())
+
+      if not proto.partial():
+        key = self.current_key
+        values = self.current_values
+
+        self.current_key = None
+        self.current_values = None
+        yield (key, values)
+      else:
+        yield ALLOW_CHECKPOINT
+
+  @staticmethod
+  def encode_data(data):
+    """Encodes the given data, which may have include raw bytes.
+
+    Works around limitations in JSON encoding, which cannot handle raw bytes.
+    """
+
+    return base64.b64encode(pickle.dumps(data))
+
+  @staticmethod
+  def decode_data(data):
+    """Decodes data encoded with the encode_data function."""
+    return pickle.loads(base64.b64decode(data))
+
+  def to_json(self):
+    """Returns an input shard state for the remaining inputs.
+
+    Returns:
+      A json-izable version of the remaining InputReader.
+    """
+    result = super(_ReducerReader, self).to_json()
+    result["current_key"] = _ReducerReader.encode_data(self.current_key)
+    result["current_values"] = _ReducerReader.encode_data(self.current_values)
+    return result
+
+  @classmethod
+  def from_json(cls, json):
+    """Creates an instance of the InputReader for the given input shard state.
+
+    Args:
+      json: The InputReader state as a dict-like object.
+
+    Returns:
+      An instance of the InputReader configured using the values of json.
+    """
+    result = super(_ReducerReader, cls).from_json(json)
+    result.current_key = _ReducerReader.decode_data(json["current_key"])
+    result.current_values = _ReducerReader.decode_data(json["current_values"])
+    return result
diff --git a/google/appengine/ext/mapreduce/json_util.py b/google/appengine/ext/mapreduce/json_util.py
new file mode 100644
index 0000000..5b7b772
--- /dev/null
+++ b/google/appengine/ext/mapreduce/json_util.py
@@ -0,0 +1,245 @@
+#!/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.
+#
+"""Json related utilities."""
+import copy
+import datetime
+import logging
+import simplejson
+
+from google.appengine.api import datastore_errors
+from google.appengine.api import datastore_types
+from google.appengine.ext import db
+
+
+
+
+
+class JsonEncoder(simplejson.JSONEncoder):
+  """MR customized json encoder."""
+
+  TYPE_ID = "__mr_json_type"
+
+  def default(self, o):
+    """Inherit docs."""
+    if type(o) in _TYPE_TO_ENCODER:
+      encoder = _TYPE_TO_ENCODER[type(o)]
+      json_struct = encoder(o)
+      json_struct[self.TYPE_ID] = type(o).__name__
+      return json_struct
+    return super(JsonEncoder, self).default(o)
+
+
+class JsonDecoder(simplejson.JSONDecoder):
+  """MR customized json decoder."""
+
+  def __init__(self, **kwargs):
+    if "object_hook" not in kwargs:
+      kwargs["object_hook"] = self._dict_to_obj
+    super(JsonDecoder, self).__init__(**kwargs)
+
+  def _dict_to_obj(self, d):
+    """Converts a dictionary of json object to a Python object."""
+    if JsonEncoder.TYPE_ID not in d:
+      return d
+
+    type_name = d.pop(JsonEncoder.TYPE_ID)
+    if type_name in _TYPE_NAME_TO_DECODER:
+      decoder = _TYPE_NAME_TO_DECODER[type_name]
+      return decoder(d)
+    else:
+      raise TypeError("Invalid type %s.", type_name)
+
+
+_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
+
+
+def _json_encode_datetime(o):
+  """Json encode a datetime object.
+
+  Args:
+    o: a datetime object.
+
+  Returns:
+    A dict of json primitives.
+  """
+  return {"isostr": o.strftime(_DATETIME_FORMAT)}
+
+
+def _json_decode_datetime(d):
+  """Converts a dict of json primitives to a datetime object."""
+  return datetime.datetime.strptime(d["isostr"], _DATETIME_FORMAT)
+
+
+def _register_json_primitive(object_type, encoder, decoder):
+  """Extend what MR can json serialize.
+
+  Args:
+    object_type: type of the object.
+    encoder: a function that takes in an object and returns a dict of
+       json primitives.
+    decoder: inverse function of encoder.
+  """
+  global _TYPE_TO_ENCODER
+  global _TYPE_NAME_TO_DECODER
+  if object_type not in _TYPE_TO_ENCODER:
+    _TYPE_TO_ENCODER[object_type] = encoder
+    _TYPE_NAME_TO_DECODER[object_type.__name__] = decoder
+
+
+_TYPE_TO_ENCODER = {}
+_TYPE_NAME_TO_DECODER = {}
+_register_json_primitive(datetime.datetime,
+                         _json_encode_datetime,
+                         _json_decode_datetime)
+
+
+class JsonMixin(object):
+  """Simple, stateless json utilities mixin.
+
+  Requires class to implement two methods:
+    to_json(self): convert data to json-compatible datastructure (dict,
+      list, strings, numbers)
+    @classmethod from_json(cls, json): load data from json-compatible structure.
+  """
+
+  def to_json_str(self):
+    """Convert data to json string representation.
+
+    Returns:
+      json representation as string.
+    """
+    json = self.to_json()
+    try:
+      return simplejson.dumps(json, sort_keys=True, cls=JsonEncoder)
+    except:
+      logging.exception("Could not serialize JSON: %r", json)
+      raise
+
+  @classmethod
+  def from_json_str(cls, json_str):
+    """Convert json string representation into class instance.
+
+    Args:
+      json_str: json representation as string.
+
+    Returns:
+      New instance of the class with data loaded from json string.
+    """
+    return cls.from_json(simplejson.loads(json_str, cls=JsonDecoder))
+
+
+class JsonProperty(db.UnindexedProperty):
+  """Property type for storing json representation of data.
+
+  Requires data types to implement two methods:
+    to_json(self): convert data to json-compatible datastructure (dict,
+      list, strings, numbers)
+    @classmethod from_json(cls, json): load data from json-compatible structure.
+  """
+
+  def __init__(self, data_type, default=None, **kwargs):
+    """Constructor.
+
+    Args:
+      data_type: underlying data type as class.
+      default: default value for the property. The value is deep copied
+        fore each model instance.
+      **kwargs: remaining arguments.
+    """
+    kwargs["default"] = default
+    super(JsonProperty, self).__init__(**kwargs)
+    self.data_type = data_type
+
+  def get_value_for_datastore(self, model_instance):
+    """Gets value for datastore.
+
+    Args:
+      model_instance: instance of the model class.
+
+    Returns:
+      datastore-compatible value.
+    """
+    value = super(JsonProperty, self).get_value_for_datastore(model_instance)
+    if not value:
+      return None
+    json_value = value
+    if not isinstance(value, dict):
+      json_value = value.to_json()
+    if not json_value:
+      return None
+    return datastore_types.Text(simplejson.dumps(
+        json_value, sort_keys=True, cls=JsonEncoder))
+
+  def make_value_from_datastore(self, value):
+    """Convert value from datastore representation.
+
+    Args:
+      value: datastore value.
+
+    Returns:
+      value to store in the model.
+    """
+
+    if value is None:
+      return None
+    json = simplejson.loads(value, cls=JsonDecoder)
+    if self.data_type == dict:
+      return json
+    return self.data_type.from_json(json)
+
+  def validate(self, value):
+    """Validate value.
+
+    Args:
+      value: model value.
+
+    Returns:
+      Whether the specified value is valid data type value.
+
+    Raises:
+      BadValueError: when value is not of self.data_type type.
+    """
+    if value is not None and not isinstance(value, self.data_type):
+      raise datastore_errors.BadValueError(
+          "Property %s must be convertible to a %s instance (%s)" %
+          (self.name, self.data_type, value))
+    return super(JsonProperty, self).validate(value)
+
+  def empty(self, value):
+    """Checks if value is empty.
+
+    Args:
+      value: model value.
+
+    Returns:
+      True passed value is empty.
+    """
+    return not value
+
+  def default_value(self):
+    """Create default model value.
+
+    If default option was specified, then it will be deeply copied.
+    None otherwise.
+
+    Returns:
+      default model value.
+    """
+    if self.default:
+      return copy.deepcopy(self.default)
+    else:
+      return None
diff --git a/google/appengine/ext/mapreduce/main.py b/google/appengine/ext/mapreduce/main.py
index 7732022..118b4d1 100644
--- a/google/appengine/ext/mapreduce/main.py
+++ b/google/appengine/ext/mapreduce/main.py
@@ -84,10 +84,12 @@
 
   return pipeline_handlers_map + [
 
-      (r".*/worker_callback.*", handlers.MapperWorkerCallbackHandler),
-      (r".*/controller_callback.*", handlers.ControllerCallbackHandler),
-      (r".*/kickoffjob_callback.*", handlers.KickOffJobHandler),
-      (r".*/finalizejob_callback.*", handlers.FinalizeJobHandler),
+
+
+      (r".*/worker_callback/.*", handlers.MapperWorkerCallbackHandler),
+      (r".*/controller_callback/.*", handlers.ControllerCallbackHandler),
+      (r".*/kickoffjob_callback/.*", handlers.KickOffJobHandler),
+      (r".*/finalizejob_callback/.*", handlers.FinalizeJobHandler),
 
 
 
diff --git a/google/appengine/ext/mapreduce/mapper_pipeline.py b/google/appengine/ext/mapreduce/mapper_pipeline.py
index c7c3b6b..8347471 100644
--- a/google/appengine/ext/mapreduce/mapper_pipeline.py
+++ b/google/appengine/ext/mapreduce/mapper_pipeline.py
@@ -118,10 +118,19 @@
     self.set_status(console_url="%s/detail?job_id=%s" % (
         (parameters.config.BASE_PATH, mapreduce_id)))
 
+  def try_cancel(self):
+    """Always allow mappers to be canceled and retried."""
+    return True
+
   def callback(self):
     """Callback after this async pipeline finishes."""
     mapreduce_id = self.outputs.job_id.value
     mapreduce_state = model.MapreduceState.get_by_job_id(mapreduce_id)
+    if mapreduce_state.result_status != model.MapreduceState.RESULT_SUCCESS:
+      self.retry("Job %s had status %s" % (
+          mapreduce_id, mapreduce_state.result_status))
+      return
+
     mapper_spec = mapreduce_state.mapreduce_spec.mapper
     outputs = []
     output_writer_class = mapper_spec.output_writer_class()
diff --git a/google/appengine/ext/mapreduce/mapreduce_pipeline.py b/google/appengine/ext/mapreduce/mapreduce_pipeline.py
index dffbb8e..1c802d5 100644
--- a/google/appengine/ext/mapreduce/mapreduce_pipeline.py
+++ b/google/appengine/ext/mapreduce/mapreduce_pipeline.py
@@ -44,25 +44,17 @@
     "ShufflePipeline",
     ]
 
-import base64
-import pickle
-
 import google
 
 from appengine_pipeline.src import pipeline
 from appengine_pipeline.src.pipeline import common as pipeline_common
 from google.appengine.api import files
-from google.appengine.api.files import file_service_pb
-from google.appengine.ext.mapreduce import context
-from google.appengine.ext.mapreduce import errors
 from google.appengine.ext.mapreduce import input_readers
 from google.appengine.ext.mapreduce import mapper_pipeline
 from google.appengine.ext.mapreduce import model
-from google.appengine.ext.mapreduce import operation
 from google.appengine.ext.mapreduce import output_writers
 from google.appengine.ext.mapreduce import pipeline_base
 from google.appengine.ext.mapreduce import shuffler
-from google.appengine.ext.mapreduce import util
 
 
 
@@ -76,6 +68,9 @@
 CleanupPipeline = mapper_pipeline._CleanupPipeline
 
 
+_ReducerReader = input_readers._ReducerReader
+
+
 class MapPipeline(pipeline_base._OutputSlotsMixin,
                   pipeline_base.PipelineBase):
   """Runs the map stage of MapReduce.
@@ -110,108 +105,6 @@
         shards=shards)
 
 
-class _ReducerReader(input_readers.RecordsReader):
-  """Reader to read KeyValues records files from Files API."""
-
-  expand_parameters = True
-
-  def __init__(self, filenames, position):
-    super(_ReducerReader, self).__init__(filenames, position)
-    self.current_key = None
-    self.current_values = None
-
-  def __iter__(self):
-    ctx = context.get()
-    combiner = None
-
-    if ctx:
-      combiner_spec = ctx.mapreduce_spec.mapper.params.get("combiner_spec")
-      if combiner_spec:
-        combiner = util.handler_for_name(combiner_spec)
-
-    for binary_record in super(_ReducerReader, self).__iter__():
-      proto = file_service_pb.KeyValues()
-      proto.ParseFromString(binary_record)
-
-      if self.current_key is None:
-        self.current_key = proto.key()
-        self.current_values = []
-      else:
-        assert proto.key() == self.current_key, (
-            "inconsistent key sequence. Expected %s but got %s" %
-            (self.current_key, proto.key()))
-
-      if combiner:
-        combiner_result = combiner(
-            self.current_key, proto.value_list(), self.current_values)
-
-        if not util.is_generator(combiner_result):
-          raise errors.BadCombinerOutputError(
-              "Combiner %s should yield values instead of returning them (%s)" %
-              (combiner, combiner_result))
-
-        self.current_values = []
-        for value in combiner_result:
-          if isinstance(value, operation.Operation):
-            value(ctx)
-          else:
-
-            self.current_values.append(value)
-      else:
-
-        self.current_values.extend(proto.value_list())
-
-      if not proto.partial():
-        key = self.current_key
-        values = self.current_values
-
-        self.current_key = None
-        self.current_values = None
-        yield (key, values)
-      else:
-        yield input_readers.ALLOW_CHECKPOINT
-
-  @staticmethod
-  def encode_data(data):
-    """Encodes the given data, which may have include raw bytes.
-
-    Works around limitations in JSON encoding, which cannot handle raw bytes.
-    """
-
-    return base64.b64encode(pickle.dumps(data))
-
-  @staticmethod
-  def decode_data(data):
-    """Decodes data encoded with the encode_data function."""
-    return pickle.loads(base64.b64decode(data))
-
-  def to_json(self):
-    """Returns an input shard state for the remaining inputs.
-
-    Returns:
-      A json-izable version of the remaining InputReader.
-    """
-    result = super(_ReducerReader, self).to_json()
-    result["current_key"] = _ReducerReader.encode_data(self.current_key)
-    result["current_values"] = _ReducerReader.encode_data(self.current_values)
-    return result
-
-  @classmethod
-  def from_json(cls, json):
-    """Creates an instance of the InputReader for the given input shard state.
-
-    Args:
-      json: The InputReader state as a dict-like object.
-
-    Returns:
-      An instance of the InputReader configured using the values of json.
-    """
-    result = super(_ReducerReader, cls).from_json(json)
-    result.current_key = _ReducerReader.decode_data(json["current_key"])
-    result.current_values = _ReducerReader.decode_data(json["current_values"])
-    return result
-
-
 class ReducePipeline(pipeline_base._OutputSlotsMixin,
                      pipeline_base.PipelineBase):
   """Runs the reduce stage of MapReduce.
diff --git a/google/appengine/ext/mapreduce/model.py b/google/appengine/ext/mapreduce/model.py
index 1236fcd..9af2222 100644
--- a/google/appengine/ext/mapreduce/model.py
+++ b/google/appengine/ext/mapreduce/model.py
@@ -41,12 +41,7 @@
 
 
 
-__all__ = ["JsonEncoder",
-           "JsonDecoder",
-           "JSON_DEFAULTS",
-           "JsonMixin",
-           "JsonProperty",
-           "MapreduceState",
+__all__ = ["MapreduceState",
            "MapperSpec",
            "MapreduceControl",
            "MapreduceSpec",
@@ -57,26 +52,28 @@
            "HugeTask"]
 
 import cgi
-import copy
 import datetime
-import logging
 import os
 import random
-import simplejson
 import time
 import urllib
 import zlib
 
-from google.appengine.api import datastore_errors
-from google.appengine.api import datastore_types
+import google
+from google.appengine._internal.graphy.backends import google_chart_api
+import simplejson
+
 from google.appengine.api import memcache
 from google.appengine.api import taskqueue
 from google.appengine.datastore import datastore_rpc
 from google.appengine.ext import db
 from google.appengine.ext.mapreduce import context
 from google.appengine.ext.mapreduce import hooks
+from google.appengine.ext.mapreduce import json_util
 from google.appengine.ext.mapreduce import util
-from google.appengine._internal.graphy.backends import google_chart_api
+
+
+
 
 
 
@@ -217,8 +214,10 @@
       raise DeprecationWarning(
           "Task is generated by an older incompatible version of mapreduce. "
           "Please kill this job manually")
+    return cls._decode_payload(request.body)
 
-    body = request.body
+  @classmethod
+  def _decode_payload(cls, body):
     compressed_payload_str = None
     if body.startswith(cls.PAYLOAD_KEY_PARAM):
       payload_key = body[len(cls.PAYLOAD_KEY_PARAM):]
@@ -241,211 +240,6 @@
     return result
 
 
-class JsonEncoder(simplejson.JSONEncoder):
-  """MR customized json encoder."""
-
-  TYPE_ID = "__mr_json_type"
-
-  def default(self, o):
-    """Inherit docs."""
-    if type(o) in JSON_DEFAULTS:
-      encoder = JSON_DEFAULTS[type(o)][0]
-      json_struct = encoder(o)
-      json_struct[self.TYPE_ID] = type(o).__name__
-      return json_struct
-    return super(JsonEncoder, self).default(o)
-
-
-class JsonDecoder(simplejson.JSONDecoder):
-  """MR customized json decoder."""
-
-  def __init__(self, **kwargs):
-    if "object_hook" not in kwargs:
-      kwargs["object_hook"] = self._dict_to_obj
-    super(JsonDecoder, self).__init__(**kwargs)
-
-  def _dict_to_obj(self, d):
-    """Converts a dictionary of json object to a Python object."""
-    if JsonEncoder.TYPE_ID not in d:
-      return d
-
-    obj_type = d.pop(JsonEncoder.TYPE_ID)
-    if obj_type in _TYPE_IDS:
-      decoder = JSON_DEFAULTS[_TYPE_IDS[obj_type]][1]
-      return decoder(d)
-    else:
-      raise TypeError("Invalid type %s.", obj_type)
-
-
-_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
-
-
-def _json_encode_datetime(o):
-  """Json encode a datetime object.
-
-  Args:
-    o: a datetime object.
-
-  Returns:
-    A dict of json primitives.
-  """
-  return {"isostr": o.strftime(_DATETIME_FORMAT)}
-
-
-def _json_decode_datetime(d):
-  """Converts a dict of json primitives to a datetime object."""
-  return datetime.datetime.strptime(d["isostr"], _DATETIME_FORMAT)
-
-
-
-
-JSON_DEFAULTS = {
-    datetime.datetime: (_json_encode_datetime, _json_decode_datetime),
-}
-
-
-_TYPE_IDS = dict(zip([_cls.__name__ for _cls in JSON_DEFAULTS],
-                     JSON_DEFAULTS.keys()))
-
-
-class JsonMixin(object):
-  """Simple, stateless json utilities mixin.
-
-  Requires class to implement two methods:
-    to_json(self): convert data to json-compatible datastructure (dict,
-      list, strings, numbers)
-    @classmethod from_json(cls, json): load data from json-compatible structure.
-  """
-
-  def to_json_str(self):
-    """Convert data to json string representation.
-
-    Returns:
-      json representation as string.
-    """
-    json = self.to_json()
-    try:
-      return simplejson.dumps(json, sort_keys=True, cls=JsonEncoder)
-    except:
-      logging.exception("Could not serialize JSON: %r", json)
-      raise
-
-  @classmethod
-  def from_json_str(cls, json_str):
-    """Convert json string representation into class instance.
-
-    Args:
-      json_str: json representation as string.
-
-    Returns:
-      New instance of the class with data loaded from json string.
-    """
-    return cls.from_json(simplejson.loads(json_str, cls=JsonDecoder))
-
-
-class JsonProperty(db.UnindexedProperty):
-  """Property type for storing json representation of data.
-
-  Requires data types to implement two methods:
-    to_json(self): convert data to json-compatible datastructure (dict,
-      list, strings, numbers)
-    @classmethod from_json(cls, json): load data from json-compatible structure.
-  """
-
-  def __init__(self, data_type, default=None, **kwargs):
-    """Constructor.
-
-    Args:
-      data_type: underlying data type as class.
-      default: default value for the property. The value is deep copied
-        fore each model instance.
-      kwargs: remaining arguments.
-    """
-    kwargs["default"] = default
-    super(JsonProperty, self).__init__(**kwargs)
-    self.data_type = data_type
-
-  def get_value_for_datastore(self, model_instance):
-    """Gets value for datastore.
-
-    Args:
-      model_instance: instance of the model class.
-
-    Returns:
-      datastore-compatible value.
-    """
-    value = super(JsonProperty, self).get_value_for_datastore(model_instance)
-    if not value:
-      return None
-    json_value = value
-    if not isinstance(value, dict):
-      json_value = value.to_json()
-    if not json_value:
-      return None
-    return datastore_types.Text(simplejson.dumps(
-        json_value, sort_keys=True, cls=JsonEncoder))
-
-  def make_value_from_datastore(self, value):
-    """Convert value from datastore representation.
-
-    Args:
-      value: datastore value.
-
-    Returns:
-      value to store in the model.
-    """
-
-    if value is None:
-      return None
-    json = simplejson.loads(value, cls=JsonDecoder)
-    if self.data_type == dict:
-      return json
-    return self.data_type.from_json(json)
-
-  def validate(self, value):
-    """Validate value.
-
-    Args:
-      value: model value.
-
-    Returns:
-      Whether the specified value is valid data type value.
-
-    Raises:
-      BadValueError: when value is not of self.data_type type.
-    """
-    if value is not None and not isinstance(value, self.data_type):
-      raise datastore_errors.BadValueError(
-          "Property %s must be convertible to a %s instance (%s)" %
-          (self.name, self.data_type, value))
-    return super(JsonProperty, self).validate(value)
-
-  def empty(self, value):
-    """Checks if value is empty.
-
-    Args:
-      value: model value.
-
-    Returns:
-      True passed value is empty.
-    """
-    return not value
-
-  def default_value(self):
-    """Create default model value.
-
-    If default option was specified, then it will be deeply copied.
-    None otherwise.
-
-    Returns:
-      default model value.
-    """
-    if self.default:
-      return copy.deepcopy(self.default)
-    else:
-      return None
-
-
 
 _FUTURE_TIME = 2**34
 
@@ -470,7 +264,7 @@
   return "%d%s" % (now_descending, request_id_hash)
 
 
-class CountersMap(JsonMixin):
+class CountersMap(json_util.JsonMixin):
   """Maintains map from counter name to counter value.
 
   The class is used to provide basic arithmetics of counter values (buil
@@ -578,7 +372,7 @@
     return self.counters
 
 
-class MapperSpec(JsonMixin):
+class MapperSpec(json_util.JsonMixin):
   """Contains a specification for the mapper phase of the mapreduce.
 
   MapperSpec instance can be changed only during mapreduce starting process,
@@ -686,7 +480,7 @@
     return self.to_json() == other.to_json()
 
 
-class MapreduceSpec(JsonMixin):
+class MapreduceSpec(json_util.JsonMixin):
   """Contains a specification for the whole mapreduce.
 
   MapreduceSpec instance can be changed only during mapreduce starting process,
@@ -830,12 +624,13 @@
   _RESULTS = frozenset([RESULT_SUCCESS, RESULT_FAILED, RESULT_ABORTED])
 
 
-  mapreduce_spec = JsonProperty(MapreduceSpec, indexed=False)
+  mapreduce_spec = json_util.JsonProperty(MapreduceSpec, indexed=False)
   active = db.BooleanProperty(default=True, indexed=False)
   last_poll_time = db.DateTimeProperty(required=True)
-  counters_map = JsonProperty(CountersMap, default=CountersMap(), indexed=False)
+  counters_map = json_util.JsonProperty(
+      CountersMap, default=CountersMap(), indexed=False)
   app_id = db.StringProperty(required=False, indexed=True)
-  writer_state = JsonProperty(dict, indexed=False)
+  writer_state = json_util.JsonProperty(dict, indexed=False)
   active_shards = db.IntegerProperty(default=0, indexed=False)
   failed_shards = db.IntegerProperty(default=0, indexed=False)
   aborted_shards = db.IntegerProperty(default=0, indexed=False)
@@ -940,10 +735,13 @@
 
 
 class TransientShardState(object):
-  """Shard's state kept in task payload.
+  """A shard's states that are kept in task payload.
 
-  TransientShardState holds a port of all shard processing state, which is not
-  saved in datastore, but rather is passed in task payload.
+  TransientShardState holds two types of states:
+  1. Some states just don't need to be saved to datastore. e.g.
+     serialized input reader and output writer instances.
+  2. Some states are duplicated from datastore, e.g. slice_id, shard_id.
+     These are used to validate the task.
   """
 
   def __init__(self,
@@ -959,7 +757,7 @@
     """Init.
 
     Args:
-      base_path: base path of this mapreduce job.
+      base_path: base path of this mapreduce job. Deprecated.
       mapreduce_spec: an instance of MapReduceSpec.
       shard_id: shard id.
       slice_id: slice id. When enqueuing task for the next slice, this number
@@ -981,6 +779,7 @@
     self.output_writer = output_writer
     self.retries = retries
     self.handler = handler
+    self._input_reader_json = self.input_reader.to_json()
 
   def reset_for_retry(self, output_writer):
     """Reset self for shard retry.
@@ -994,9 +793,20 @@
     self.output_writer = output_writer
     self.handler = self.mapreduce_spec.mapper.handler
 
-  def advance_for_next_slice(self):
-    """Advance relavent states for next slice."""
-    self.slice_id += 1
+  def advance_for_next_slice(self, recovery_slice=False):
+    """Advance relavent states for next slice.
+
+    Args:
+      recovery_slice: True if this slice is running recovery logic.
+        See handlers.MapperWorkerCallbackHandler._attempt_slice_recovery
+        for more info.
+    """
+    if recovery_slice:
+      self.slice_id += 2
+
+      self.input_reader = self.input_reader.from_json(self._input_reader_json)
+    else:
+      self.slice_id += 1
 
   def to_dict(self):
     """Convert state to dictionary to save in task payload."""
@@ -1020,11 +830,11 @@
     mapreduce_spec = MapreduceSpec.from_json_str(request.get("mapreduce_spec"))
     mapper_spec = mapreduce_spec.mapper
     input_reader_spec_dict = simplejson.loads(request.get("input_reader_state"),
-                                              cls=JsonDecoder)
+                                              cls=json_util.JsonDecoder)
     input_reader = mapper_spec.input_reader_class().from_json(
         input_reader_spec_dict)
     initial_input_reader_spec_dict = simplejson.loads(
-        request.get("initial_input_reader_state"), cls=JsonDecoder)
+        request.get("initial_input_reader_state"), cls=json_util.JsonDecoder)
     initial_input_reader = mapper_spec.input_reader_class().from_json(
         initial_input_reader_spec_dict)
 
@@ -1032,7 +842,7 @@
     if mapper_spec.output_writer_class():
       output_writer = mapper_spec.output_writer_class().from_json(
           simplejson.loads(request.get("output_writer_state", "{}"),
-                           cls=JsonDecoder))
+                           cls=json_util.JsonDecoder))
       assert isinstance(output_writer, mapper_spec.output_writer_class()), (
           "%s.from_json returned an instance of wrong class: %s" % (
               mapper_spec.output_writer_class(),
@@ -1042,10 +852,7 @@
     if not handler:
       handler = mapreduce_spec.mapper.handler
 
-    request_path = request.path
-    base_path = request_path[:request_path.rfind("/")]
-
-    return cls(base_path,
+    return cls(mapreduce_spec.params["base_path"],
                mapreduce_spec,
                str(request.get("shard_id")),
                int(request.get("slice_id")),
@@ -1062,7 +869,16 @@
   The shard state is stored in the datastore and is later aggregated by
   controller task. ShardState key_name is equal to shard_id.
 
-  Properties:
+  Shard state contains critical state to ensure the correctness of
+  shard execution. It is the single source of truth about a shard's
+  progress. For example:
+  1. A slice is allowed to run only if its payload matches shard state's
+     expectation.
+  2. A slice is considered running only if it has acquired the shard's lock.
+  3. A slice is considered done only if it has successfully committed shard
+     state to db.
+
+  Properties about the shard:
     active: if we have this shard still running as boolean.
     counters_map: shard's counters map as CountersMap. All counters yielded
       within mapreduce are stored here.
@@ -1074,18 +890,21 @@
     update_time: The last time this shard state was updated.
     shard_description: A string description of the work this shard will do.
     last_work_item: A string description of the last work item processed.
-    writer_state: writer state for this shard. This is filled when a job
-      has one output per shard by MR worker after finalizing output files.
-    slice_id: slice id of current executing slice. A task
+    writer_state: writer state for this shard. The shard's output writer
+      instance can save in-memory output references to this field in its
+      "finalize" method.
+
+   Properties about slice management:
+    slice_id: slice id of current executing slice. A slice's task
       will not run unless its slice_id matches this. Initial
       value is 0. By the end of slice execution, this number is
       incremented by 1.
     slice_start_time: a slice updates this to now at the beginning of
-      execution transactionally. If transaction succeeds, the current task holds
+      execution. If the transaction succeeds, the current task holds
       a lease of slice duration + some grace period. During this time, no
       other task with the same slice_id will execute. Upon slice failure,
       the task should try to unset this value to allow retries to carry on
-      ASAP. slice_start_time is only meaningful when slice_id is the same.
+      ASAP.
     slice_request_id: the request id that holds/held the lease. When lease has
       expired, new request needs to verify that said request has indeed
       ended according to logs API. Do this only when lease has expired
@@ -1095,15 +914,11 @@
       proceed after a long conservative timeout.
     slice_retries: the number of times a slice has been retried due to
       processing data when lock is held. Taskqueue/datastore errors
-      related to shard management are not counted. This count is
+      related to slice/shard management are not counted. This count is
       only a lower bound and is used to determined when to fail a slice
       completely.
     acquired_once: whether the lock for this slice has been acquired at
       least once. When this is True, duplicates in outputs are possible.
-      This is very different from when slice_retries is 0, e.g. when
-      outputs have been written but a taskqueue problem prevents a slice
-      to continue, acquired_once would be True but slice_retries would be
-      0.
   """
 
   RESULT_SUCCESS = "success"
@@ -1118,11 +933,13 @@
   _MAX_STATES_IN_MEMORY = 10
 
 
+  mapreduce_id = db.StringProperty(required=True)
   active = db.BooleanProperty(default=True, indexed=False)
-  counters_map = JsonProperty(CountersMap, default=CountersMap(), indexed=False)
+  counters_map = json_util.JsonProperty(
+      CountersMap, default=CountersMap(), indexed=False)
   result_status = db.StringProperty(choices=_RESULTS, indexed=False)
   retries = db.IntegerProperty(default=0, indexed=False)
-  writer_state = JsonProperty(dict, indexed=False)
+  writer_state = json_util.JsonProperty(dict, indexed=False)
   slice_id = db.IntegerProperty(default=0, indexed=False)
   slice_start_time = db.DateTimeProperty(indexed=False)
   slice_request_id = db.ByteStringProperty(indexed=False)
@@ -1130,7 +947,6 @@
   acquired_once = db.BooleanProperty(default=False, indexed=False)
 
 
-  mapreduce_id = db.StringProperty(required=True)
   update_time = db.DateTimeProperty(auto_now=True, indexed=False)
   shard_description = db.TextProperty(default="")
   last_work_item = db.TextProperty(default="")
@@ -1174,13 +990,22 @@
     self.slice_retries = 0
     self.acquired_once = False
 
-  def advance_for_next_slice(self):
-    """Advance self for next slice."""
-    self.slice_id += 1
+  def advance_for_next_slice(self, recovery_slice=False):
+    """Advance self for next slice.
+
+    Args:
+      recovery_slice: True if this slice is running recovery logic.
+        See handlers.MapperWorkerCallbackHandler._attempt_slice_recovery
+        for more info.
+    """
     self.slice_start_time = None
     self.slice_request_id = None
     self.slice_retries = 0
     self.acquired_once = False
+    if recovery_slice:
+      self.slice_id += 2
+    else:
+      self.slice_id += 1
 
   def set_for_failure(self):
     self.active = False
diff --git a/google/appengine/ext/mapreduce/output_writers.py b/google/appengine/ext/mapreduce/output_writers.py
index d6f98c7..708e424 100644
--- a/google/appengine/ext/mapreduce/output_writers.py
+++ b/google/appengine/ext/mapreduce/output_writers.py
@@ -64,6 +64,7 @@
 from google.appengine.api.files import file_service_pb
 from google.appengine.ext.mapreduce import context
 from google.appengine.ext.mapreduce import errors
+from google.appengine.ext.mapreduce import json_util
 from google.appengine.ext.mapreduce import model
 from google.appengine.ext.mapreduce import operation
 from google.appengine.ext.mapreduce import records
@@ -75,6 +76,9 @@
   from google.appengine.ext import cloudstorage
   if hasattr(cloudstorage, "_STUB"):
     cloudstorage = None
+
+  if cloudstorage:
+    from google.appengine.ext.cloudstorage import cloudstorage_api
 except ImportError:
   pass
 
@@ -86,7 +90,7 @@
 COUNTER_IO_WRITE_MSEC = "io-write-msec"
 
 
-class OutputWriter(model.JsonMixin):
+class OutputWriter(json_util.JsonMixin):
   """Abstract base class for output writers.
 
   Output writers process all mapper handler output, which is not
@@ -176,15 +180,18 @@
                               self.__class__)
 
   @classmethod
-  def create(cls, mapreduce_state, shard_state):
+  def create(cls, mr_spec, shard_number, shard_attempt, _writer_state=None):
     """Create new writer for a shard.
 
     Args:
-      mapreduce_state: an instance of model.MapreduceState describing current
-      job. State can NOT be modified.
-      shard_state: shard state can NOT be modified. Output file state should
-      be contained in the output writer instance. The serialized output writer
-      instance will be saved by mapreduce across slices.
+      mr_spec: an instance of model.MapreduceSpec describing current job.
+      shard_number: int shard number.
+      shard_attempt: int shard attempt.
+      _writer_state: deprecated. This is for old writers that share file
+        across shards. For new writers, each shard must have its own
+        dedicated outputs. Output state should be contained in
+        the output writer instance. The serialized output writer
+        instance will be saved by mapreduce across slices.
     """
     raise NotImplementedError("create() not implemented in %s" % cls)
 
@@ -231,7 +238,7 @@
     raise NotImplementedError("get_filenames() not implemented in %s" % cls)
 
 
-  def _can_be_retried(self, tstate):
+  def _supports_shard_retry(self, tstate):
     """Whether this output writer instance supports shard retry.
 
     Args:
@@ -242,6 +249,40 @@
     """
     return False
 
+  def _supports_slice_recovery(self, mapper_spec):
+    """Whether this output writer supports slice recovery.
+
+    Args:
+      mapper_spec: instance of model.MapperSpec.
+    """
+    return False
+
+
+  def _recover(self, mr_spec, shard_number, shard_attempt):
+    """Create a new output writer instance from the old one.
+
+    This method is called when _supports_slice_recovery returns True,
+    and when there is a chance the old output writer instance is out of sync
+    with its storage medium due to a retry of a slice. _recover should
+    create a new instance based on the old one. When finalize is called
+    on the new instance, it could combine valid outputs from all instances
+    to generate the final output. How the new instance maintains references
+    to previous outputs is up to implementation.
+
+    Any exception during recovery is subject to normal slice/shard retry.
+    So recovery logic must be idempotent.
+
+    Args:
+      mr_spec: an instance of model.MapreduceSpec describing current job.
+      shard_number: int shard number.
+      shard_attempt: int shard attempt.
+
+    Returns:
+      a new instance of output writer.
+    """
+    raise NotImplementedError()
+
+
 
 _FILES_API_FLUSH_SIZE = 128*1024
 
@@ -632,7 +673,7 @@
     return {"filename": self._filename,
             "request_filename": self._request_filename}
 
-  def _can_be_retried(self, tstate):
+  def _supports_shard_retry(self, tstate):
     """Inherit doc.
 
     Only shard with output per shard can be retried.
@@ -644,33 +685,21 @@
     return False
 
   @classmethod
-  def create(cls, mapreduce_state, shard_state):
-    """Create new writer for a shard.
-
-    Args:
-      mapreduce_state: an instance of model.MapreduceState describing current
-        job.
-      shard_state: an instance of mode.ShardState describing the shard
-        outputing this file.
-
-    Returns:
-      an output writer instance for this shard.
-    """
-    output_sharding = cls._get_output_sharding(mapreduce_state=mapreduce_state)
-    shard_number = shard_state.shard_number
+  def create(cls, mr_spec, shard_number, shard_attempt, _writer_state=None):
+    """Inherit docs."""
+    mapper_spec = mr_spec.mapper
+    output_sharding = cls._get_output_sharding(mapper_spec=mapper_spec)
     if output_sharding == cls.OUTPUT_SHARDING_INPUT_SHARDS:
-      mapper_spec = mapreduce_state.mapreduce_spec.mapper
       params = _get_params(mapper_spec)
       mime_type = params.get("mime_type", "application/octet-stream")
       filesystem = cls._get_filesystem(mapper_spec=mapper_spec)
       bucket = params.get(cls.GS_BUCKET_NAME_PARAM)
       acl = params.get(cls.GS_ACL_PARAM)
-      retries = shard_state.retries
 
       request_filename = (
-          mapreduce_state.mapreduce_spec.name + "-" +
-          mapreduce_state.mapreduce_spec.mapreduce_id + "-output-" +
-          str(shard_number) + "-retry-" + str(retries))
+          mr_spec.name + "-" +
+          mr_spec.mapreduce_id + "-output-" +
+          str(shard_number) + "-attempt-" + str(shard_attempt))
       if bucket is not None:
         request_filename = "%s/%s" % (bucket, request_filename)
       filename = cls._create_file(filesystem,
@@ -678,7 +707,7 @@
                                   mime_type,
                                   acl=acl)
     else:
-      state = cls._State.from_json(mapreduce_state.writer_state)
+      state = cls._State.from_json(_writer_state)
       filename = state.filenames[0]
       request_filename = state.request_filenames[0]
     return cls(filename, request_filename)
@@ -846,11 +875,16 @@
         $name - the name of the job
         $id - the id assigned to the job
         $num - the shard number
-        $retry - the retry count for this shard
       If there is more than one shard $num must be used. An arbitrary suffix may
       be applied by the writer.
     CONTENT_TYPE_PARAM: mime type to apply on the files. If not provided, Google
       Cloud Storage will apply its default.
+    _NO_DUPLICATE: if True, slice recovery logic will be used to ensure
+      output files has no duplicates. Every shard should have only one final
+      output in user specified location. But it may produce many smaller
+      files (named "seg") due to slice recovery. These segs live in a
+      tmp directory and should be combined and renamed to the final location.
+      In current impl, they are not combined.
   """
 
 
@@ -858,38 +892,60 @@
   ACL_PARAM = "acl"
   NAMING_FORMAT_PARAM = "naming_format"
   CONTENT_TYPE_PARAM = "content_type"
+  _NO_DUPLICATE = "no_duplicate"
 
 
-  DEFAULT_NAMING_FORMAT = "$name-$id-output-$num-retry-$retry"
+  DEFAULT_NAMING_FORMAT = "$name/$id/output-$num"
 
 
+  _MR_TMP = "gae_mr_tmp"
+  _TMP_FILE_NAMING_FORMAT = (
+      _MR_TMP + "/$name/$id/attempt-$attempt/output-$num/seg-$seg")
   _ACCOUNT_ID_PARAM = "account_id"
-  _JSON_FILENAME = "filename"
+  _SEG_PREFIX = "seg_prefix"
+  _LAST_SEG_INDEX = "last_seg_index"
   _JSON_GCS_BUFFER = "buffer"
+  _JSON_SEG_INDEX = "seg_index"
+  _JSON_NO_DUP = "no_dup"
+
+  _VALID_LENGTH = "x-goog-meta-gae-mr-valid-length"
 
 
-  def __init__(self, streaming_buffer, filename, writer_spec=None):
+  def __init__(self, streaming_buffer, writer_spec=None):
     """Initialize a GoogleCloudStorageOutputWriter instance.
 
     Args:
       streaming_buffer: an instance of writable buffer from cloudstorage_api.
-      filename: the GCS client filename this writer is writing to.
-      writer_spec: the specification for the writer, useful for subclasses.
+
+      writer_spec: the specification for the writer.
     """
     self._streaming_buffer = streaming_buffer
-    self._filename = filename
+    self._no_dup = False
+    if writer_spec:
+      self._no_dup = writer_spec.get(self._NO_DUPLICATE, False)
+    if self._no_dup:
+
+
+
+      self._seg_index = int(streaming_buffer.name.rsplit("-", 1)[1])
+
+
+
+
+      self._seg_valid_length = 0
 
   @classmethod
   def _generate_filename(cls, writer_spec, name, job_id, num,
-                         retry):
-    """Generates a filename for a shard / retry count.
+                         attempt=None, seg_index=None):
+    """Generates a filename for a particular output.
 
     Args:
       writer_spec: specification dictionary for the output writer.
       name: name of the job.
       job_id: the ID number assigned to the job.
       num: shard number.
-      retry: the retry number.
+      attempt: the shard attempt number.
+      seg_index: index of the seg. None means the final output.
 
     Returns:
       a string containing the filename.
@@ -898,12 +954,20 @@
       BadWriterParamsError if the template contains any errors such as invalid
         syntax or contains unknown substitution placeholders.
     """
-    naming_format = writer_spec.get(cls.NAMING_FORMAT_PARAM,
-                                    cls.DEFAULT_NAMING_FORMAT)
+    naming_format = cls._TMP_FILE_NAMING_FORMAT
+    if seg_index is None:
+      naming_format = writer_spec.get(cls.NAMING_FORMAT_PARAM,
+                                      cls.DEFAULT_NAMING_FORMAT)
+
     template = string.Template(naming_format)
     try:
 
-      return template.substitute(name=name, id=job_id, num=num, retry=retry)
+      if seg_index is None:
+        return template.substitute(name=name, id=job_id, num=num)
+      else:
+        return template.substitute(name=name, id=job_id, num=num,
+                                   attempt=attempt,
+                                   seg=seg_index)
     except ValueError, error:
       raise errors.BadWriterParamsError("Naming template is bad, %s" % (error))
     except KeyError, error:
@@ -934,31 +998,32 @@
     except ValueError, error:
       raise errors.BadWriterParamsError("Bad bucket name, %s" % (error))
 
+    if writer_spec.get(cls._NO_DUPLICATE, False) not in (True, False):
+      raise errors.BadWriterParamsError("No duplicate must a boolean.")
 
-    cls._generate_filename(writer_spec, "name", "id", 0, 0)
+
+    cls._generate_filename(writer_spec, "name", "id", 0)
+    cls._generate_filename(writer_spec, "name", "id", 0, 1, 0)
 
   @classmethod
-  def create(cls, mapreduce_state, shard_state):
-    """Create new writer for a shard.
-
-    Args:
-      mapreduce_state: an instance of model.MapreduceState describing current
-        job. State can NOT be modified.
-      shard_state: an instance of model.ShardState.
-
-    Returns:
-      an output writer for the requested shard.
-    """
-
-    job_spec = mapreduce_state.mapreduce_spec
-    writer_spec = _get_params(job_spec.mapper, allow_old=False)
+  def create(cls, mr_spec, shard_number, shard_attempt, _writer_state=None):
+    """Inherit docs."""
+    writer_spec = _get_params(mr_spec.mapper, allow_old=False)
+    seg_index = None
+    if writer_spec.get(cls._NO_DUPLICATE, False):
+      seg_index = 0
 
 
-    key = cls._generate_filename(writer_spec, job_spec.name,
-                                 job_spec.mapreduce_id,
-                                 shard_state.shard_number, shard_state.retries)
+    key = cls._generate_filename(writer_spec, mr_spec.name,
+                                 mr_spec.mapreduce_id,
+                                 shard_number, shard_attempt,
+                                 seg_index)
+    return cls._create(writer_spec, key)
 
-    filename = "/%s/%s" % (writer_spec[cls.BUCKET_NAME_PARAM], key)
+  @classmethod
+  def _create(cls, writer_spec, filename_suffix):
+
+    filename = "/%s/%s" % (writer_spec[cls.BUCKET_NAME_PARAM], filename_suffix)
 
     content_type = writer_spec.get(cls.CONTENT_TYPE_PARAM, None)
 
@@ -973,7 +1038,7 @@
                                options=options,
                                _account_id=account_id)
 
-    return cls(writer, filename, writer_spec=writer_spec)
+    return cls(writer, writer_spec=writer_spec)
 
   @classmethod
   def _get_filename(cls, shard_state):
@@ -989,12 +1054,27 @@
 
   @classmethod
   def from_json(cls, state):
-    return cls(pickle.loads(state[cls._JSON_GCS_BUFFER]),
-               state[cls._JSON_FILENAME])
+    writer = cls(pickle.loads(state[cls._JSON_GCS_BUFFER]))
+    no_dup = state.get(cls._JSON_NO_DUP, False)
+    writer._no_dup = no_dup
+    if no_dup:
+      writer._seg_valid_length = state[cls._VALID_LENGTH]
+      writer._seg_index = state[cls._JSON_SEG_INDEX]
+    return writer
 
   def to_json(self):
-    return {self._JSON_GCS_BUFFER: pickle.dumps(self._streaming_buffer),
-            self._JSON_FILENAME: self._filename}
+    result = {self._JSON_GCS_BUFFER: pickle.dumps(self._streaming_buffer),
+              self._JSON_NO_DUP: self._no_dup}
+    if self._no_dup:
+      result.update({
+
+
+
+
+
+          self._VALID_LENGTH: self._streaming_buffer.tell(),
+          self._JSON_SEG_INDEX: self._seg_index})
+    return result
 
   def write(self, data):
     """Write data to the GoogleCloudStorage file.
@@ -1012,12 +1092,74 @@
   def finalize(self, ctx, shard_state):
     self._streaming_buffer.close()
 
-    shard_state.writer_state = {"filename": self._filename}
+    if self._no_dup:
+      cloudstorage_api._copy2(
+          self._streaming_buffer.name,
+          self._streaming_buffer.name,
+          metadata={self._VALID_LENGTH: self._streaming_buffer.tell()})
 
 
-  def _can_be_retried(self, tstate):
+      mr_spec = ctx.mapreduce_spec
+      writer_spec = _get_params(mr_spec.mapper, allow_old=False)
+      filename = self._generate_filename(writer_spec,
+                                         mr_spec.name,
+                                         mr_spec.mapreduce_id,
+                                         shard_state.shard_number)
+      seg_filename = self._streaming_buffer.name
+      prefix, last_index = seg_filename.rsplit("-", 1)
+
+
+
+      shard_state.writer_state = {self._SEG_PREFIX: prefix + "-",
+                                  self._LAST_SEG_INDEX: int(last_index),
+                                  "filename": filename}
+    else:
+      shard_state.writer_state = {"filename": self._streaming_buffer.name}
+
+
+  def _supports_shard_retry(self, tstate):
     return True
 
+  def _supports_slice_recovery(self, mapper_spec):
+    writer_spec = _get_params(mapper_spec, allow_old=False)
+    return writer_spec.get(self._NO_DUPLICATE, False)
+
+  def _recover(self, mr_spec, shard_number, shard_attempt):
+    next_seg_index = self._seg_index
+
+
+
+
+    if self._seg_valid_length != 0:
+      try:
+        gcs_next_offset = self._streaming_buffer._get_offset_from_gcs() + 1
+
+        if gcs_next_offset > self._streaming_buffer.tell():
+          self._streaming_buffer._force_close(gcs_next_offset)
+
+        else:
+          self._streaming_buffer.close()
+      except cloudstorage.FileClosedError:
+        pass
+      cloudstorage_api._copy2(
+          self._streaming_buffer.name,
+          self._streaming_buffer.name,
+          metadata={self._VALID_LENGTH:
+                    self._seg_valid_length})
+      next_seg_index = self._seg_index + 1
+
+    writer_spec = _get_params(mr_spec.mapper, allow_old=False)
+
+    key = self._generate_filename(
+        writer_spec, mr_spec.name,
+        mr_spec.mapreduce_id,
+        shard_number,
+        shard_attempt,
+        next_seg_index)
+    new_writer = self._create(writer_spec, key)
+    new_writer._seg_index = next_seg_index
+    return new_writer
+
 
 class _GoogleCloudStorageRecordOutputWriter(_GoogleCloudStorageOutputWriter):
   """Write data to the Google Cloud Storage file using LevelDB format.
@@ -1031,17 +1173,15 @@
 
   def __init__(self,
                streaming_buffer,
-               filename,
                writer_spec=None):
     """Initialize a CloudStorageOutputWriter instance.
 
     Args:
       streaming_buffer: an instance of writable buffer from cloudstorage_api.
-      filename: the GCS client filename this writer is writing to.
       writer_spec: the specification for the writer.
     """
     super(_GoogleCloudStorageRecordOutputWriter, self).__init__(
-        streaming_buffer, filename, writer_spec)
+        streaming_buffer, writer_spec)
     self._record_writer = records.RecordsWriter(
         super(_GoogleCloudStorageRecordOutputWriter, self))
 
@@ -1058,4 +1198,3 @@
       data: string containing the data to be written.
     """
     self._record_writer.write(data)
-
diff --git a/google/appengine/ext/mapreduce/shuffler.py b/google/appengine/ext/mapreduce/shuffler.py
index deccfe3..7c21ef4 100644
--- a/google/appengine/ext/mapreduce/shuffler.py
+++ b/google/appengine/ext/mapreduce/shuffler.py
@@ -460,15 +460,9 @@
     return {"filenames": self._filenames}
 
   @classmethod
-  def create(cls, mapreduce_state, shard_state):
-    """Create new writer for a shard.
-
-    Args:
-      mapreduce_state: an instance of model.MapreduceState describing current
-      job. State can be modified.
-      shard_state: shard state.
-    """
-    return cls(mapreduce_state.writer_state["filenames"])
+  def create(cls, mr_spec, shard_number, shard_attempt, _writer_state=None):
+    """Inherit docs."""
+    return cls(_writer_state["filenames"])
 
   @classmethod
   def get_filenames(cls, mapreduce_state):
diff --git a/google/appengine/ext/mapreduce/status.py b/google/appengine/ext/mapreduce/status.py
index 6a7cf54..5492055 100644
--- a/google/appengine/ext/mapreduce/status.py
+++ b/google/appengine/ext/mapreduce/status.py
@@ -43,6 +43,7 @@
 from google.appengine.api import yaml_listener
 from google.appengine.api import yaml_object
 from google.appengine.ext import db
+from google.appengine.ext import webapp
 from google.appengine.ext.mapreduce import base_handler
 from google.appengine.ext.mapreduce import errors
 from google.appengine.ext.mapreduce import model
@@ -274,7 +275,7 @@
     mr_yaml_file.close()
 
 
-class ResourceHandler(base_handler.BaseHandler):
+class ResourceHandler(webapp.RequestHandler):
   """Handler for static resources."""
 
   _RESOURCE_MAP = {
diff --git a/google/appengine/ext/mapreduce/test_support.py b/google/appengine/ext/mapreduce/test_support.py
index 6117aa9..9429431 100644
--- a/google/appengine/ext/mapreduce/test_support.py
+++ b/google/appengine/ext/mapreduce/test_support.py
@@ -34,13 +34,14 @@
 
 
 import base64
-import cgi
 import collections
 import logging
+import traceback
 import os
 import re
 
 from google.appengine.ext.mapreduce import main
+from google.appengine.ext.mapreduce import model
 from google.appengine.ext.webapp import mock_webapp
 
 
@@ -63,13 +64,8 @@
     return {}
 
   body = base64.b64decode(task["body"])
-  result = {}
-  for (name, value) in cgi.parse_qs(body).items():
-    if len(value) == 1:
-      result[name] = value[0]
-    else:
-      result[name] = value
-  return result
+
+  return model.HugeTask._decode_payload(body)
 
 
 def execute_task(task, retries=0, handlers_map=None):
@@ -194,6 +190,8 @@
   taskqueue.FlushQueue(queue)
   task_run_counts = collections.defaultdict(lambda: 0)
   for task in tasks:
+    import logging
+    logging.error(task)
     retries = 0
     while True:
       try:
@@ -212,6 +210,8 @@
             "Task %s is being retried for the %s time",
             task["name"],
             retries)
+        logging.debug(traceback.format_exc())
+
   return task_run_counts
 
 
diff --git a/google/appengine/ext/mapreduce/tools/gcs_file_seg_reader.py b/google/appengine/ext/mapreduce/tools/gcs_file_seg_reader.py
new file mode 100644
index 0000000..e59d110
--- /dev/null
+++ b/google/appengine/ext/mapreduce/tools/gcs_file_seg_reader.py
@@ -0,0 +1,134 @@
+#!/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.
+#
+"""A simple reader for file segs produced by GCS output writer."""
+
+from google.appengine.ext.mapreduce import output_writers
+
+
+
+
+
+
+try:
+
+  from google.appengine.ext import cloudstorage
+  if hasattr(cloudstorage, "_STUB"):
+    cloudstorage = None
+except ImportError:
+  pass
+
+
+class _GCSFileSegReader(object):
+  """A simple reader for file segs produced by GCS output writer.
+
+  Internal use only.
+
+  This reader conforms to Python stream interface.
+  """
+
+  def __init__(self, seg_prefix, last_seg_index):
+    """Init.
+
+    Instances are pickle safe.
+
+    Args:
+      seg_prefix: filename prefix for all segs. It is expected
+        seg_prefix + index = seg filename.
+      last_seg_index: the last index of all segs. int.
+    """
+    self._EOF = False
+    self._offset = 0
+
+
+    self._seg_prefix = seg_prefix
+    self._last_seg_index = last_seg_index
+    self._seg_index = -1
+    self._seg_valid_length = None
+    self._seg = None
+    self._next_seg()
+
+  def read(self, n):
+    """Read data from file segs.
+
+    Args:
+      n: max bytes to read. Must be positive.
+
+    Returns:
+      some bytes. May be smaller than n bytes. "" when no more data is left.
+    """
+    if self._EOF:
+      return ""
+
+    while self._seg_index <= self._last_seg_index:
+      result = self._read_from_seg(n)
+      if result != "":
+        return result
+      else:
+        self._next_seg()
+
+    self._EOF = True
+    return ""
+
+  def close(self):
+    if self._seg:
+      self._seg.close()
+
+  def tell(self):
+    """Returns the next offset to read."""
+    return self._offset
+
+  def _next_seg(self):
+    """Get next seg."""
+    if self._seg:
+      self._seg.close()
+    self._seg_index += 1
+    if self._seg_index > self._last_seg_index:
+      self._seg = None
+      return
+
+    filename = self._seg_prefix + str(self._seg_index)
+    stat = cloudstorage.stat(filename)
+    writer = output_writers._GoogleCloudStorageOutputWriter
+    if writer._VALID_LENGTH not in stat.metadata:
+      raise ValueError(
+          "Expect %s in metadata for file %s." %
+          (writer._VALID_LENGTH, filename))
+    self._seg_valid_length = int(stat.metadata[writer._VALID_LENGTH])
+    if self._seg_valid_length > stat.st_size:
+      raise ValueError(
+          "Valid length %s is too big for file %s of length %s" %
+          (self._seg_valid_length, filename, stat.st_size))
+    self._seg = cloudstorage.open(filename)
+
+  def _read_from_seg(self, n):
+    """Read from current seg.
+
+    Args:
+      n: max number of bytes to read.
+
+    Returns:
+      valid bytes from the current seg. "" if no more is left.
+    """
+    result = self._seg.read(size=n)
+    if result == "":
+      return result
+    offset = self._seg.tell()
+    if offset > self._seg_valid_length:
+      extra = offset - self._seg_valid_length
+      result = result[:-1*extra]
+    self._offset += len(result)
+    return result
diff --git a/google/appengine/ext/ndb/model.py b/google/appengine/ext/ndb/model.py
index a4ff27b..18ce978 100644
--- a/google/appengine/ext/ndb/model.py
+++ b/google/appengine/ext/ndb/model.py
@@ -280,6 +280,7 @@
 
 __author__ = 'guido@google.com (Guido van Rossum)'
 
+import collections
 import copy
 import cPickle as pickle
 import datetime
@@ -381,6 +382,83 @@
     return not eq
 
 
+class _NestedCounter(object):
+  """ A recursive counter for StructuredProperty deserialization.
+
+  Deserialization has some complicated rules to handle StructuredPropertys
+  that may or may not be empty. The simplest case is a leaf counter, where
+  the counter will return the index of the repeated value that last had this
+  leaf property written. When a non-leaf counter requested, this will return
+  the max of all its leaf values. This is due to the fact that the next index
+  that a full non-leaf property may be written to comes after all indices that
+  have part of that property written (otherwise, a partial entity would be
+  overwritten.
+
+  Consider an evaluation of the following structure:
+  -A
+    -B
+      -D
+      -E
+    -C
+  With the properties being deserialized in the order:
+
+  1) a.b.d = z
+  2) a.c = y
+  3) a.b = None
+  4) a = None
+  5) a.b.e = x
+  6) a.b.d = w
+
+  The counter state should be the following:
+     a | a.b | a.b.d | a.b.e | a.c
+  0) 0    0      0       0      0
+  1) 1    1      1       0      0
+  2)@1   @1      1       0      1
+  3)@1*  @1*     1       0      1
+  4)@1*  @1*     1       0      1
+  5)@1*  @1*     1       1      1
+  6)@3   @3      3       1      1
+
+  Here, @ indicates that this counter value is actually a calculated value.
+  It is equal to the MAX of its sub-counters.
+
+  Note that in the * cases, our counters actually fall behind. We cannot
+  increase the counters when this happens because child properties have
+  not yet been fully populated. In theses cases, we'll have to do a series
+  of increments to catch up the counters following None deserializations.
+  """
+
+  def __init__(self):
+    self.__counter = 0
+    self.__sub_counters = collections.defaultdict(_NestedCounter)
+
+  def get(self, parts=None):
+    if parts:
+      return self.__sub_counters[parts[0]].get(parts[1:])
+    if self.__is_parent_node():
+      return max(v.get() for v in self.__sub_counters.itervalues())
+    return self.__counter
+
+  def increment(self, parts=None):
+    if parts:
+      self.__make_parent_node()
+      return self.__sub_counters[parts[0]].increment(parts[1:])
+    if self.__is_parent_node():
+      return -1
+    self.__counter += 1
+    return self.__counter
+
+  def _absolute_counter(self):
+    # Used only for testing.
+    return self.__counter
+
+  def __is_parent_node(self):
+    return self.__counter == -1
+
+  def __make_parent_node(self):
+    self.__counter = -1
+
+
 class IndexProperty(_NotEqualMixin):
   """Immutable object representing a single property in an index."""
 
@@ -2289,22 +2367,37 @@
       prop._code_name = next
       prop_is_fake = True
 
-    values = self._get_base_value_unwrapped_as_list(entity)
     # Find the first subentity that doesn't have a value for this
     # property yet.
-    for sub in values:
-      if not isinstance(sub, self._modelclass):
-        raise TypeError('sub-entities must be instances of their Model class.')
-      if not prop._has_value(sub, rest):
-        subentity = sub
-        break
-    else:
+    if not hasattr(entity, '_subentity_counter'):
+      entity._subentity_counter = _NestedCounter()
+    counter = entity._subentity_counter
+    counter_path = parts[depth - 1:]
+    next_index = counter.get(counter_path)
+    subentity = None
+    if self._has_value(entity):
+      # If an entire subentity has been set to None, we have to loop
+      # to advance until we find the next partial entity.
+      while next_index < self._get_value_size(entity):
+        subentity = self._get_base_value_at_index(entity, next_index)
+        if not isinstance(subentity, self._modelclass):
+          raise TypeError('sub-entities must be instances '
+                          'of their Model class.')
+        if not prop._has_value(subentity, rest):
+          break
+        next_index = counter.increment(counter_path)
+      else:
+        subentity = None
+    # The current property is going to be populated, so advance the counter.
+    counter.increment(counter_path)
+    if not subentity:
       # We didn't find one.  Add a new one to the underlying list of
-      # values (the list returned by
-      # _get_base_value_unwrapped_as_list() is a copy so we
-      # can't append to it).
+      # values.
       subentity = self._modelclass()
-      values = self._retrieve_value(entity)
+      values = self._retrieve_value(entity, self._default)
+      if values is None:
+        self._store_value(entity, [])
+        values = self._retrieve_value(entity, self._default)
       values.append(_BaseValue(subentity))
     if prop_is_fake:
       # Add the synthetic property to the subentity's _properties
@@ -2332,6 +2425,17 @@
         'Structured property %s requires a subproperty' % self._name)
     self._modelclass._check_properties([rest], require_indexed=require_indexed)
 
+  def _get_base_value_at_index(self, entity, index):
+    assert self._repeated
+    value = self._retrieve_value(entity, self._default)
+    value[index] = self._opt_call_to_base_type(value[index])
+    return value[index].b_val
+
+  def _get_value_size(self, entity):
+    values = self._retrieve_value(entity, self._default)
+    if values is None:
+      return 0
+    return len(values)
 
 class LocalStructuredProperty(_StructuredGetForDictMixin, BlobProperty):
   """Substructure that is serialized to an opaque blob.
@@ -2394,7 +2498,8 @@
     if value is not None:
       if self._repeated:
         for subent in value:
-          subent._prepare_for_put()
+          if subent is not None:
+            subent._prepare_for_put()
       else:
         value._prepare_for_put()
 
@@ -2597,7 +2702,8 @@
   ...   hash = ComputedProperty(_compute_hash, name='sha1')
   """
 
-  def __init__(self, func, name=None, indexed=None, repeated=None):
+  def __init__(self, func, name=None, indexed=None,
+               repeated=None, verbose_name=None):
     """Constructor.
 
     Args:
@@ -2605,7 +2711,8 @@
             a calculated value.
     """
     super(ComputedProperty, self).__init__(name=name, indexed=indexed,
-                                           repeated=repeated)
+                                           repeated=repeated,
+                                           verbose_name=verbose_name)
     self._func = func
 
   def _set_value(self, entity, value):
diff --git a/google/appengine/ext/ndb/utils.py b/google/appengine/ext/ndb/utils.py
index 3df332c..6c7f8a8 100644
--- a/google/appengine/ext/ndb/utils.py
+++ b/google/appengine/ext/ndb/utils.py
@@ -3,6 +3,7 @@
 These are not meant for use by code outside NDB.
 """
 
+import functools
 import logging
 import os
 import sys
@@ -24,13 +25,18 @@
   # A decorator to decorate a decorator's wrapper.  Following the lead
   # of Twisted and Monocle, this is supposed to make debugging heavily
   # decorated code easier.  We'll see...
-  # TODO: Evaluate; so far it hasn't helped, and it has hurt some.
+  # TODO(pcostello): This copies the functionality of functools.wraps
+  # following the patch in http://bugs.python.org/issue3445. We can replace
+  # this once upgrading to python 3.3.
   def wrapping_wrapper(wrapper):
     try:
       wrapper.__wrapped__ = wrapped
       wrapper.__name__ = wrapped.__name__
       wrapper.__doc__ = wrapped.__doc__
       wrapper.__dict__.update(wrapped.__dict__)
+      # Local functions won't have __module__ attribute.
+      if hasattr(wrapped, '__module__'):
+        wrapper.__module__ = wrapped.__module__
     except Exception:
       pass
     return wrapper
diff --git a/google/appengine/ext/webapp/util.py b/google/appengine/ext/webapp/util.py
index 54c134b..7f9d21f 100644
--- a/google/appengine/ext/webapp/util.py
+++ b/google/appengine/ext/webapp/util.py
@@ -114,9 +114,13 @@
   env["wsgi.multithread"] = False
   env["wsgi.multiprocess"] = False
   result = application(env, _start_response)
-  if result is not None:
-    for data in result:
-      sys.stdout.write(data)
+  try:
+    if result is not None:
+      for data in result:
+        sys.stdout.write(data)
+  finally:
+    if hasattr(result, 'close'):
+      result.close()
 
 
 def _start_response(status, headers, exc_info=None):
diff --git a/google/appengine/tools/api_server.py b/google/appengine/tools/api_server.py
index 11ecf5d..1c42fce 100644
--- a/google/appengine/tools/api_server.py
+++ b/google/appengine/tools/api_server.py
@@ -234,6 +234,8 @@
 def _SetupStubs(
     app_id,
     application_root,
+    appidentity_email_address,
+    appidentity_private_key_path,
     trusted,
     blobstore_path,
     use_sqlite,
@@ -317,7 +319,9 @@
 
 
 
-  tmp_app_identity_stub = app_identity_stub.AppIdentityServiceStub()
+  tmp_app_identity_stub = app_identity_stub.AppIdentityServiceStub.Create(
+      email_address=appidentity_email_address,
+      private_key_path=appidentity_private_key_path)
   if default_gcs_bucket_name is not None:
     tmp_app_identity_stub.SetDefaultGcsBucketName(default_gcs_bucket_name)
   apiproxy_stub_map.apiproxy.RegisterStub(
@@ -481,6 +485,8 @@
                       action=boolean_action.BooleanAction,
                       const=True,
                       default=False)
+  parser.add_argument('--appidentity_email_address', default=None)
+  parser.add_argument('--appidentity_private_key_path', default=None)
   parser.add_argument('--application_root', default=None)
   parser.add_argument('--application_host', default='localhost')
   parser.add_argument('--application_port', default=None)
@@ -567,6 +573,8 @@
                port,
                app_id,
                script=None,
+               appidentity_email_address=None,
+               appidentity_private_key_path=None,
                application_host=None,
                application_port=None,
                application_root=None,
@@ -602,6 +610,8 @@
       script: The name of the script that should be used, along with the
           executable argument, to run the API Server e.g. "api_server.py".
           If None then the executable is run without a script argument.
+      appidentity_email_address: Email address for service account substitute.
+      appidentity_private_key_path: Private key for service account substitute.
       application_host: The name of the host where the development application
           server is running e.g. "localhost".
       application_port: The port where the application server is running e.g.
@@ -659,6 +669,8 @@
       self._args = [executable]
     self._BindArgument('--api_host', host)
     self._BindArgument('--api_port', port)
+    self._BindArgument('--appidentity_email_address', appidentity_email_address)
+    self._BindArgument('--appidentity_private_key_path', appidentity_private_key_path)
     self._BindArgument('--application_host', application_host)
     self._BindArgument('--application_port', application_port)
     self._BindArgument('--application_root', application_root)
@@ -854,6 +866,8 @@
   request_info._local_dispatcher = ApiServerDispatcher()
   _SetupStubs(app_id=args.application,
               application_root=args.application_root,
+              appidentity_email_address=args.appidentity_email_address,
+              appidentity_private_key_path=args.appidentity_private_key_path,
               trusted=args.trusted,
               blobstore_path=args.blobstore_path,
               datastore_path=args.datastore_path,
diff --git a/google/appengine/tools/app_engine_web_xml_parser.py b/google/appengine/tools/app_engine_web_xml_parser.py
index aaec181..d419974 100644
--- a/google/appengine/tools/app_engine_web_xml_parser.py
+++ b/google/appengine/tools/app_engine_web_xml_parser.py
@@ -278,8 +278,8 @@
   def ProcessCodeLockNode(self, node):
     self.app_engine_web_xml.codelock = xml_parser_utils.BooleanValue(node.text)
 
-  def ProcessUseVmNode(self, node):
-    self.app_engine_web_xml.use_vm = xml_parser_utils.BooleanValue(node.text)
+  def ProcessVmNode(self, node):
+    self.app_engine_web_xml.vm = xml_parser_utils.BooleanValue(node.text)
 
   def ProcessApiConfigNode(self, node):
     servlet = xml_parser_utils.GetAttribute(node, 'servlet-class').strip()
@@ -401,7 +401,7 @@
     self.threadsafe = False
     self.threadsafe_value_provided = False
     self.codelock = None
-    self.use_vm = False
+    self.vm = False
     self.api_config = None
     self.api_endpoint_ids = []
     self.pagespeed = None
diff --git a/google/appengine/tools/appcfg.py b/google/appengine/tools/appcfg.py
index cdecb18..29718b9 100644
--- a/google/appengine/tools/appcfg.py
+++ b/google/appengine/tools/appcfg.py
@@ -72,6 +72,7 @@
 from google.appengine.datastore import datastore_index
 from google.appengine.tools import appcfg_java
 from google.appengine.tools import appengine_rpc
+
 try:
 
 
@@ -82,6 +83,7 @@
 from google.appengine.tools import sdk_update_checker
 
 
+
 LIST_DELIMITER = '\n'
 TUPLE_DELIMITER = '|'
 BACKENDS_ACTION = 'backends'
@@ -117,7 +119,7 @@
 DAY = 24*3600
 SUNDAY = 6
 
-SUPPORTED_RUNTIMES = ('go', 'php', 'python', 'python27', 'java', 'java7')
+SUPPORTED_RUNTIMES = ('go', 'php', 'python', 'python27', 'java', 'java7', 'vm')
 
 
 
@@ -152,6 +154,9 @@
     'computeMetadata/v1beta1/instance/service-accounts/default')
 
 
+APP_YAML_FILENAME = 'app.yaml'
+
+
 class Error(Exception):
   pass
 
@@ -299,7 +304,8 @@
     Returns:
       True if the file should be considered a static resource based on its name.
     """
-    return ('__static__' + os.sep) in filename
+    static = '__static__' + os.sep
+    return static in filename
 
   @staticmethod
   def __GetAppReadableIfStaticFile(config, filename):
@@ -1911,8 +1917,6 @@
     def PrintRetryMessage(_, delay):
       StatusUpdate('Will check again in %s seconds.' % delay)
 
-    app_summary = None
-
     app_summary = self.Deploy()
 
 
@@ -2060,7 +2064,7 @@
     if message:
       StatusUpdate(message)
     if fatal:
-      raise CannotStartServingError(fatal)
+      raise CannotStartServingError(message or 'Unknown error.')
     return result['serving'], result
 
   @staticmethod
@@ -2136,30 +2140,7 @@
     path = ''
     try:
       self.resource_limits = GetResourceLimits(self.rpcserver, self.config)
-
-      StatusUpdate('Scanning files on local disk.')
-      num_files = 0
-      for path in paths:
-        file_handle = openfunc(path)
-        file_classification = FileClassification(self.config, path)
-        try:
-          file_length = GetFileLength(file_handle)
-          if file_classification.IsApplicationFile():
-            max_size = self.resource_limits['max_file_size']
-          else:
-            max_size = self.resource_limits['max_blob_size']
-          if file_length > max_size:
-            logging.error('Ignoring file \'%s\': Too long '
-                          '(max %d bytes, file is %d bytes)',
-                          path, max_size, file_length)
-          else:
-            logging.info('Processing file \'%s\'', path)
-            self.AddFile(path, file_handle)
-        finally:
-          file_handle.close()
-        num_files += 1
-        if num_files % 500 == 0:
-          StatusUpdate('Scanned %d files.' % num_files)
+      self._AddFilesThatAreSmallEnough(paths, openfunc)
     except KeyboardInterrupt:
       logging.info('User interrupted. Aborting.')
       raise
@@ -2168,28 +2149,9 @@
                     path, e)
       raise
 
-    app_summary = None
     try:
-
       missing_files = self.Begin()
-      if missing_files:
-        StatusUpdate('Uploading %d files and blobs.' % len(missing_files))
-        num_files = 0
-        for missing_file in missing_files:
-          file_handle = openfunc(missing_file)
-          try:
-            self.UploadFile(missing_file, file_handle)
-          finally:
-            file_handle.close()
-          num_files += 1
-          if num_files % 500 == 0:
-            StatusUpdate('Processed %d out of %s.' %
-                         (num_files, len(missing_files)))
-
-        self.file_batcher.Flush()
-        self.blob_batcher.Flush()
-        self.errorblob_batcher.Flush()
-        StatusUpdate('Uploaded %d files and blobs' % num_files)
+      self._UploadMissingFiles(missing_files, openfunc)
 
 
       if (self.config.derived_file_type and
@@ -2197,7 +2159,6 @@
         try:
           self.Precompile()
         except urllib2.HTTPError, e:
-
           ErrorUpdate('Error %d: --- begin server output ---\n'
                       '%s\n--- end server output ---' %
                       (e.code, e.read().rstrip('\n')))
@@ -2218,29 +2179,110 @@
       app_summary = self.Commit()
       StatusUpdate('Completed update of %s' % self.Describe())
 
-    except KeyboardInterrupt:
-
-      logging.info('User interrupted. Aborting.')
-      self.Rollback()
-      raise
-    except urllib2.HTTPError, err:
-
-      logging.info('HTTP Error (%s)', err)
-      self.Rollback()
-      raise
-    except CannotStartServingError, err:
-
-      logging.error(err.message)
-      self.Rollback()
-      raise
-    except:
-      logging.exception('An unexpected error occurred. Aborting.')
+    except BaseException, e:
+      self._LogDoUploadException(e)
       self.Rollback()
       raise
 
     logging.info('Done!')
     return app_summary
 
+  def _AddFilesThatAreSmallEnough(self, paths, openfunc):
+    """Calls self.AddFile on files that are small enough.
+
+    By small enough, we mean that their size is within
+    self.resource_limits['max_file_size'] for application files, and
+    'max_blob_size' otherwise. Files that are too large are logged as errors,
+    and dropped (not sure why this isn't handled by raising an exception...).
+
+    Args:
+      paths: List of paths, relative to the app's base path.
+      openfunc: A function that takes a paths element, and returns a file-like
+        object.
+    """
+    StatusUpdate('Scanning files on local disk.')
+    num_files = 0
+    for path in paths:
+      file_handle = openfunc(path)
+      try:
+        file_length = GetFileLength(file_handle)
+
+
+        file_classification = FileClassification(self.config, path)
+        if file_classification.IsApplicationFile():
+          max_size = self.resource_limits['max_file_size']
+        else:
+          max_size = self.resource_limits['max_blob_size']
+
+
+        if file_length > max_size:
+          logging.error('Ignoring file \'%s\': Too long '
+                        '(max %d bytes, file is %d bytes)',
+                        path, max_size, file_length)
+        else:
+          logging.info('Processing file \'%s\'', path)
+          self.AddFile(path, file_handle)
+      finally:
+        file_handle.close()
+
+
+      num_files += 1
+      if num_files % 500 == 0:
+        StatusUpdate('Scanned %d files.' % num_files)
+
+  def _UploadMissingFiles(self, missing_files, openfunc):
+    """DoUpload helper to upload files that need to be uploaded.
+
+    Args:
+      missing_files: List of files that need to be uploaded. Begin returns such
+        a list. Design note: we don't call Begin here, because we want DoUpload
+        to call it directly so that Begin/Commit are more clearly paired.
+      openfunc: Function that takes a path relative to the app's base path, and
+        returns a file-like object.
+    """
+    if not missing_files:
+      return
+
+    StatusUpdate('Uploading %d files and blobs.' % len(missing_files))
+    num_files = 0
+    for missing_file in missing_files:
+      file_handle = openfunc(missing_file)
+      try:
+        self.UploadFile(missing_file, file_handle)
+      finally:
+        file_handle.close()
+
+
+      num_files += 1
+      if num_files % 500 == 0:
+        StatusUpdate('Processed %d out of %s.' %
+                     (num_files, len(missing_files)))
+
+
+    self.file_batcher.Flush()
+    self.blob_batcher.Flush()
+    self.errorblob_batcher.Flush()
+    StatusUpdate('Uploaded %d files and blobs' % num_files)
+
+  @staticmethod
+  def _LogDoUploadException(exception):
+    """Helper that logs exceptions that occurred during DoUpload.
+
+    Args:
+      exception: An exception that was thrown during DoUpload.
+    """
+    def InstanceOf(tipe):
+      return isinstance(exception, tipe)
+
+    if InstanceOf(KeyboardInterrupt):
+      logging.info('User interrupted. Aborting.')
+    elif InstanceOf(urllib2.HTTPError):
+      logging.info('HTTP Error (%s)', exception)
+    elif InstanceOf(CannotStartServingError):
+      logging.error(exception.message)
+    else:
+      logging.exception('An unexpected error occurred. Aborting.')
+
 
 def FileIterator(base, skip_files, runtime, separator=os.path.sep):
   """Walks a directory tree, returning all the files. Follows symlinks.
@@ -2493,12 +2535,7 @@
     if len(self.args) < 1:
       self._PrintHelpAndExit()
 
-    if self.options.allow_any_runtime:
-
-
-
-      appinfo.AppInfoExternal._skip_runtime_checks = True
-    else:
+    if not self.options.allow_any_runtime:
       if self.options.runtime:
         if self.options.runtime not in SUPPORTED_RUNTIMES:
           _PrintErrorAndExit(self.error_fh,
@@ -3252,6 +3289,9 @@
                       'ignoring --no_precompilation')
       self.options.precompilation = True
 
+    if appyaml.runtime.startswith('java'):
+      self.options.precompilation = False
+
     if self.options.precompilation:
       if not appyaml.derived_file_type:
         appyaml.derived_file_type = []
@@ -3282,9 +3322,14 @@
             '-goroot', goroot,
             '-print_extras',
         ] + go_files
+
+        env = {
+            'GOOS': 'linux',
+            'GOARCH': 'amd64',
+        }
         try:
           p = subprocess.Popen(gab_argv, stdout=subprocess.PIPE,
-                               stderr=subprocess.PIPE, env={})
+                               stderr=subprocess.PIPE, env=env)
           (stdout, stderr) = p.communicate()
         except Exception, e:
           raise RuntimeError('failed running go-app-builder', e)
@@ -3297,12 +3342,12 @@
         overlay = dict([l.split('|') for l in stdout.split('\n') if l])
         logging.info('GOPATH overlay: %s', overlay)
 
-        def ofunc(path):
+        def Open(path):
           if path in overlay:
             return self.opener(overlay[path], 'rb')
           return self.opener(os.path.join(basepath, path), 'rb')
         paths = app_paths + overlay.keys()
-        openfunc = ofunc
+        openfunc = Open
 
     appversion = AppVersionUpload(rpcserver,
                                   appyaml,
@@ -3344,9 +3389,6 @@
       self.UpdateUsingSpecificFiles()
       return
 
-
-    yaml_file_basename = 'app.yaml'
-
     if appcfg_java.IsWarFileWithoutYaml(self.basepath):
       java_app_update = appcfg_java.JavaAppUpdate(self.basepath, self.options)
       sdk_root = os.path.dirname(appcfg_java.__file__)
@@ -3362,9 +3404,8 @@
       try:
         appyaml = self._ParseAppInfoFromYaml(
             self.stage_dir,
-            basename=os.path.splitext(yaml_file_basename)[0])
-        self._UpdateWithParsedAppYaml(
-            appyaml, self.stage_dir, yaml_file_basename)
+            basename=os.path.splitext(APP_YAML_FILENAME)[0])
+        self._UpdateWithParsedAppYaml(appyaml, self.stage_dir)
       finally:
         if self.options.retain_upload_dir:
           StatusUpdate(
@@ -3374,10 +3415,18 @@
     else:
       appyaml = self._ParseAppInfoFromYaml(
           self.basepath,
-          basename=os.path.splitext(yaml_file_basename)[0])
-      self._UpdateWithParsedAppYaml(appyaml, self.basepath, yaml_file_basename)
+          basename=os.path.splitext(APP_YAML_FILENAME)[0])
+      self._UpdateWithParsedAppYaml(appyaml, self.basepath)
 
-  def _UpdateWithParsedAppYaml(self, appyaml, basepath, yaml_file_basename):
+  def _UpdateWithParsedAppYaml(self, appyaml, basepath):
+    """Completes update command.
+
+    Helper to Update.
+
+    Args:
+      appyaml: AppInfoExternal for the app.
+      basepath: Path where application's files can be found.
+    """
     self.runtime = appyaml.runtime
     rpcserver = self._GetRpcServer()
 
@@ -3397,20 +3446,24 @@
 
     dos_yaml = self._ParseDosYaml(basepath, appyaml)
     if dos_yaml and dos_yaml.application != appyaml.application:
-      return _AbortAppMismatch('dos.yaml')
+      _AbortAppMismatch('dos.yaml')
+      return
 
     queue_yaml = self._ParseQueueYaml(basepath, appyaml)
     if queue_yaml and queue_yaml.application != appyaml.application:
-      return _AbortAppMismatch('queue.yaml')
+      _AbortAppMismatch('queue.yaml')
+      return
 
     cron_yaml = self._ParseCronYaml(basepath, appyaml)
     if cron_yaml and cron_yaml.application != appyaml.application:
-      return _AbortAppMismatch('cron.yaml')
+      _AbortAppMismatch('cron.yaml')
+      return
 
     index_defs = self._ParseIndexYaml(basepath, appyaml)
     if index_defs and index_defs.application != appyaml.application:
-      return _AbortAppMismatch('index.yaml')
-    self.UpdateVersion(rpcserver, basepath, appyaml, yaml_file_basename)
+      _AbortAppMismatch('index.yaml')
+      return
+    self.UpdateVersion(rpcserver, basepath, appyaml, APP_YAML_FILENAME)
 
     if appyaml.runtime == 'python':
       MigratePython27Notice()
@@ -3778,6 +3831,52 @@
 
     print >> self.out_fh, response
 
+  def DebugAction(self):
+    """Sets the specified version and instance for an app to be debuggable."""
+    if len(self.args) == 1:
+      appyaml = self._ParseAppInfoFromYaml(self.args[0])
+      app_id = appyaml.application
+      module = appyaml.module or ''
+      version = appyaml.version
+    elif not self.args:
+      if not (self.options.app_id and self.options.version):
+        self.parser.error(
+            ('Expected a <directory> argument or both --application and '
+             '--version flags.'))
+      module = ''
+    else:
+      self._PrintHelpAndExit()
+
+    if self.options.instance is None:
+      self.parser.error(
+          ('Expected an --instance flag.'))
+
+
+
+    if self.options.app_id:
+      app_id = self.options.app_id
+    if self.options.module:
+      module = self.options.module
+    if self.options.version:
+      version = self.options.version
+
+    rpcserver = self._GetRpcServer()
+    response = rpcserver.Send('/api/vms/debug',
+                              app_id=app_id,
+                              version_match=version,
+                              module=module,
+                              instance=self.options.instance)
+    print >> self.out_fh, response
+
+  def _DebugActionOptions(self, parser):
+    """Adds debug-specific options to 'parser'.
+
+    Args:
+      parser: An instance of OptionsParser.
+    """
+    parser.add_option('-I', '--instance', type='int', dest='instance',
+                      help='Instance to debug.')
+
   def _ParseAndValidateModuleYamls(self, yaml_paths):
     """Validates given yaml paths and returns the parsed yaml objects.
 
@@ -3827,9 +3926,9 @@
   def _ModuleAction(self, action_path):
     """Process flags and yaml files and make a call to the given path.
 
-    The 'start' and 'stop' actions are extremely similar in how they process
-    input to appcfg.py and only really differ in what path they hit on the
-    RPCServer.
+    The 'start_module_version' and 'stop_module_version' actions are extremely
+    similar in how they process input to appcfg.py and only really differ in
+    what path they hit on the RPCServer.
 
     Args:
       action_path: Path on the RPCServer to send the call to.
@@ -3877,12 +3976,12 @@
                                 version=version)
       print >> self.out_fh, response
 
-  def Start(self):
-    """Starts one or more modules."""
+  def StartModuleVersion(self):
+    """Starts one or more versions."""
     self._ModuleAction('/api/modules/start')
 
-  def Stop(self):
-    """Stops one or more modules."""
+  def StopModuleVersion(self):
+    """Stops one or more versions."""
     self._ModuleAction('/api/modules/stop')
 
   def Rollback(self):
@@ -4230,6 +4329,16 @@
       self.options.debug = True
 
   def _MakeLoaderArgs(self):
+    """Returns a dict made from many attributes of self.options, plus others.
+
+    See body for list of self.options attributes included. In addition, result
+    includes
+      'application' = self.options.app_id
+      'throttle_class' = self.throttle_class
+
+    Returns:
+      A dict.
+    """
     args = dict([(arg_name, getattr(self.options, arg_name, None)) for
                  arg_name in (
                      'url',
@@ -4679,21 +4788,23 @@
 The 'cron_info' command will display the next 'number' runs (default 5) for
 each cron job defined in the cron.yaml file."""),
 
-      'start': Action(
-          function='Start',
+      'start_module_version': Action(
+          function='StartModuleVersion',
           uses_basepath=False,
-          usage='%prog [options] start [file, ...]',
+          usage='%prog [options] start_module_version [file, ...]',
           short_desc='Start a module version.',
           long_desc="""
-The 'start' command will put a module version into the START state."""),
+The 'start_module_version' command will put a module version into the START
+state."""),
 
-      'stop': Action(
-          function='Stop',
+      'stop_module_version': Action(
+          function='StopModuleVersion',
           uses_basepath=False,
-          usage='%prog [options] stop [file, ...]',
+          usage='%prog [options] stop_module_version [file, ...]',
           short_desc='Stop a module version.',
           long_desc="""
-The 'stop' command will put a module version into the STOP state."""),
+The 'stop_module_version' command will put a module version into the STOP
+state."""),
 
 
 
@@ -4739,7 +4850,7 @@
 Defaults to using the application, version and module specified in app.yaml;
 use the --application, --version and --module flags to override these values.
 The --module flag can also be a comma-delimited string of several modules. (ex.
-module1,module2,module2) In this case, the default version of each module will
+module1,module2,module3) In this case, the default version of each module will
 be changed to the version specified.
 
 The 'migrate_traffic' command can be thought of as a safer version of this
@@ -4791,6 +4902,18 @@
           long_desc="""
 The 'delete_version' command deletes the specified version for the specified
 application."""),
+
+      'debug': Action(
+          function='DebugAction',
+          usage='%prog [options] debug -I instance [-A app_id] [-V version] '
+          ' [-M module] [directory]',
+          options=_DebugActionOptions,
+          short_desc='Debug a vm runtime application.',
+          hidden=True,
+          uses_basepath=False,
+          long_desc="""
+The 'debug' command configures a vm runtime instance to be accessable for
+debugging."""),
   }
 
 
diff --git a/google/appengine/tools/appcfg_java.py b/google/appengine/tools/appcfg_java.py
index 173e519..d9c9af5 100644
--- a/google/appengine/tools/appcfg_java.py
+++ b/google/appengine/tools/appcfg_java.py
@@ -21,16 +21,25 @@
 import re
 import shutil
 import subprocess
+import sys
 import tempfile
 
 from google.appengine.tools import app_engine_web_xml_parser
 from google.appengine.tools import backends_xml_parser
+from google.appengine.tools import cron_xml_parser
+from google.appengine.tools import dispatch_xml_parser
+from google.appengine.tools import dos_xml_parser
+from google.appengine.tools import indexes_xml_parser
 from google.appengine.tools import jarfile
+from google.appengine.tools import queue_xml_parser
 from google.appengine.tools import web_xml_parser
 from google.appengine.tools import yaml_translator
 
 
+_CLASSES_JAR_NAME_PREFIX = '_ah_webinf_classes'
+_COMPILED_JSP_JAR_NAME_PREFIX = '_ah_compiled_jsps'
 _LOCAL_JSPC_CLASS = 'com.google.appengine.tools.development.LocalJspC'
+_MAX_COMPILED_JSP_JAR_SIZE = 1024 * 1024 * 5
 
 
 class Error(Exception):
@@ -85,24 +94,89 @@
 
 class JavaAppUpdate(object):
   """Performs Java-specific update configurations."""
+
   _JSP_REGEX = re.compile('.*\\.jspx?')
 
+  class _XmlParser(object):
 
 
 
+    def __init__(self, xml_name, yaml_name, xml_to_yaml_function):
+      self.xml_name = xml_name
+      self.yaml_name = yaml_name
+      self.xml_to_yaml_function = xml_to_yaml_function
+
+  _XML_PARSERS = [
+      _XmlParser('cron.xml', 'cron.yaml', cron_xml_parser.GetCronYaml),
+      _XmlParser('datastore-indexes.xml', 'index.yaml',
+                 indexes_xml_parser.GetIndexYaml),
+      _XmlParser('dispatch.xml', 'dispatch.yaml',
+                 dispatch_xml_parser.GetDispatchYaml),
+      _XmlParser('dos.xml', 'dos.yaml', dos_xml_parser.GetDosYaml),
+      _XmlParser('queue.xml', 'queue.yaml', queue_xml_parser.GetQueueYaml),
+  ]
+
+  _XML_VALIDATOR_CLASS = 'com.google.appengine.tools.admin.XmlValidator'
+
   def __init__(self, basepath, options):
     self.basepath = basepath
     self.options = options
 
+    java_home, exec_suffix = _JavaHomeAndSuffix()
+    self.java_command = os.path.join(java_home, 'bin', 'java' + exec_suffix)
+    self.javac_command = os.path.join(java_home, 'bin', 'javac' + exec_suffix)
+
+    self._ValidateXmlFiles()
+
     self.app_engine_web_xml = self._ReadAppEngineWebXml()
     self.app_engine_web_xml.app_root = self.basepath
+    if self.options.app_id:
+      self.app_engine_web_xml.app_id = self.options.app_id
+    if self.options.version:
+      self.app_engine_web_xml.version_id = self.options.version
     self.web_xml = self._ReadWebXml()
 
-  def _ReadAppEngineWebXml(self, basepath=None):
-    if not basepath:
-      basepath = self.basepath
+  def _ValidateXmlFiles(self):
+
+
+
+
+
+
+
+
+    sdk_dir = os.path.dirname(jarfile.__file__)
+    xml_validator_jar = os.path.join(
+        sdk_dir, 'java', 'lib', 'impl', 'libxmlvalidator.jar')
+    if not os.path.exists(xml_validator_jar):
+
+      print >>sys.stderr, ('Not validating XML files because %s does not '
+                           'exist' % xml_validator_jar)
+      return
+    validator_args = []
+    schema_dir = os.path.join(sdk_dir, 'java', 'docs')
+    for schema_name in os.listdir(schema_dir):
+      basename, extension = os.path.splitext(schema_name)
+      if extension == '.xsd':
+        schema_file = os.path.join(schema_dir, schema_name)
+        xml_file = os.path.join(self.basepath, 'WEB-INF', basename + '.xml')
+        if os.path.exists(xml_file):
+          validator_args += [xml_file, schema_file]
+    if validator_args:
+      command_and_args = [
+          self.java_command,
+          '-classpath',
+          xml_validator_jar,
+          self._XML_VALIDATOR_CLASS,
+      ] + validator_args
+      status = subprocess.call(command_and_args)
+      if status:
+
+        raise ConfigurationError('XML validation failed')
+
+  def _ReadAppEngineWebXml(self):
     return self._ReadAndParseXml(
-        basepath=basepath,
+        basepath=self.basepath,
         file_name='appengine-web.xml',
         parser=app_engine_web_xml_parser.AppEngineWebXmlParser)
 
@@ -114,11 +188,9 @@
         file_name='web.xml',
         parser=web_xml_parser.WebXmlParser)
 
-  def _ReadBackendsXml(self, basepath=None):
-    if not basepath:
-      basepath = self.basepath
+  def _ReadBackendsXml(self):
     return self._ReadAndParseXml(
-        basepath=basepath,
+        basepath=self.basepath,
         file_name='backends.xml',
         parser=backends_xml_parser.BackendsXmlParser)
 
@@ -158,7 +230,8 @@
     if self.options.compile_jsps:
       self._CompileJspsIfAny(tools_dir, stage_dir)
 
-    web_inf_lib = os.path.join(stage_dir, 'WEB-INF', 'lib')
+    web_inf = os.path.join(stage_dir, 'WEB-INF')
+    web_inf_lib = os.path.join(web_inf, 'lib')
     api_jar_dict = _FindApiJars(web_inf_lib)
     api_versions = set(api_jar_dict.values())
     if not api_versions:
@@ -173,7 +246,23 @@
     for staged_api_jar in api_jar_dict:
       os.remove(staged_api_jar)
 
-    self._GenerateAppYaml(stage_dir, api_version)
+    appengine_generated = os.path.join(
+        stage_dir, 'WEB-INF', 'appengine-generated')
+    self._GenerateAppYaml(stage_dir, api_version, appengine_generated)
+
+    app_id = self.options.app_id or self.app_engine_web_xml.app_id
+    assert app_id, 'Missing app id'
+
+
+    for parser in self._XML_PARSERS:
+      xml_name = os.path.join(web_inf, parser.xml_name)
+      if os.path.exists(xml_name):
+        with open(xml_name) as xml_file:
+          xml_string = xml_file.read()
+        yaml_string = parser.xml_to_yaml_function(app_id, xml_string)
+        yaml_file = os.path.join(appengine_generated, parser.yaml_name)
+        with open(yaml_file, 'w') as yaml:
+          yaml.write(yaml_string)
 
     return stage_dir
 
@@ -202,13 +291,15 @@
         static_file_list,
         api_version).GetYaml()
 
-  def _GenerateAppYaml(self, stage_dir, api_version):
-    """Creates the app.yaml file in WEB-INF/appengine-generated/."""
+  def _GenerateAppYaml(self, stage_dir, api_version, appengine_generated):
+    """Creates the app.yaml file in WEB-INF/appengine-generated/.
+
+    Returns:
+      The path to the WEB-INF/appengine-generated directory.
+    """
     static_file_list = self._GetStaticFileList(stage_dir)
     yaml_str = self.GenerateAppYamlString(
         stage_dir, static_file_list, api_version)
-    appengine_generated = os.path.join(
-        stage_dir, 'WEB-INF', 'appengine-generated')
     if not os.path.isdir(appengine_generated):
       os.mkdir(appengine_generated)
     with open(os.path.join(appengine_generated, 'app.yaml'), 'w') as handle:
@@ -246,76 +337,88 @@
       return
     shutil.copy(source, dest)
 
-  def _CopyOrLinkDirectories(self, source_dir, dest_dir):
-    for name in os.listdir(source_dir):
-      source_path = os.path.join(source_dir, name)
-      dest_path = os.path.join(dest_dir, name)
-      if os.path.isdir(source_path):
-        self._CopyOrLinkDirectories(source_path, dest_path)
+  def _MoveDirectoryContents(self, source_dir, dest_dir):
+    """Move the contents of source_dir to dest_dir, which might not exist.
+
+    Raises:
+      IOError: if the dest_dir hierarchy already contains a file where the
+        source_dir hierarchy has a file or directory of the same name, or if
+        the dest_dir hierarchy already contains a directory where the source_dir
+        hierarchy has a file of the same name.
+    """
+    if not os.path.exists(dest_dir):
+      os.mkdir(dest_dir)
+    for entry in os.listdir(source_dir):
+      source_entry = os.path.join(source_dir, entry)
+      dest_entry = os.path.join(dest_dir, entry)
+      if os.path.exists(dest_entry):
+        if os.path.isdir(source_entry) and os.path.isdir(dest_entry):
+          self._MoveDirectoryContents(source_entry, dest_entry)
+        else:
+          raise IOError('Cannot overwrite existing %s' % dest_entry)
       else:
-        self._CopyOrLinkFile(source_path, dest_path)
+        shutil.move(source_entry, dest_entry)
 
   @staticmethod
   def _GetStaticFileList(staging_dir):
     return _FilesMatching(os.path.join(staging_dir, '__static__'))
 
   def _CompileJspsIfAny(self, tools_dir, staging_dir):
-    """Performs necessary preparations for JSP Compilation."""
+    """Compiles JSP files, if any, into .class files.."""
     if self._MatchingFileExists(self._JSP_REGEX, staging_dir):
-      staging_web_inf = os.path.join(staging_dir, 'WEB-INF')
-      lib_dir = os.path.join(staging_web_inf, 'lib')
-
-      for jar_file in GetUserJspLibFiles(tools_dir):
-        self._CopyOrLinkFile(
-            jar_file, os.path.join(lib_dir, os.path.basename(jar_file)))
-      for jar_file in GetSharedJspLibFiles(tools_dir):
-        self._CopyOrLinkFile(
-            jar_file, os.path.join(lib_dir, os.path.basename(jar_file)))
-
-      classes_dir = os.path.join(staging_web_inf, 'classes')
       gen_dir = tempfile.mkdtemp()
-      generated_web_xml = os.path.join(staging_web_inf, 'generated_web.xml')
+      try:
+        self._CompileJspsWithGenDir(tools_dir, staging_dir, gen_dir)
+      finally:
+        shutil.rmtree(gen_dir)
 
-      classpath = self._GetJspClasspath(tools_dir, classes_dir, gen_dir)
-      java_home, exec_suffix = _JavaHomeAndSuffix()
+  def _CompileJspsWithGenDir(self, tools_dir, staging_dir, gen_dir):
+    staging_web_inf = os.path.join(staging_dir, 'WEB-INF')
+    lib_dir = os.path.join(staging_web_inf, 'lib')
 
-      java_command = os.path.join(java_home, 'bin', 'java' + exec_suffix)
+    for jar_file in GetUserJspLibFiles(tools_dir):
+      self._CopyOrLinkFile(
+          jar_file, os.path.join(lib_dir, os.path.basename(jar_file)))
+    for jar_file in GetSharedJspLibFiles(tools_dir):
+      self._CopyOrLinkFile(
+          jar_file, os.path.join(lib_dir, os.path.basename(jar_file)))
 
-      command_and_args = [
-          java_command,
-          '-classpath', classpath,
-          _LOCAL_JSPC_CLASS,
-          '-uriroot', staging_dir,
-          '-p', 'org.apache.jsp',
-          '-l',
-          '-v',
-          '-webinc', generated_web_xml,
-          '-d', gen_dir,
-          '-javaEncoding', self.options.compile_encoding,
-      ]
+    classes_dir = os.path.join(staging_web_inf, 'classes')
+    generated_web_xml = os.path.join(staging_web_inf, 'generated_web.xml')
 
-      status = subprocess.call(command_and_args)
-      if status:
-        raise CompileError(
-            'Compilation of JSPs exited with status %d' % status)
+    classpath = self._GetJspClasspath(tools_dir, classes_dir, gen_dir)
 
-      self._CompileJavaFiles(classpath, staging_web_inf, gen_dir,
-                             java_home, exec_suffix)
+    command_and_args = [
+        self.java_command,
+        '-classpath', classpath,
+        _LOCAL_JSPC_CLASS,
+        '-uriroot', staging_dir,
+        '-p', 'org.apache.jsp',
+        '-l',
+        '-v',
+        '-webinc', generated_web_xml,
+        '-d', gen_dir,
+        '-javaEncoding', self.options.compile_encoding,
+    ]
+
+    status = subprocess.call(command_and_args)
+    if status:
+      raise CompileError(
+          'Compilation of JSPs exited with status %d' % status)
+
+    self._CompileJavaFiles(classpath, staging_web_inf, gen_dir)
 
 
-      self.web_xml = self._ReadWebXml(staging_dir)
+    self.web_xml = self._ReadWebXml(staging_dir)
 
-  def _CompileJavaFiles(
-      self, classpath, web_inf, jsp_class_dir, java_home, exec_suffix):
+  def _CompileJavaFiles(self, classpath, web_inf, jsp_class_dir):
     """Compile all *.java files found under jsp_class_dir."""
     java_files = _FilesMatching(jsp_class_dir, lambda f: f.endswith('.java'))
     if not java_files:
       return
 
-    javac_command = os.path.join(java_home, 'bin', 'javac' + exec_suffix)
-
     command_and_args = [
-        javac_command,
+        self.javac_command,
         '-classpath', classpath,
         '-d', jsp_class_dir,
         '-encoding', self.options.compile_encoding,
@@ -327,10 +430,10 @@
           'Compilation of JSP-generated code exited with status %d' % status)
 
     if self.options.jar_jsps:
-      raise RuntimeError('Only --disable_jar_jsps supported for now')
+      self._ZipJasperGeneratedFiles(web_inf, jsp_class_dir)
     else:
       web_inf_classes = os.path.join(web_inf, 'classes')
-      self._CopyOrLinkDirectories(jsp_class_dir, web_inf_classes)
+      self._MoveDirectoryContents(jsp_class_dir, web_inf_classes)
 
     if self.options.delete_jsps:
       jsps = _FilesMatching(os.path.dirname(web_inf),
@@ -339,8 +442,26 @@
         os.remove(f)
 
     if self.options.do_jar_classes:
+      self._ZipWebInfClassesFiles(web_inf)
 
-      raise RuntimeError('--jar_classes not supported yet')
+  @staticmethod
+  def _ZipJasperGeneratedFiles(web_inf, jsp_class_dir):
+    lib_dir = os.path.join(web_inf, 'lib')
+    jarfile.Make(jsp_class_dir, lib_dir, _COMPILED_JSP_JAR_NAME_PREFIX,
+                 maximum_size=_MAX_COMPILED_JSP_JAR_SIZE,
+                 include_predicate=lambda name: not name.endswith('.java'))
+
+  @staticmethod
+  def _ZipWebInfClassesFiles(web_inf):
+
+
+    lib_dir = os.path.join(web_inf, 'lib')
+    classes_dir = os.path.join(web_inf, 'classes')
+    jarfile.Make(classes_dir, lib_dir, _CLASSES_JAR_NAME_PREFIX,
+                 maximum_size=_MAX_COMPILED_JSP_JAR_SIZE)
+    shutil.rmtree(classes_dir)
+
+    os.mkdir(classes_dir)
 
   @staticmethod
   def _GetJspClasspath(tools_dir, classes_dir, gen_dir):
@@ -434,11 +555,21 @@
     def IsExecutable(binary):
       return os.path.isfile(binary) and os.access(binary, os.X_OK)
 
-    for suffix in ['', '.exe']:
-      if all(IsExecutable(os.path.join(path, 'bin', binary + suffix))
-             for binary in ['java', 'javac', 'jar']):
-        return (path, suffix)
-    return None
+    def ResultFor(path):
+      for suffix in ['', '.exe']:
+        if all(IsExecutable(os.path.join(path, 'bin', binary + suffix))
+               for binary in ['java', 'javac', 'jar']):
+          return (path, suffix)
+      return None
+
+    result = ResultFor(path)
+    if not result:
+
+
+      head, tail = os.path.split(path)
+      if tail == 'jre':
+        result = ResultFor(head)
+    return result
 
   java_home = os.getenv('JAVA_HOME')
   if java_home:
@@ -476,7 +607,8 @@
   result = {}
   for jar_file in _FilesMatching(lib_dir, lambda f: f.endswith('.jar')):
     manifest = jarfile.ReadManifest(jar_file)
-    section = manifest.sections.get('com/google/appengine/api/')
-    if section and 'Specification-Version' in section:
-      result[jar_file] = section['Specification-Version']
+    if manifest:
+      section = manifest.sections.get('com/google/appengine/api/')
+      if section and 'Specification-Version' in section:
+        result[jar_file] = section['Specification-Version']
   return result
diff --git a/google/appengine/tools/appengine_rpc_httplib2.py b/google/appengine/tools/appengine_rpc_httplib2.py
index 4cf03a9..dcd956d 100644
--- a/google/appengine/tools/appengine_rpc_httplib2.py
+++ b/google/appengine/tools/appengine_rpc_httplib2.py
@@ -31,6 +31,7 @@
 import logging
 import os
 import re
+import types
 import urllib
 import urllib2
 
@@ -298,7 +299,7 @@
       self.access_token = access_token
       self.client_id = client_id
       self.client_secret = client_secret
-      self.scope = scope if isinstance(scope, basestring) else ' '.join(scope)
+      self.scope = scope
       self.refresh_token = refresh_token
       self.credential_file = credential_file
       self.token_uri = token_uri
@@ -403,7 +404,7 @@
       flow = client.OAuth2WebServerFlow(
           client_id=self.oauth2_parameters.client_id,
           client_secret=self.oauth2_parameters.client_secret,
-          scope=self.oauth2_parameters.scope,
+          scope=_ScopesToString(self.oauth2_parameters.scope),
           user_agent=self.user_agent)
       self.credentials = tools.run(flow, self.storage)
     if self.credentials and not self.credentials.invalid:
@@ -415,3 +416,13 @@
         self.credentials.authorize(http)
         return
     logger.debug('_Authenticate skipped auth; needs_auth=%s', needs_auth)
+
+
+def _ScopesToString(scopes):
+  """Converts scope value to a string."""
+
+
+  if isinstance(scopes, types.StringTypes):
+    return scopes
+  else:
+    return ' '.join(scopes)
diff --git a/google/appengine/tools/cron_xml_parser.py b/google/appengine/tools/cron_xml_parser.py
new file mode 100644
index 0000000..ca63131
--- /dev/null
+++ b/google/appengine/tools/cron_xml_parser.py
@@ -0,0 +1,141 @@
+#!/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.
+#
+"""Directly processes text of cron.xml.
+
+CronXmlParser is called with an XML string to produce a CronXml object
+containing the data from the XML.
+
+CronXmlParser: converts XML to CronXml objct
+Cron: describes a single cron specified in cron.xml
+"""
+
+from xml.etree import ElementTree
+
+from google.appengine.cron import groc
+from google.appengine.cron import groctimespecification
+from google.appengine.tools import xml_parser_utils
+from google.appengine.tools.app_engine_config_exception import AppEngineConfigException
+
+
+def GetCronYaml(unused_application, cron_xml_str):
+  return _MakeCronListIntoYaml(CronXmlParser().ProcessXml(cron_xml_str))
+
+
+def _MakeCronListIntoYaml(cron_list):
+  """Converts list of yaml statements describing cron jobs into a string."""
+  statements = ['cron:']
+  for cron in cron_list:
+    statements += cron.ToYaml()
+  return '\n'.join(statements) + '\n'
+
+
+class CronXmlParser(object):
+  """Provides logic for walking down XML tree and pulling data."""
+
+  def ProcessXml(self, xml_str):
+    """Parses XML string and returns object representation of relevant info.
+
+    Args:
+      xml_str: The XML string.
+    Returns:
+      A list of Cron objects containing information about cron jobs from the
+      XML.
+    Raises:
+      AppEngineConfigException: In case of malformed XML or illegal inputs.
+    """
+
+    try:
+      self.crons = []
+      self.errors = []
+      xml_root = ElementTree.fromstring(xml_str)
+      if xml_root.tag != 'cronentries':
+        raise AppEngineConfigException('Root tag must be <cronentries>')
+
+      for child in xml_root.getchildren():
+        self.ProcessCronNode(child)
+
+      if self.errors:
+        raise AppEngineConfigException('\n'.join(self.errors))
+
+      return self.crons
+    except ElementTree.ParseError:
+      raise AppEngineConfigException('Bad input -- not valid XML')
+
+  def ProcessCronNode(self, node):
+    """Processes XML <cron> nodes into Cron objects.
+
+    The following information is parsed out:
+      description: Describing the purpose of the cron job.
+      url: The location of the script.
+      schedule: Written in groc; the schedule according to which the job is
+        executed.
+      timezone: The timezone that the schedule runs in.
+      target: Which version of the app this applies to.
+
+    Args:
+      node: <cron> XML node in cron.xml.
+    """
+    tag = xml_parser_utils.GetTag(node)
+    if tag != 'cron':
+      self.errors.append('Unrecognized node: <%s>' % tag)
+      return
+
+    cron = Cron()
+    cron.url = xml_parser_utils.GetChildNodeText(node, 'url')
+    cron.timezone = xml_parser_utils.GetChildNodeText(node, 'timezone')
+    cron.target = xml_parser_utils.GetChildNodeText(node, 'target')
+    cron.description = xml_parser_utils.GetChildNodeText(node, 'description')
+    cron.schedule = xml_parser_utils.GetChildNodeText(node, 'schedule')
+
+    validation_error = self._ValidateCronEntry(cron)
+    if validation_error:
+      self.errors.append(validation_error)
+    else:
+      self.crons.append(cron)
+
+  def _ValidateCronEntry(self, cron):
+
+
+    if not cron.url:
+      return 'No URL for <cron> entry'
+    if not cron.schedule:
+      return "No schedule provided for <cron> entry with URL '%s'" % cron.url
+    try:
+      groctimespecification.GrocTimeSpecification(cron.schedule)
+    except groc.GrocException:
+      return ("Text '%s' in <schedule> node failed to parse,"
+              ' for <cron> entry with url %s.'
+              % (cron.schedule, cron.url))
+
+
+class Cron(object):
+  """Instances contain information about individual cron entries."""
+  TZ_GMT = 'UTC'
+
+  def ToYaml(self):
+    """Returns data from Cron object as a list of Yaml statements."""
+    statements = [
+        '- url: %s' % self._SanitizeForYaml(self.url),
+        '  schedule: %s' % self._SanitizeForYaml(self.schedule)]
+    for optional in ('target', 'timezone', 'description'):
+      field = getattr(self, optional)
+      if field:
+        statements.append('  %s: %s' % (optional, self._SanitizeForYaml(field)))
+    return statements
+
+  def _SanitizeForYaml(self, field):
+    return "'%s'" % field.replace('\n', ' ').replace("'", "''")
diff --git a/google/appengine/tools/dev-channel-js.js b/google/appengine/tools/dev-channel-js.js
index f696d05..c76dfca 100644
--- a/google/appengine/tools/dev-channel-js.js
+++ b/google/appengine/tools/dev-channel-js.js
@@ -335,6 +335,10 @@
   childCtor.superClass_ = parentCtor.prototype;
   childCtor.prototype = new tempCtor;
   childCtor.prototype.constructor = childCtor;
+  childCtor.base = function(me, methodName, var_args) {
+    var args = Array.prototype.slice.call(arguments, 2);
+    return parentCtor.prototype[methodName].apply(me, args);
+  };
 };
 goog.base = function(me, opt_methodName, var_args) {
   var caller = arguments.callee.caller;
@@ -503,7 +507,7 @@
 };
 goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
   if (opt_isLikelyToContainHtmlChars) {
-    return str.replace(goog.string.amperRe_, "&amp;").replace(goog.string.ltRe_, "&lt;").replace(goog.string.gtRe_, "&gt;").replace(goog.string.quotRe_, "&quot;");
+    return str.replace(goog.string.amperRe_, "&amp;").replace(goog.string.ltRe_, "&lt;").replace(goog.string.gtRe_, "&gt;").replace(goog.string.quotRe_, "&quot;").replace(goog.string.singleQuoteRe_, "&#39;");
   }
   if (!goog.string.allRe_.test(str)) {
     return str;
@@ -512,18 +516,24 @@
   -1 != str.indexOf("<") && (str = str.replace(goog.string.ltRe_, "&lt;"));
   -1 != str.indexOf(">") && (str = str.replace(goog.string.gtRe_, "&gt;"));
   -1 != str.indexOf('"') && (str = str.replace(goog.string.quotRe_, "&quot;"));
+  -1 != str.indexOf("'") && (str = str.replace(goog.string.singleQuoteRe_, "&#39;"));
   return str;
 };
 goog.string.amperRe_ = /&/g;
 goog.string.ltRe_ = /</g;
 goog.string.gtRe_ = />/g;
-goog.string.quotRe_ = /\"/g;
-goog.string.allRe_ = /[&<>\"]/;
+goog.string.quotRe_ = /"/g;
+goog.string.singleQuoteRe_ = /'/g;
+goog.string.allRe_ = /[&<>"']/;
 goog.string.unescapeEntities = function(str) {
   return goog.string.contains(str, "&") ? "document" in goog.global ? goog.string.unescapeEntitiesUsingDom_(str) : goog.string.unescapePureXmlEntities_(str) : str;
 };
-goog.string.unescapeEntitiesUsingDom_ = function(str) {
-  var seen = {"&amp;":"&", "&lt;":"<", "&gt;":">", "&quot;":'"'}, div = document.createElement("div");
+goog.string.unescapeEntitiesWithDocument = function(str, document) {
+  return goog.string.contains(str, "&") ? goog.string.unescapeEntitiesUsingDom_(str, document) : str;
+};
+goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
+  var seen = {"&amp;":"&", "&lt;":"<", "&gt;":">", "&quot;":'"'}, div;
+  div = opt_document ? opt_document.createElement("div") : document.createElement("div");
   return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
     var value = seen[s];
     if (value) {
@@ -2729,14 +2739,13 @@
 goog.events.Event = function(type, opt_target) {
   this.type = type instanceof goog.events.EventId ? String(type) : type;
   this.currentTarget = this.target = opt_target;
+  this.defaultPrevented = this.propagationStopped_ = !1;
+  this.returnValue_ = !0;
 };
 goog.events.Event.prototype.disposeInternal = function() {
 };
 goog.events.Event.prototype.dispose = function() {
 };
-goog.events.Event.prototype.propagationStopped_ = !1;
-goog.events.Event.prototype.defaultPrevented = !1;
-goog.events.Event.prototype.returnValue_ = !0;
 goog.events.Event.prototype.stopPropagation = function() {
   this.propagationStopped_ = !0;
 };
@@ -2779,30 +2788,18 @@
 MSGOTPOINTERCAPTURE:"MSGotPointerCapture", MSINERTIASTART:"MSInertiaStart", MSLOSTPOINTERCAPTURE:"MSLostPointerCapture", MSPOINTERCANCEL:"MSPointerCancel", MSPOINTERDOWN:"MSPointerDown", MSPOINTERENTER:"MSPointerEnter", MSPOINTERHOVER:"MSPointerHover", MSPOINTERLEAVE:"MSPointerLeave", MSPOINTERMOVE:"MSPointerMove", MSPOINTEROUT:"MSPointerOut", MSPOINTEROVER:"MSPointerOver", MSPOINTERUP:"MSPointerUp", TEXTINPUT:"textinput", COMPOSITIONSTART:"compositionstart", COMPOSITIONUPDATE:"compositionupdate", 
 COMPOSITIONEND:"compositionend", EXIT:"exit", LOADABORT:"loadabort", LOADCOMMIT:"loadcommit", LOADREDIRECT:"loadredirect", LOADSTART:"loadstart", LOADSTOP:"loadstop", RESPONSIVE:"responsive", SIZECHANGED:"sizechanged", UNRESPONSIVE:"unresponsive", VISIBILITYCHANGE:"visibilitychange"};
 goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
+  goog.events.Event.call(this, opt_e ? opt_e.type : "");
+  this.relatedTarget = this.currentTarget = this.target = null;
+  this.charCode = this.keyCode = this.button = this.screenY = this.screenX = this.clientY = this.clientX = this.offsetY = this.offsetX = 0;
+  this.metaKey = this.shiftKey = this.altKey = this.ctrlKey = !1;
+  this.event_ = this.state = null;
   opt_e && this.init(opt_e, opt_currentTarget);
 };
 goog.inherits(goog.events.BrowserEvent, goog.events.Event);
 goog.events.BrowserEvent.MouseButton = {LEFT:0, MIDDLE:1, RIGHT:2};
 goog.events.BrowserEvent.IEButtonMap = [1, 4, 2];
-goog.events.BrowserEvent.prototype.target = null;
-goog.events.BrowserEvent.prototype.relatedTarget = null;
-goog.events.BrowserEvent.prototype.offsetX = 0;
-goog.events.BrowserEvent.prototype.offsetY = 0;
-goog.events.BrowserEvent.prototype.clientX = 0;
-goog.events.BrowserEvent.prototype.clientY = 0;
-goog.events.BrowserEvent.prototype.screenX = 0;
-goog.events.BrowserEvent.prototype.screenY = 0;
-goog.events.BrowserEvent.prototype.button = 0;
-goog.events.BrowserEvent.prototype.keyCode = 0;
-goog.events.BrowserEvent.prototype.charCode = 0;
-goog.events.BrowserEvent.prototype.ctrlKey = !1;
-goog.events.BrowserEvent.prototype.altKey = !1;
-goog.events.BrowserEvent.prototype.shiftKey = !1;
-goog.events.BrowserEvent.prototype.metaKey = !1;
-goog.events.BrowserEvent.prototype.event_ = null;
 goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
   var type = this.type = e.type;
-  goog.events.Event.call(this, type);
   this.target = e.target || e.srcElement;
   this.currentTarget = opt_currentTarget;
   var relatedTarget = e.relatedTarget;
@@ -2824,7 +2821,6 @@
   this.state = e.state;
   this.event_ = e;
   e.defaultPrevented && this.preventDefault();
-  delete this.propagationStopped_;
 };
 goog.events.BrowserEvent.prototype.stopPropagation = function() {
   goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
@@ -4583,7 +4579,7 @@
   return goog.debug.LOGGING_ENABLED && level.value >= this.getEffectiveLevel().value;
 };
 goog.debug.Logger.prototype.log = function(level, msg, opt_exception) {
-  goog.debug.LOGGING_ENABLED && this.isLoggable(level) && this.doLogRecord_(this.getLogRecord(level, msg, opt_exception));
+  goog.debug.LOGGING_ENABLED && this.isLoggable(level) && (goog.isFunction(msg) && (msg = msg()), this.doLogRecord_(this.getLogRecord(level, msg, opt_exception)));
 };
 goog.debug.Logger.prototype.getLogRecord = function(level, msg, opt_exception) {
   var logRecord = goog.debug.LogBuffer.isBufferingEnabled() ? goog.debug.LogBuffer.getInstance().addRecord(level, msg, this.name_) : new goog.debug.LogRecord(level, String(msg), this.name_);
diff --git a/google/appengine/tools/dev_appserver.py b/google/appengine/tools/dev_appserver.py
index 7a5bf7e..2fcf8bf 100644
--- a/google/appengine/tools/dev_appserver.py
+++ b/google/appengine/tools/dev_appserver.py
@@ -3447,6 +3447,8 @@
     mysql_user: MySQL user.
     mysql_password: MySQL password.
     mysql_socket: MySQL socket.
+    appidentity_email_address: Email address for service account substitute.
+    appidentity_private_key_path: Path to private key for service account sub.
     enable_sendmail: Whether to use sendmail as an alternative to SMTP.
     show_mail_body: Whether to log the body of emails.
     remove: Used for dependency injection.
@@ -3490,6 +3492,8 @@
   smtp_password = config.get('smtp_password', '')
   enable_sendmail = config.get('enable_sendmail', False)
   show_mail_body = config.get('show_mail_body', False)
+  appidentity_email_address = config.get('appidentity_email_address', None)
+  appidentity_private_key_path = config.get('appidentity_private_key_path', None)
   remove = config.get('remove', os.remove)
   disable_task_running = config.get('disable_task_running', False)
   task_retry_seconds = config.get('task_retry_seconds', 30)
@@ -3541,7 +3545,9 @@
 
     apiproxy_stub_map.apiproxy.RegisterStub(
         'app_identity_service',
-        app_identity_stub.AppIdentityServiceStub())
+        app_identity_stub.AppIdentityServiceStub.Create(
+            email_address=appidentity_email_address,
+            private_key_path=appidentity_private_key_path))
 
     apiproxy_stub_map.apiproxy.RegisterStub(
         'capability_service',
diff --git a/google/appengine/tools/devappserver2/api_server.py b/google/appengine/tools/devappserver2/api_server.py
index 6e47f09..c117635 100644
--- a/google/appengine/tools/devappserver2/api_server.py
+++ b/google/appengine/tools/devappserver2/api_server.py
@@ -210,6 +210,8 @@
     app_id,
     application_root,
     trusted,
+    appidentity_email_address,
+    appidentity_private_key_path,
     blobstore_path,
     datastore_consistency,
     datastore_path,
@@ -240,6 +242,12 @@
     application_root: The path to the directory containing the user's
         application e.g. "/home/joe/myapp".
     trusted: A bool indicating if privileged APIs should be made available.
+    appidentity_email_address: Email address associated with a service account
+        that has a downloadable key. May be None for no local application
+        identity.
+    appidentity_private_key_path: Path to private key file associated with
+        service account (.pem format). Must be set if appidentity_email_address
+        is set.
     blobstore_path: The path to the file that should be used for blobstore
         storage.
     datastore_consistency: The datastore_stub_util.BaseConsistencyPolicy to
@@ -284,7 +292,9 @@
     default_gcs_bucket_name: A str, overriding the default bucket behavior.
   """
 
-  identity_stub = app_identity_stub.AppIdentityServiceStub()
+  identity_stub = app_identity_stub.AppIdentityServiceStub.Create(
+      email_address=appidentity_email_address,
+      private_key_path=appidentity_private_key_path)
   if default_gcs_bucket_name is not None:
     identity_stub.SetDefaultGcsBucketName(default_gcs_bucket_name)
   apiproxy_stub_map.apiproxy.RegisterStub('app_identity_service', identity_stub)
@@ -462,6 +472,8 @@
     app_id='myapp',
     application_root='/tmp/root',
     trusted=False,
+    appidentity_email_address=None,
+    appidentity_private_key_path=None,
     blobstore_path='/dev/null',
     datastore_consistency=None,
     datastore_path=':memory:',
@@ -496,6 +508,8 @@
               app_id,
               application_root,
               trusted,
+              appidentity_email_address,
+              appidentity_private_key_path,
               blobstore_path,
               datastore_consistency,
               datastore_path,
diff --git a/google/appengine/tools/devappserver2/application_configuration.py b/google/appengine/tools/devappserver2/application_configuration.py
index 2e6d681..8403d3a 100644
--- a/google/appengine/tools/devappserver2/application_configuration.py
+++ b/google/appengine/tools/devappserver2/application_configuration.py
@@ -123,9 +123,15 @@
 
   @property
   def version_id(self):
-    return '%s.%s' % (
-        self.major_version,
-        self._minor_version_id)
+    if self.module_name == 'default':
+      return '%s.%s' % (
+          self.major_version,
+          self._minor_version_id)
+    else:
+      return '%s:%s.%s' % (
+          self.module_name,
+          self.major_version,
+          self._minor_version_id)
 
   @property
   def runtime(self):
@@ -377,7 +383,8 @@
 
   @property
   def version_id(self):
-    return '%s.%s' % (
+    return '%s:%s.%s' % (
+        self.module_name,
         self.major_version,
         self._minor_version_id)
 
diff --git a/google/appengine/tools/devappserver2/application_configuration_test.py b/google/appengine/tools/devappserver2/application_configuration_test.py
index 13019cd..7cd7071 100644
--- a/google/appengine/tools/devappserver2/application_configuration_test.py
+++ b/google/appengine/tools/devappserver2/application_configuration_test.py
@@ -78,7 +78,7 @@
     self.assertEqual('dev~app', config.application)
     self.assertEqual('module1', config.module_name)
     self.assertEqual('1', config.major_version)
-    self.assertRegexpMatches(config.version_id, r'1\.\d+')
+    self.assertRegexpMatches(config.version_id, r'module1:1\.\d+')
     self.assertEqual('python27', config.runtime)
     self.assertFalse(config.threadsafe)
     self.assertEqual(automatic_scaling, config.automatic_scaling)
@@ -505,7 +505,7 @@
     self.assertEqual('dev~app', config.application)
     self.assertEqual('static', config.module_name)
     self.assertEqual('1', config.major_version)
-    self.assertRegexpMatches(config.version_id, r'1\.\d+')
+    self.assertRegexpMatches(config.version_id, r'static:1\.\d+')
     self.assertEqual('python27', config.runtime)
     self.assertFalse(config.threadsafe)
     self.assertEqual(None, config.automatic_scaling)
@@ -576,7 +576,7 @@
     self.assertEqual('dev~app', config.application)
     self.assertEqual('dynamic', config.module_name)
     self.assertEqual('1', config.major_version)
-    self.assertRegexpMatches(config.version_id, r'1\.\d+')
+    self.assertRegexpMatches(config.version_id, r'dynamic:1\.\d+')
     self.assertEqual('python27', config.runtime)
     self.assertFalse(config.threadsafe)
     self.assertEqual(None, config.automatic_scaling)
diff --git a/google/appengine/tools/devappserver2/devappserver2.py b/google/appengine/tools/devappserver2/devappserver2.py
index aa0edf0..e8e9f79 100644
--- a/google/appengine/tools/devappserver2/devappserver2.py
+++ b/google/appengine/tools/devappserver2/devappserver2.py
@@ -364,6 +364,17 @@
                          default=False,
                          help='enable XDebug remote debugging')
 
+  # App Identity
+  appidentity_group = parser.add_argument_group('Application Identity')
+  appidentity_group.add_argument(
+      '--appidentity_email_address',
+      help='email address associated with a service account that has a '
+      'downloadable key. May be None for no local application identity.')
+  appidentity_group.add_argument(
+      '--appidentity_private_key_path',
+      help='path to private key file associated with service account '
+      '(.pem format). Must be set if appidentity_email_address is set.')
+
   # Python
   python_group = parser.add_argument_group('Python')
   python_group.add_argument(
@@ -762,6 +773,10 @@
         # The "trusted" flag is only relevant for Google administrative
         # applications.
         trusted=getattr(options, 'trusted', False),
+        appidentity_email_address=options.appidentity_email_address,
+        appidentity_private_key_path=os.path.abspath(
+            options.appidentity_private_key_path)
+        if options.appidentity_private_key_path else None,
         blobstore_path=blobstore_path,
         datastore_path=datastore_path,
         datastore_consistency=consistency,
diff --git a/google/appengine/tools/devappserver2/dispatcher.py b/google/appengine/tools/devappserver2/dispatcher.py
index c548a83..ba32594 100644
--- a/google/appengine/tools/devappserver2/dispatcher.py
+++ b/google/appengine/tools/devappserver2/dispatcher.py
@@ -446,8 +446,8 @@
     """
     return self._get_module(module_name, version).get_num_instances()
 
-  def start_module(self, module_name, version):
-    """Starts a module.
+  def start_version(self, module_name, version):
+    """Starts a version of a module.
 
     Args:
       module_name: A str containing the name of the module.
@@ -461,8 +461,8 @@
     """
     self._get_module(module_name, version).resume()
 
-  def stop_module(self, module_name, version):
-    """Stops a module.
+  def stop_version(self, module_name, version):
+    """Stops a version of a module.
 
     Args:
       module_name: A str containing the name of the module.
diff --git a/google/appengine/tools/devappserver2/dispatcher_test.py b/google/appengine/tools/devappserver2/dispatcher_test.py
index 7595d14..a261f2b 100644
--- a/google/appengine/tools/devappserver2/dispatcher_test.py
+++ b/google/appengine/tools/devappserver2/dispatcher_test.py
@@ -48,7 +48,7 @@
     self.application = application
     self.module_name = module_name
     self.major_version = version
-    self.version_id = '%s.%s' % (version, '12345')
+    self.version_id = '%s:%s.%s' % (module_name, version, '12345')
     self.runtime = 'python27'
     self.threadsafe = False
     self.handlers = []
diff --git a/google/appengine/tools/devappserver2/endpoints/endpoints_server_regtest.py b/google/appengine/tools/devappserver2/endpoints/endpoints_server_regtest.py
deleted file mode 100644
index a8b7365..0000000
--- a/google/appengine/tools/devappserver2/endpoints/endpoints_server_regtest.py
+++ /dev/null
@@ -1,231 +0,0 @@
-#!/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.
-#
-"""Regression tests for Endpoints server in devappserver2."""
-
-
-import base64
-import json
-import os.path
-
-from google.testing.pybase import googletest
-
-from google.appengine.tools.devappserver2 import regtest_utils
-from google.appengine.tools.devappserver2.endpoints import endpoints_server
-
-
-class EndpointsServerRegtest(regtest_utils.BaseTestCase):
-  """Tests that the development server can correctly serve static content."""
-
-  def setUp(self):
-    super(EndpointsServerRegtest, self).setUp()
-    server_path = os.path.join(self.devappserver2_path,
-                               'endpoints/testdata/app.yaml')
-    self.start_server([server_path])
-
-  def test_rest_get(self):
-    """Test that a GET request to a REST API works."""
-    status, content, headers = self.fetch_url('default', 'GET',
-                                              '/_ah/api/test_service/v1/test')
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual({'text': 'Test response'}, response_json)
-
-  def test_rest_post(self):
-    """Test that a POST request to a REST API works."""
-    body = json.dumps({'name': 'MyName', 'number': 23})
-    send_headers = {'content-type': 'application/json'}
-    status, content, headers = self.fetch_url('default', 'POST',
-                                              '/_ah/api/test_service/v1/t2path',
-                                              body, send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual({'text': 'MyName 23'}, response_json)
-
-  def test_cors(self):
-    """Test that CORS headers are handled properly."""
-    send_headers = {'Origin': 'test.com',
-                    'Access-control-request-method': 'GET',
-                    'Access-Control-Request-Headers': 'Date,Expires'}
-    status, _, headers = self.fetch_url('default', 'GET',
-                                        '/_ah/api/test_service/v1/test',
-                                        headers=send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual(headers[endpoints_server._CORS_HEADER_ALLOW_ORIGIN],
-                     'test.com')
-    self.assertIn('GET',
-                  headers[endpoints_server._CORS_HEADER_ALLOW_METHODS].split(
-                      ','))
-    self.assertEqual(headers[endpoints_server._CORS_HEADER_ALLOW_HEADERS],
-                     'Date,Expires')
-
-  def test_rpc(self):
-    """Test that an RPC request works."""
-    body = json.dumps([{'jsonrpc': '2.0',
-                        'id': 'gapiRpc',
-                        'method': 'test_service.t2name',
-                        'params': {'name': 'MyName', 'number': 23},
-                        'apiVersion': 'v1'}])
-    send_headers = {'content-type': 'application-rpc'}
-    status, content, headers = self.fetch_url('default', 'POST',
-                                              '/_ah/api/rpc',
-                                              body, send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual([{'result': {'text': 'MyName 23'},
-                       'id': 'gapiRpc'}], response_json)
-
-  def test_echo_datetime_message(self):
-    """Test sending and receiving a datetime."""
-    body = json.dumps({'milliseconds': '5000', 'time_zone_offset': '60'})
-    send_headers = {'content-type': 'application/json'}
-    status, content, headers = self.fetch_url(
-        'default', 'POST', '/_ah/api/test_service/v1/echo_datetime_message',
-        body, send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual({'milliseconds': '5000', 'time_zone_offset': '60'},
-                     response_json)
-
-  def test_echo_datetime_field(self):
-    """Test sending and receiving a message that includes a datetime."""
-    body_json = {'datetime_value': '2013-03-13T15:29:37.883000+08:00'}
-    body = json.dumps(body_json)
-    send_headers = {'content-type': 'application/json'}
-    status, content, headers = self.fetch_url(
-        'default', 'POST', '/_ah/api/test_service/v1/echo_dt_field',
-        body, send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual(body_json, response_json)
-
-  def test_increment_integers(self):
-    """Test sending and receiving integer values."""
-    body_json = {'var_int32': 100, 'var_int64': '1000',
-                 'var_repeated_int64': ['10', '11', '900'],
-                 'var_sint64': -555, 'var_uint64': 4320}
-    body = json.dumps(body_json)
-    send_headers = {'content-type': 'application/json'}
-    status, content, headers = self.fetch_url(
-        'default', 'POST', '/_ah/api/test_service/v1/increment_integers',
-        body, send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    expected_response = {'var_int32': 101, 'var_int64': '1001',
-                         'var_repeated_int64': ['11', '12', '901'],
-                         'var_sint64': '-554', 'var_uint64': '4321'}
-    self.assertEqual(expected_response, response_json)
-
-  def test_echo_bytes(self):
-    """Test sending and receiving a BytesField parameter."""
-    value = 'This is a test of a message encoded as a BytesField.01234\000\001'
-    bytes_value = base64.urlsafe_b64encode(value)
-    body_json = {'bytes_value': bytes_value}
-    body = json.dumps(body_json)
-    send_headers = {'content-type': 'application/json'}
-    status, content, headers = self.fetch_url(
-        'default', 'POST', '/_ah/api/test_service/v1/echo_bytes',
-        body, send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual(response_json, body_json)
-    self.assertEqual(value, base64.urlsafe_b64decode(body_json['bytes_value']))
-
-  def test_empty_test(self):
-    """Test that an empty response that should have an object returns 200."""
-    status, content, headers = self.fetch_url(
-        'default', 'GET', '/_ah/api/test_service/v1/empty_test')
-    self.assertEqual(200, status)
-    self.assertEqual('2', headers['Content-Length'])
-    self.assertEqual('{}', content)
-
-  def test_empty_response(self):
-    """An empty response that should be empty should return 204."""
-    status, content, headers = self.fetch_url(
-        'default', 'GET', '/_ah/api/test_service/v1/empty_response')
-    self.assertEqual(204, status)
-    self.assertEqual('0', headers['Content-Length'])
-    self.assertEqual('', content)
-
-  def test_discovery_config(self):
-    """Test that the discovery configuration looks right."""
-    status, content, headers = self.fetch_url(
-        'default', 'GET', '/_ah/api/discovery/v1/apis/test_service/v1/rest')
-    self.assertEqual(200, status)
-    self.assertEqual('application/json; charset=UTF-8', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertRegexpMatches(
-        response_json['baseUrl'],
-        r'^http://localhost(:\d+)?/_ah/api/test_service/v1/$')
-    self.assertRegexpMatches(response_json['rootUrl'],
-                             r'^http://localhost(:\d+)?/_ah/api/$')
-
-  def test_multiclass_rest_get(self):
-    """Test that a GET request to a second class in the REST API works."""
-    status, content, headers = self.fetch_url(
-        'default', 'GET', '/_ah/api/test_service/v1/extrapath/test')
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual({'text': 'Extra test response'}, response_json)
-
-  def test_multiclass_rpc(self):
-    """Test that an RPC request to a second class in the API works."""
-    body = json.dumps([{'jsonrpc': '2.0',
-                        'id': 'gapiRpc',
-                        'method': 'test_service.extraname.test',
-                        'params': {},
-                        'apiVersion': 'v1'}])
-    send_headers = {'content-type': 'application-rpc'}
-    status, content, headers = self.fetch_url('default', 'POST',
-                                              '/_ah/api/rpc',
-                                              body, send_headers)
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual([{'result': {'text': 'Extra test response'},
-                       'id': 'gapiRpc'}], response_json)
-
-  def test_second_api_no_collision(self):
-    """Test that a GET request to a second similar API works."""
-    status, content, headers = self.fetch_url('default', 'GET',
-                                              '/_ah/api/second_service/v1/test')
-    self.assertEqual(200, status)
-    self.assertEqual('application/json', headers['Content-Type'])
-
-    response_json = json.loads(content)
-    self.assertEqual({'text': 'Second response'}, response_json)
-
-
-if __name__ == '__main__':
-  googletest.main()
diff --git a/google/appengine/tools/devappserver2/file_watcher.py b/google/appengine/tools/devappserver2/file_watcher.py
index c4f973f..b0efcdc 100644
--- a/google/appengine/tools/devappserver2/file_watcher.py
+++ b/google/appengine/tools/devappserver2/file_watcher.py
@@ -17,6 +17,7 @@
 """Monitors a directory tree for changes."""
 
 
+import logging
 import sys
 import types
 
@@ -26,11 +27,20 @@
 
 
 class _MultipleFileWatcher(object):
-  """A FileWatcher than can watch many directories."""
+  """A FileWatcher that combines multiple file watchers.
 
-  def __init__(self, directories, use_mtime_file_watcher):
-    self._file_watchers = [get_file_watcher([directory], use_mtime_file_watcher)
-                           for directory in directories]
+  For file watchers that can only watch a single directory, this class can
+  manage multiple watchers and allows the caller to treat them as a single
+  unit.
+  """
+
+  def __init__(self, watchers):
+    """Initialize a MultipleFileWatcher instance.
+
+    Args:
+      watchers: a list of watchers to treat as a single watcher.
+    """
+    self._file_watchers = watchers
 
   def start(self):
     for watcher in self._file_watchers:
@@ -47,6 +57,60 @@
     return any([watcher.has_changes() for watcher in self._file_watchers])
 
 
+def _create_watcher(directories, watcher_class):
+  """Creates the best watcher based on multiple directory support.
+
+  For file watchers that can support multiple directories, directly instantiate
+  an instance passing in an iterable of directories names. For file watchers
+  that only support single directories, instantiate one directly if there is
+  only a single directory to watch or wrap them in a MultipleFileWatcher if
+  there are multiple directories to watch.
+
+  Args:
+    directories: an iterable of all the directories to watch.
+    watcher_class: a callable that creates the per-directory FileWatcher
+      instance. Must be callable with a single item of the type held by
+      directories.
+
+  Returns:
+    A FileWatcher appropriate for the list of directories.
+  """
+  if watcher_class.SUPPORTS_MULTIPLE_DIRECTORIES:
+    return watcher_class(directories)
+  elif len(directories) == 1:
+    return watcher_class(directories[0])
+  else:
+    return _MultipleFileWatcher([watcher_class(d) for d in directories])
+
+
+def _create_linux_watcher(directories):
+  """Create a watcher for Linux.
+
+  While we prefer InotifyFileWatcher for Linux, there are only a limited number
+  of inotify instances available per user (for example, 128 on a Goobuntu 12.04
+  install). Try to create an InotifyFileWatcher but fall back on
+  MTimeFileWatcher if the user is out of resources.
+
+  Args:
+    directories: A list representing the paths of the directories to monitor.
+
+  Returns:
+    An InotifyFileWatcher if the user has available resources and an
+    MtimeFileWatcher if not.
+  """
+
+  # TODO: develop a way to check if the filesystem supports inotify.
+  # (for example, NFS does not) and also use MTimeFileWatcher in that case.
+
+  try:
+    return _create_watcher(directories,
+                           inotify_file_watcher.InotifyFileWatcher)
+  except OSError as e:
+    logging.warning('Could not create InotifyFileWatcher;'
+                    ' falling back to MTimeFileWatcher: %s', e)
+    return _create_watcher(directories, mtime_file_watcher.MtimeFileWatcher)
+
+
 def get_file_watcher(directories, use_mtime_file_watcher):
   """Returns an instance that monitors a hierarchy of directories.
 
@@ -61,17 +125,15 @@
     before has_changes().
   """
   assert not isinstance(directories, types.StringTypes), 'expected list got str'
-  if len(directories) != 1:
-    return _MultipleFileWatcher(directories, use_mtime_file_watcher)
 
-  directory = directories[0]
   if use_mtime_file_watcher:
-    return mtime_file_watcher.MtimeFileWatcher(directory)
+    return _create_watcher(directories, mtime_file_watcher.MtimeFileWatcher)
   elif sys.platform.startswith('linux'):
-    return inotify_file_watcher.InotifyFileWatcher(directory)
+    return _create_linux_watcher(directories)
   elif sys.platform.startswith('win'):
-    return win32_file_watcher.Win32FileWatcher(directory)
-  return mtime_file_watcher.MtimeFileWatcher(directory)
+    return _create_watcher(directories, win32_file_watcher.Win32FileWatcher)
+  else:
+    return _create_watcher(directories, mtime_file_watcher.MtimeFileWatcher)
 
   # NOTE: The Darwin-specific watcher implementation (found in the deleted file
   # fsevents_file_watcher.py) was incorrect - the Mac OS X FSEvents
diff --git a/google/appengine/tools/devappserver2/gcs_application.py b/google/appengine/tools/devappserver2/gcs_application.py
index cd87980..578546e 100644
--- a/google/appengine/tools/devappserver2/gcs_application.py
+++ b/google/appengine/tools/devappserver2/gcs_application.py
@@ -36,6 +36,15 @@
   """A WSGI application that forwards GCS requests to stub."""
 
   def __call__(self, environ, start_response):
+    """Handles WSGI requests.
+
+    Args:
+      environ: An environ dict for the current request as defined in PEP-333.
+      start_response: A function with semantics defined in PEP-333.
+
+    Returns:
+      An iterable over strings containing the body of the HTTP response.
+    """
     request = webob.Request(environ)
 
     try:
diff --git a/google/appengine/tools/devappserver2/go_application.py b/google/appengine/tools/devappserver2/go_application.py
index 58988b1..6f2f3d7 100644
--- a/google/appengine/tools/devappserver2/go_application.py
+++ b/google/appengine/tools/devappserver2/go_application.py
@@ -90,7 +90,8 @@
     """Return the environment that used be used to run the Go executable."""
     environ = {'GOROOT': _GOROOT,
                'PWD': self._module_configuration.application_root,
-               'TZ': 'UTC'}
+               'TZ': 'UTC',
+               'RUN_WITH_DEVAPPSERVER': '1'}
     if 'SYSTEMROOT' in os.environ:
       environ['SYSTEMROOT'] = os.environ['SYSTEMROOT']
     if 'USER' in os.environ:
@@ -227,6 +228,11 @@
           otherwise. This argument is used to decide whether a build is Required
           or not.
 
+    Returns:
+      True if compilation was successfully performed (will raise
+        an exception if compilation was attempted but failed).
+      False if compilation was not attempted.
+
     Raises:
       BuildError: if building the executable fails for any reason.
     """
@@ -240,7 +246,7 @@
       raise BuildError('Required Go components are missing from the SDK.')
 
     if self._go_executable and not maybe_modified_since_last_build:
-      return
+      return False
 
     (self._go_file_to_mtime,
      old_go_file_to_mtime) = (self._get_go_files_to_mtime(),
@@ -256,7 +262,7 @@
     if (self._go_executable and
         self._go_file_to_mtime == old_go_file_to_mtime and
         self._extras_hash == old_extras_hash):
-      return
+      return False
 
     if self._go_file_to_mtime != old_go_file_to_mtime:
       logging.debug('Rebuilding Go application due to source modification')
@@ -265,3 +271,4 @@
     else:
       logging.debug('Building Go application')
     self._build()
+    return True
diff --git a/google/appengine/tools/devappserver2/go_runtime.py b/google/appengine/tools/devappserver2/go_runtime.py
index e174b04..2d44ae8 100644
--- a/google/appengine/tools/devappserver2/go_runtime.py
+++ b/google/appengine/tools/devappserver2/go_runtime.py
@@ -103,6 +103,7 @@
     self._go_application = go_application.GoApplication(
         self._module_configuration)
     self._modified_since_last_build = False
+    self._last_build_error = None
 
   def get_restart_directories(self):
     """Returns a list of directories changes in which should trigger a restart.
@@ -161,17 +162,26 @@
 
     with self._application_lock:
       try:
-        self._go_application.maybe_build(self._modified_since_last_build)
+        if self._go_application.maybe_build(self._modified_since_last_build):
+          if self._last_build_error:
+            logging.info('Go application successfully built.')
+          self._last_build_error = None
       except go_application.BuildError as e:
         logging.error('Failed to build Go application: %s', e)
-        proxy = _GoBuildFailureRuntimeProxy(e)
+        # Deploy a failure proxy now and each time a new instance is requested.
+        self._last_build_error = e
+
+      self._modified_since_last_build = False
+
+      if self._last_build_error:
+        logging.debug('Deploying new instance of failure proxy.')
+        proxy = _GoBuildFailureRuntimeProxy(self._last_build_error)
       else:
         proxy = http_runtime.HttpRuntimeProxy(
             self._go_application.go_executable,
             instance_config_getter,
             self._module_configuration,
             self._go_application.get_environment())
-      self._modified_since_last_build = False
 
     return instance.Instance(self.request_data,
                              instance_id,
diff --git a/google/appengine/tools/devappserver2/inotify_file_watcher.py b/google/appengine/tools/devappserver2/inotify_file_watcher.py
index 3ea2f38..490d137 100644
--- a/google/appengine/tools/devappserver2/inotify_file_watcher.py
+++ b/google/appengine/tools/devappserver2/inotify_file_watcher.py
@@ -47,34 +47,59 @@
 _INTERESTING_INOTIFY_EVENTS = (
     IN_ATTRIB|IN_MODIFY|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE)
 
+# inotify only available on Linux and a ctypes.CDLL will raise if code tries to
+# specify the arg types or return type for a non-existent function.
+if sys.platform.startswith('linux'):
+  _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
+  _libc.inotify_init.argtypes = []
+  _libc.inotify_init.restype = ctypes.c_int
+  _libc.inotify_add_watch.argtypes = [ctypes.c_int,
+                                      ctypes.c_char_p,
+                                      ctypes.c_uint32]
+  _libc.inotify_add_watch.restype = ctypes.c_int
+  _libc.inotify_rm_watch.argtypes = [ctypes.c_int,
+                                     ctypes.c_int]
+  _libc.inotify_rm_watch.restype = ctypes.c_int
+else:
+  _libc = None
+
 
 class InotifyFileWatcher(object):
   """Monitors a directory tree for changes using inotify."""
 
-  _libc = None
+  SUPPORTS_MULTIPLE_DIRECTORIES = True
 
-  def __init__(self, directory):
+  def __init__(self, directories):
     """Initializer for InotifyFileWatcher.
 
     Args:
-      directory: A string representing the path to a directory that should
-          be monitored for changes i.e. files and directories added, renamed,
-          deleted or changed.
+      directories: An iterable of strings representing the path to a directory
+          that should be monitored for changes i.e. files and directories added,
+          renamed, deleted or changed.
+
+    Raises:
+      OSError: if there are no inotify instances available.
     """
-    self._directory = os.path.abspath(directory)
+    assert _libc is not None, 'InotifyFileWatcher only available on Linux.'
+    self._directories = [os.path.abspath(d) for d in directories]
     self._watch_to_directory = {}
     self._directory_to_watch_descriptor = {}
     self._directory_to_subdirs = {}
     self._inotify_events = ''
-    self._inotify_fd = None
-    self._inotify_poll = None
+    self._inotify_fd = _libc.inotify_init()
+    if self._inotify_fd < 0:
+      error = OSError('failed call to inotify_init')
+      error.errno = ctypes.get_errno()
+      error.strerror = errno.errorcode[ctypes.get_errno()]
+      raise error
+    self._inotify_poll = select.poll()
 
 
   def _remove_watch_for_path(self, path):
     logging.debug('_remove_watch_for_path(%r)', path)
     wd = self._directory_to_watch_descriptor[path]
 
-    if InotifyFileWatcher._libc.inotify_rm_watch(self._inotify_fd, wd) < 0:
+    if _libc.inotify_rm_watch(self._inotify_fd, wd) < 0:
       # If the directory is deleted then the watch will removed automatically
       # and inotify_rm_watch will fail. Just log the error.
       logging.debug('inotify_rm_watch failed for %r: %d [%r]',
@@ -108,7 +133,7 @@
         # empty string for symlinks :-(
         parent_path = os.path.dirname(directory_path)
 
-        watch_descriptor = InotifyFileWatcher._libc.inotify_add_watch(
+        watch_descriptor = _libc.inotify_add_watch(
             self._inotify_fd,
             ctypes.create_string_buffer(directory_path),
             _INTERESTING_INOTIFY_EVENTS)
@@ -134,17 +159,9 @@
 
   def start(self):
     """Start watching the directory for changes."""
-    self._class_setup()
-
-    self._inotify_fd = InotifyFileWatcher._libc.inotify_init()
-    if self._inotify_fd < 0:
-      error = OSError('failed call to inotify_init')
-      error.errno = ctypes.get_errno()
-      error.strerror = errno.errorcode[ctypes.get_errno()]
-      raise error
-    self._inotify_poll = select.poll()
     self._inotify_poll.register(self._inotify_fd, select.POLLIN)
-    self._add_watch_for_path(self._directory)
+    for directory in self._directories:
+      self._add_watch_for_path(directory)
 
   def quit(self):
     """Stop watching the directory for changes."""
@@ -204,20 +221,3 @@
 
   def has_changes(self):
     return bool(self._get_changed_paths())
-
-  @classmethod
-  def _class_setup(cls):
-    if cls._libc:
-      return
-
-    libc_name = ctypes.util.find_library('c')
-    cls._libc = ctypes.CDLL(libc_name, use_errno=True)
-    cls._libc.inotify_init.argtypes = []
-    cls._libc.inotify_init.restype = ctypes.c_int
-    cls._libc.inotify_add_watch.argtypes = [ctypes.c_int,
-                                            ctypes.c_char_p,
-                                            ctypes.c_uint32]
-    cls._libc.inotify_add_watch.restype = ctypes.c_int
-    cls._libc.inotify_rm_watch.argtypes = [ctypes.c_int,
-                                           ctypes.c_int]
-    cls._libc.inotify_rm_watch.restype = ctypes.c_int
diff --git a/google/appengine/tools/devappserver2/inotify_file_watcher_test.py b/google/appengine/tools/devappserver2/inotify_file_watcher_test.py
index 2af45b1..2795921 100644
--- a/google/appengine/tools/devappserver2/inotify_file_watcher_test.py
+++ b/google/appengine/tools/devappserver2/inotify_file_watcher_test.py
@@ -35,7 +35,7 @@
   def setUp(self):
     self._directory = tempfile.mkdtemp()  # The watched directory
     self._junk_directory = tempfile.mkdtemp()  # A scrap directory.
-    self._watcher = inotify_file_watcher.InotifyFileWatcher(self._directory)
+    self._watcher = inotify_file_watcher.InotifyFileWatcher([self._directory])
     logging.debug('watched directory=%r, junk directory=%r',
                   self._directory, self._junk_directory)
 
@@ -200,7 +200,7 @@
         set([path]),
         self._watcher._get_changed_paths())
 
-  def test_symlink(self):
+  def test_symlink_directory(self):
     sym_target = os.path.join(self._directory, 'test')
     os.mkdir(os.path.join(self._junk_directory, 'subdir'))
     self._watcher.start()
@@ -218,6 +218,13 @@
         set([os.path.join(self._directory, 'test', 'file1')]),
         self._watcher._get_changed_paths())
 
+    # Check that modifying the file in the symlinked directory is reported.
+    with open(os.path.join(self._junk_directory, 'file1'), 'w') as fp:
+      fp.write('some data')
+    self.assertEqual(
+        set([os.path.join(self._directory, 'test', 'file1')]),
+        self._watcher._get_changed_paths())
+
     # Check that a removed symlinked directory is reported.
     os.remove(sym_target)
     self.assertEqual(
@@ -232,6 +239,33 @@
         set(),
         self._watcher._get_changed_paths())
 
+  @unittest.skip('b/11896748')
+  def test_symlink_file(self):
+    actual_file = os.path.join(self._junk_directory, 'moo')
+    with open(actual_file, 'w'):
+      pass
+    symbolic_link = os.path.join(self._directory, 'moo')
+    self._watcher.start()
+
+    # Check that symlinking a file into watched directory is reported.
+    os.symlink(actual_file, symbolic_link)
+    self.assertEqual(
+        set([symbolic_link]),
+        self._watcher._get_changed_paths())
+
+    # Check that modifying the source file is reported.
+    with open(actual_file, 'w') as fp:
+      fp.write('some data')
+    self.assertEqual(
+        set([symbolic_link]),
+        self._watcher._get_changed_paths())
+
+    # Check that deleting the source file is reported.
+    os.unlink(actual_file)
+    self.assertEqual(
+        set([symbolic_link]),
+        self._watcher._get_changed_paths())
+
   def test_many_directories(self):
     # Linux supports a limited number of watches per file descriptor. The
     # default is 8192 (i.e. 2^13).
@@ -242,5 +276,46 @@
         set([path]),
         self._watcher._get_changed_paths())
 
+
+@unittest.skipUnless(sys.platform.startswith('linux'), 'requires linux')
+class TestInotifyFileWatcherMultipleDirectories(unittest.TestCase):
+  """Tests for inotify_file_watcher.InotifyFileWatcher."""
+
+  def setUp(self):
+    self._directories = [tempfile.mkdtemp() for _ in range(4)]
+    self._watcher = inotify_file_watcher.InotifyFileWatcher(self._directories)
+    self._watcher.start()
+
+  def tearDown(self):
+    self._watcher.quit()
+    for directory in self._directories:
+      shutil.rmtree(directory)
+
+  @staticmethod
+  def _create_file(*paths):
+    realpath = os.path.realpath(os.path.join(*paths))
+    with open(realpath, 'w'):
+      pass
+    return realpath
+
+  def testInDir0(self):
+    path = self._create_file(self._directories[0], 'moo')
+    self.assertEqual(
+        set([path]),
+        self._watcher._get_changed_paths())
+
+  def testInDir2(self):
+    path = self._create_file(self._directories[2], 'moo')
+    self.assertEqual(
+        set([path]),
+        self._watcher._get_changed_paths())
+
+  def testInDir1And3(self):
+    path1 = self._create_file(self._directories[1], 'moo')
+    path3 = self._create_file(self._directories[3], 'moo')
+    self.assertEqual(
+        set([path1, path3]),
+        self._watcher._get_changed_paths())
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/google/appengine/tools/devappserver2/module.py b/google/appengine/tools/devappserver2/module.py
index 5bd09b0..9a9589e 100644
--- a/google/appengine/tools/devappserver2/module.py
+++ b/google/appengine/tools/devappserver2/module.py
@@ -43,7 +43,6 @@
 from google.appengine.tools.devappserver2 import blob_upload
 from google.appengine.tools.devappserver2 import channel
 from google.appengine.tools.devappserver2 import constants
-
 from google.appengine.tools.devappserver2 import endpoints
 from google.appengine.tools.devappserver2 import errors
 from google.appengine.tools.devappserver2 import file_watcher
@@ -144,7 +143,6 @@
   """The abstract base for all instance pool implementations."""
 
   _RUNTIME_INSTANCE_FACTORIES = {
-
       'go': go_runtime.GoRuntimeInstanceFactory,
       'php': php_runtime.PHPRuntimeInstanceFactory,
       'python': python_runtime.PythonRuntimeInstanceFactory,
@@ -265,9 +263,6 @@
     runtime_config = runtime_config_pb2.Config()
     runtime_config.app_id = self._module_configuration.application
     runtime_config.version_id = self._module_configuration.version_id
-    if self._module_configuration.module_name:
-      runtime_config.version_id = '%s:%s' % (
-          self._module_configuration.module_name, runtime_config.version_id)
     if self._threadsafe_override is None:
       runtime_config.threadsafe = self._module_configuration.threadsafe or False
     else:
@@ -589,7 +584,7 @@
             user_request_id=environ['REQUEST_LOG_ID'],
             ip=environ.get('REMOTE_ADDR', ''),
             app_id=self._module_configuration.application,
-            version_id=self._module_configuration.version_id,
+            version_id=self._module_configuration.major_version,
             nickname=email.split('@', 1)[0],
             user_agent=environ.get('HTTP_USER_AGENT', ''),
             host=hostname,
@@ -704,7 +699,7 @@
 
     Returns:
       A string suitable for use as a REQUEST_LOG_ID. The returned string is
-      variable length to emulate the the production values, which encapsulate
+      variable length to emulate the production values, which encapsulate
       the application id, version and some log state.
     """
     return ''.join(random.choice(_LOWER_HEX_DIGITS)
@@ -721,6 +716,8 @@
 
     Args:
       instances: An int containing the number of instances to run.
+    Raises:
+      request_info.NotSupportedWithAutoScalingError: Always.
     """
     raise request_info.NotSupportedWithAutoScalingError()
 
@@ -1519,9 +1516,8 @@
     if self._module_configuration.is_backend:
       environ['BACKEND_ID'] = self._module_configuration.module_name
     else:
-      environ['BACKEND_ID'] = appinfo.MODULE_SEPARATOR.join([
-          self._module_configuration.module_name,
-          self._module_configuration.version_id.split('.', 1)[0]])
+      environ['BACKEND_ID'] = (
+          self._module_configuration.version_id.split('.', 1)[0])
     if inst is not None:
       return self._handle_instance_request(
           environ, start_response, url_map, match, request_id, inst,
@@ -1669,7 +1665,7 @@
     """Suspends serving for this module, quitting all running instances."""
     with self._instances_change_lock:
       if self._suspended:
-        raise request_info.ModuleAlreadyStoppedError()
+        raise request_info.VersionAlreadyStoppedError()
       self._suspended = True
       with self._condition:
         instances_to_stop = zip(self._instances, self._wsgi_servers)
@@ -1689,7 +1685,7 @@
     """Resumes serving for this module."""
     with self._instances_change_lock:
       if not self._suspended:
-        raise request_info.ModuleAlreadyStartedError()
+        raise request_info.VersionAlreadyStartedError()
       self._suspended = False
       with self._condition:
         if self._quit_event.is_set():
@@ -1711,7 +1707,7 @@
       self._async_start_instance(wsgi_servr, inst)
 
   def restart(self):
-    """Restarts the the module, replacing all running instances."""
+    """Restarts the module, replacing all running instances."""
     with self._instances_change_lock:
       with self._condition:
         if self._quit_event.is_set():
@@ -2027,9 +2023,8 @@
     if self._module_configuration.is_backend:
       environ['BACKEND_ID'] = self._module_configuration.module_name
     else:
-      environ['BACKEND_ID'] = appinfo.MODULE_SEPARATOR.join([
-          self._module_configuration.module_name,
-          self._module_configuration.version_id.split('.', 1)[0]])
+      environ['BACKEND_ID'] = (
+          self._module_configuration.version_id.split('.', 1)[0])
     if inst is not None:
       return self._handle_instance_request(
           environ, start_response, url_map, match, request_id, inst,
@@ -2172,7 +2167,7 @@
     self._async_shutdown_instance(inst, wsgi_servr.port)
 
   def restart(self):
-    """Restarts the the module, replacing all running instances."""
+    """Restarts the module, replacing all running instances."""
     instances_to_stop = []
     instances_to_start = []
     with self._condition:
@@ -2377,7 +2372,7 @@
       return ['The command timed-out while waiting for another one to complete']
 
   def restart(self):
-    """Restarts the the module."""
+    """Restarts the module."""
     with self._inst_lock:
       if self._inst:
         self._inst.quit(force=True)
diff --git a/google/appengine/tools/devappserver2/module_test.py b/google/appengine/tools/devappserver2/module_test.py
index ac1c161..a350cd0 100644
--- a/google/appengine/tools/devappserver2/module_test.py
+++ b/google/appengine/tools/devappserver2/module_test.py
@@ -70,7 +70,7 @@
     self.handlers = handlers
     self.normalized_libraries = normalized_libraries or []
     self.env_variables = env_variables or []
-    self.version_id = '%s.%s' % (version, '12345')
+    self.version_id = '%s:%s.%s' % (module_name, version, '12345')
     self.is_backend = False
 
   def check_for_updates(self):
@@ -1602,11 +1602,11 @@
 
   def test_already_suspended(self):
     self.module._suspended = True
-    self.assertRaises(request_info.ModuleAlreadyStoppedError,
+    self.assertRaises(request_info.VersionAlreadyStoppedError,
                       self.module.suspend)
 
   def test_already_resumed(self):
-    self.assertRaises(request_info.ModuleAlreadyStartedError,
+    self.assertRaises(request_info.VersionAlreadyStartedError,
                       self.module.resume)
 
   def test_suspend_instance(self):
diff --git a/google/appengine/tools/devappserver2/mtime_file_watcher.py b/google/appengine/tools/devappserver2/mtime_file_watcher.py
index b705ffe..27e0dff 100644
--- a/google/appengine/tools/devappserver2/mtime_file_watcher.py
+++ b/google/appengine/tools/devappserver2/mtime_file_watcher.py
@@ -26,6 +26,9 @@
 class MtimeFileWatcher(object):
   """Monitors a directory tree for changes using mtime polling."""
 
+  # TODO: evaluate whether we can directly support multiple directories.
+  SUPPORTS_MULTIPLE_DIRECTORIES = False
+
   def __init__(self, directory):
     self._directory = directory
     self._quit_event = threading.Event()
diff --git a/google/appengine/tools/devappserver2/php/runtime.py b/google/appengine/tools/devappserver2/php/runtime.py
index d1b8e61..6ec8852 100644
--- a/google/appengine/tools/devappserver2/php/runtime.py
+++ b/google/appengine/tools/devappserver2/php/runtime.py
@@ -106,8 +106,9 @@
 
     # Modify the SCRIPT_FILENAME to specify the setup script that readies the
     # PHP environment. Put the user script in REAL_SCRIPT_FILENAME.
-    user_environ['REAL_SCRIPT_FILENAME'] = environ[
-        http_runtime_constants.SCRIPT_HEADER]
+    user_environ['REAL_SCRIPT_FILENAME'] = os.path.normpath(
+        os.path.join(self.config.application_root,
+                     environ[http_runtime_constants.SCRIPT_HEADER]))
     user_environ['SCRIPT_FILENAME'] = SETUP_PHP_PATH
     user_environ['REMOTE_REQUEST_ID'] = environ[
         http_runtime_constants.REQUEST_ID_ENVIRON]
diff --git a/google/appengine/tools/devappserver2/php/setup.php b/google/appengine/tools/devappserver2/php/setup.php
index 76f72d0..ddf8abd 100644
--- a/google/appengine/tools/devappserver2/php/setup.php
+++ b/google/appengine/tools/devappserver2/php/setup.php
@@ -1,5 +1,8 @@
 <?php
 
+// Ensure that the class autoloader is the first include.
+require_once 'google/appengine/runtime/autoloader.php';
+
 function _gae_syslog($priority, $format_string, $message) {
   // TODO(bquinlan): Use the logs service to persist this message.
 }
diff --git a/google/appengine/tools/devappserver2/python/sandbox.py b/google/appengine/tools/devappserver2/python/sandbox.py
index 4a6aba5..d89dc90 100644
--- a/google/appengine/tools/devappserver2/python/sandbox.py
+++ b/google/appengine/tools/devappserver2/python/sandbox.py
@@ -146,7 +146,6 @@
   sys.meta_path = [
       StubModuleImportHook(),
       ModuleOverrideImportHook(_MODULE_OVERRIDE_POLICIES),
-      BuiltinImportHook(),
       CModuleImportHook(enabled_library_regexes),
       path_override_hook,
       PyCryptoRandomImportHook,
@@ -295,7 +294,7 @@
     Returns:
       A tuple (source_file, path_name, description, loader) where:
         source_file: An open file or None.
-        path_name: A str containing the the path to the module.
+        path_name: A str containing the path to the module.
         description: A description tuple like the one imp.find_module returns.
         loader: A PEP 302 compatible path hook. If this is not None, then the
             other elements will be None.
@@ -815,26 +814,8 @@
 ]
 
 
-class BuiltinImportHook(object):
-  """An import hook implementing a builtin whitelist.
-
-  BuiltinImportHook implements the PEP 302 finder protocol where it returns
-  itself as a loader for any builtin module that isn't whitelisted. The loader
-  implementation always raises ImportError.
-  """
-
-  def find_module(self, fullname, unused_path=None):
-    if (fullname in sys.builtin_module_names and
-        fullname not in _WHITE_LIST_C_MODULES):
-      return self
-    return None
-
-  def load_module(self, fullname):
-    raise ImportError('No module named %s' % fullname)
-
-
 class CModuleImportHook(object):
-  """An import hook implementing a C module whitelist.
+  """An import hook implementing a C module (builtin or extensions) whitelist.
 
   CModuleImportHook implements the PEP 302 finder protocol where it returns
   itself as a loader for any builtin module that isn't whitelisted or part of an
@@ -845,21 +826,23 @@
   def __init__(self, enabled_regexes):
     self._enabled_regexes = enabled_regexes
 
-  def find_module(self, fullname, path=None):
-    if fullname in _WHITE_LIST_C_MODULES:
-      return None
-    if any(regex.match(fullname) for regex in self._enabled_regexes):
-      return None
+  @staticmethod
+  def _module_type(fullname, path):
     _, _, submodule_name = fullname.rpartition('.')
     try:
-      result = imp.find_module(submodule_name, path)
+      f, _, description = imp.find_module(submodule_name, path)
+      _, _, file_type = description
     except ImportError:
       return None
-    f, _, description = result
-    _, _, file_type = description
-    if isinstance(f, file):
+    if f:
       f.close()
-    if file_type == imp.C_EXTENSION:
+    return file_type
+
+  def find_module(self, fullname, path=None):
+    if (fullname in _WHITE_LIST_C_MODULES or
+        any(regex.match(fullname) for regex in self._enabled_regexes)):
+      return None
+    if self._module_type(fullname, path) in [imp.C_EXTENSION, imp.C_BUILTIN]:
       return self
     return None
 
diff --git a/google/appengine/tools/devappserver2/python/sandbox_test.py b/google/appengine/tools/devappserver2/python/sandbox_test.py
index efec593..93e9584 100644
--- a/google/appengine/tools/devappserver2/python/sandbox_test.py
+++ b/google/appengine/tools/devappserver2/python/sandbox_test.py
@@ -260,31 +260,25 @@
     self.assertEqual(hook, hook.find_module('lxml.etree', lxml.__path__))
 
   def test_find_module_not_c_module(self):
-    hook = sandbox.BuiltinImportHook()
+    hook = sandbox.CModuleImportHook([])
     self.assertIsNone(hook.find_module('httplib'))
 
-  def test_load_module(self):
-    hook = sandbox.CModuleImportHook([])
-    self.assertRaises(ImportError, hook.load_module, 'lxml')
-
-
-class BuiltinImportHookTest(unittest.TestCase):
   def test_find_module_whitelisted(self):
-    hook = sandbox.BuiltinImportHook()
+    hook = sandbox.CModuleImportHook([])
     for name in sandbox._WHITE_LIST_C_MODULES:
       self.assertIsNone(hook.find_module(name))
 
   def test_find_module_not_whitelisted(self):
-    hook = sandbox.BuiltinImportHook()
+    hook = sandbox.CModuleImportHook([])
     self.assertEqual(hook, hook.find_module('__builtin__'))
 
-  def test_find_module_not_builtin(self):
-    hook = sandbox.BuiltinImportHook()
-    self.assertIsNone(hook.find_module('httplib'))
+  def test_find_module_not_whitelisted_enabled_via_libaries(self):
+    hook = sandbox.CModuleImportHook([re.compile(r'__builtin__')])
+    self.assertIsNone(hook.find_module('__builtin__'))
 
   def test_load_module(self):
-    hook = sandbox.BuiltinImportHook()
-    self.assertRaises(ImportError, hook.load_module, '__builtin__')
+    hook = sandbox.CModuleImportHook([])
+    self.assertRaises(ImportError, hook.load_module, 'lxml')
 
 
 class PathOverrideImportHookTest(unittest.TestCase):
diff --git a/google/appengine/tools/devappserver2/runtime_config_pb2.py b/google/appengine/tools/devappserver2/runtime_config_pb2.py
index 71a00a4..28fae40 100644
--- a/google/appengine/tools/devappserver2/runtime_config_pb2.py
+++ b/google/appengine/tools/devappserver2/runtime_config_pb2.py
@@ -17,6 +17,8 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: apphosting/tools/devappserver2/runtime_config.proto
 
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.net.proto2.python.public import descriptor as _descriptor
 from google.net.proto2.python.public import message as _message
 from google.net.proto2.python.public import reflection as _reflection
@@ -29,7 +31,8 @@
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='apphosting/tools/devappserver2/runtime_config.proto',
   package='apphosting.tools.devappserver2',
-  serialized_pb='\n3apphosting/tools/devappserver2/runtime_config.proto\x12\x1e\x61pphosting.tools.devappserver2\"\xf2\x04\n\x06\x43onfig\x12\x0e\n\x06\x61pp_id\x18\x01 \x02(\x0c\x12\x12\n\nversion_id\x18\x02 \x02(\x0c\x12\x18\n\x10\x61pplication_root\x18\x03 \x02(\x0c\x12\x19\n\nthreadsafe\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x08\x61pi_host\x18\x11 \x01(\t:\tlocalhost\x12\x10\n\x08\x61pi_port\x18\x05 \x02(\x05\x12:\n\tlibraries\x18\x06 \x03(\x0b\x32\'.apphosting.tools.devappserver2.Library\x12\x16\n\nskip_files\x18\x07 \x01(\t:\x02^$\x12\x18\n\x0cstatic_files\x18\x08 \x01(\t:\x02^$\x12\x43\n\rpython_config\x18\x0e \x01(\x0b\x32,.apphosting.tools.devappserver2.PythonConfig\x12=\n\nphp_config\x18\t \x01(\x0b\x32).apphosting.tools.devappserver2.PhpConfig\x12\x38\n\x07\x65nviron\x18\n \x03(\x0b\x32\'.apphosting.tools.devappserver2.Environ\x12\x42\n\x10\x63loud_sql_config\x18\x0b \x01(\x0b\x32(.apphosting.tools.devappserver2.CloudSQL\x12\x12\n\ndatacenter\x18\x0c \x02(\t\x12\x13\n\x0binstance_id\x18\r \x02(\t\x12\x1b\n\x10stderr_log_level\x18\x0f \x01(\x03:\x01\x31\x12\x13\n\x0b\x61uth_domain\x18\x10 \x02(\t\x12\x15\n\rmax_instances\x18\x12 \x01(\x05\"A\n\tPhpConfig\x12\x1b\n\x13php_executable_path\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65nable_debugger\x18\x03 \x02(\x08\"<\n\x0cPythonConfig\x12\x16\n\x0estartup_script\x18\x01 \x01(\t\x12\x14\n\x0cstartup_args\x18\x02 \x01(\t\"t\n\x08\x43loudSQL\x12\x12\n\nmysql_host\x18\x01 \x02(\t\x12\x12\n\nmysql_port\x18\x02 \x02(\x05\x12\x12\n\nmysql_user\x18\x03 \x02(\t\x12\x16\n\x0emysql_password\x18\x04 \x02(\t\x12\x14\n\x0cmysql_socket\x18\x05 \x01(\t\"(\n\x07Library\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t\"%\n\x07\x45nviron\x12\x0b\n\x03key\x18\x01 \x02(\x0c\x12\r\n\x05value\x18\x02 \x02(\x0c\x42\x32\n,com.google.appengine.tools.development.proto \x02P\x01')
+  serialized_pb=_b('\n3apphosting/tools/devappserver2/runtime_config.proto\x12\x1e\x61pphosting.tools.devappserver2\"\xf2\x04\n\x06\x43onfig\x12\x0e\n\x06\x61pp_id\x18\x01 \x02(\x0c\x12\x12\n\nversion_id\x18\x02 \x02(\x0c\x12\x18\n\x10\x61pplication_root\x18\x03 \x02(\x0c\x12\x19\n\nthreadsafe\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x08\x61pi_host\x18\x11 \x01(\t:\tlocalhost\x12\x10\n\x08\x61pi_port\x18\x05 \x02(\x05\x12:\n\tlibraries\x18\x06 \x03(\x0b\x32\'.apphosting.tools.devappserver2.Library\x12\x16\n\nskip_files\x18\x07 \x01(\t:\x02^$\x12\x18\n\x0cstatic_files\x18\x08 \x01(\t:\x02^$\x12\x43\n\rpython_config\x18\x0e \x01(\x0b\x32,.apphosting.tools.devappserver2.PythonConfig\x12=\n\nphp_config\x18\t \x01(\x0b\x32).apphosting.tools.devappserver2.PhpConfig\x12\x38\n\x07\x65nviron\x18\n \x03(\x0b\x32\'.apphosting.tools.devappserver2.Environ\x12\x42\n\x10\x63loud_sql_config\x18\x0b \x01(\x0b\x32(.apphosting.tools.devappserver2.CloudSQL\x12\x12\n\ndatacenter\x18\x0c \x02(\t\x12\x13\n\x0binstance_id\x18\r \x02(\t\x12\x1b\n\x10stderr_log_level\x18\x0f \x01(\x03:\x01\x31\x12\x13\n\x0b\x61uth_domain\x18\x10 \x02(\t\x12\x15\n\rmax_instances\x18\x12 \x01(\x05\"A\n\tPhpConfig\x12\x1b\n\x13php_executable_path\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65nable_debugger\x18\x03 \x02(\x08\"<\n\x0cPythonConfig\x12\x16\n\x0estartup_script\x18\x01 \x01(\t\x12\x14\n\x0cstartup_args\x18\x02 \x01(\t\"t\n\x08\x43loudSQL\x12\x12\n\nmysql_host\x18\x01 \x02(\t\x12\x12\n\nmysql_port\x18\x02 \x02(\x05\x12\x12\n\nmysql_user\x18\x03 \x02(\t\x12\x16\n\x0emysql_password\x18\x04 \x02(\t\x12\x14\n\x0cmysql_socket\x18\x05 \x01(\t\"(\n\x07Library\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t\"%\n\x07\x45nviron\x12\x0b\n\x03key\x18\x01 \x02(\x0c\x12\r\n\x05value\x18\x02 \x02(\x0c\x42\x32\n,com.google.appengine.tools.development.proto \x02P\x01')
+)
 
 
 
@@ -44,21 +47,21 @@
     _descriptor.FieldDescriptor(
       name='app_id', full_name='apphosting.tools.devappserver2.Config.app_id', index=0,
       number=1, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='version_id', full_name='apphosting.tools.devappserver2.Config.version_id', index=1,
       number=2, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='application_root', full_name='apphosting.tools.devappserver2.Config.application_root', index=2,
       number=3, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -72,7 +75,7 @@
     _descriptor.FieldDescriptor(
       name='api_host', full_name='apphosting.tools.devappserver2.Config.api_host', index=4,
       number=17, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("localhost", "utf-8"),
+      has_default_value=True, default_value=_b("localhost").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -93,14 +96,14 @@
     _descriptor.FieldDescriptor(
       name='skip_files', full_name='apphosting.tools.devappserver2.Config.skip_files', index=7,
       number=7, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("^$", "utf-8"),
+      has_default_value=True, default_value=_b("^$").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='static_files', full_name='apphosting.tools.devappserver2.Config.static_files', index=8,
       number=8, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("^$", "utf-8"),
+      has_default_value=True, default_value=_b("^$").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -135,14 +138,14 @@
     _descriptor.FieldDescriptor(
       name='datacenter', full_name='apphosting.tools.devappserver2.Config.datacenter', index=13,
       number=12, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='instance_id', full_name='apphosting.tools.devappserver2.Config.instance_id', index=14,
       number=13, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -156,7 +159,7 @@
     _descriptor.FieldDescriptor(
       name='auth_domain', full_name='apphosting.tools.devappserver2.Config.auth_domain', index=16,
       number=16, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -191,7 +194,7 @@
     _descriptor.FieldDescriptor(
       name='php_executable_path', full_name='apphosting.tools.devappserver2.PhpConfig.php_executable_path', index=0,
       number=1, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -226,14 +229,14 @@
     _descriptor.FieldDescriptor(
       name='startup_script', full_name='apphosting.tools.devappserver2.PythonConfig.startup_script', index=0,
       number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='startup_args', full_name='apphosting.tools.devappserver2.PythonConfig.startup_args', index=1,
       number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -261,7 +264,7 @@
     _descriptor.FieldDescriptor(
       name='mysql_host', full_name='apphosting.tools.devappserver2.CloudSQL.mysql_host', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -275,21 +278,21 @@
     _descriptor.FieldDescriptor(
       name='mysql_user', full_name='apphosting.tools.devappserver2.CloudSQL.mysql_user', index=2,
       number=3, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='mysql_password', full_name='apphosting.tools.devappserver2.CloudSQL.mysql_password', index=3,
       number=4, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='mysql_socket', full_name='apphosting.tools.devappserver2.CloudSQL.mysql_socket', index=4,
       number=5, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -317,14 +320,14 @@
     _descriptor.FieldDescriptor(
       name='name', full_name='apphosting.tools.devappserver2.Library.name', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='version', full_name='apphosting.tools.devappserver2.Library.version', index=1,
       number=2, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -352,14 +355,14 @@
     _descriptor.FieldDescriptor(
       name='key', full_name='apphosting.tools.devappserver2.Environ.key', index=0,
       number=1, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='value', full_name='apphosting.tools.devappserver2.Environ.value', index=1,
       number=2, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -388,43 +391,43 @@
 DESCRIPTOR.message_types_by_name['Library'] = _LIBRARY
 DESCRIPTOR.message_types_by_name['Environ'] = _ENVIRON
 
-class Config(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _CONFIG
-
+Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), dict(
+  DESCRIPTOR = _CONFIG,
+  __module__ = 'google.appengine.tools.devappserver2.runtime_config_pb2'
   # @@protoc_insertion_point(class_scope:apphosting.tools.devappserver2.Config)
+  ))
 
-class PhpConfig(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _PHPCONFIG
-
+PhpConfig = _reflection.GeneratedProtocolMessageType('PhpConfig', (_message.Message,), dict(
+  DESCRIPTOR = _PHPCONFIG,
+  __module__ = 'google.appengine.tools.devappserver2.runtime_config_pb2'
   # @@protoc_insertion_point(class_scope:apphosting.tools.devappserver2.PhpConfig)
+  ))
 
-class PythonConfig(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _PYTHONCONFIG
-
+PythonConfig = _reflection.GeneratedProtocolMessageType('PythonConfig', (_message.Message,), dict(
+  DESCRIPTOR = _PYTHONCONFIG,
+  __module__ = 'google.appengine.tools.devappserver2.runtime_config_pb2'
   # @@protoc_insertion_point(class_scope:apphosting.tools.devappserver2.PythonConfig)
+  ))
 
-class CloudSQL(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _CLOUDSQL
-
+CloudSQL = _reflection.GeneratedProtocolMessageType('CloudSQL', (_message.Message,), dict(
+  DESCRIPTOR = _CLOUDSQL,
+  __module__ = 'google.appengine.tools.devappserver2.runtime_config_pb2'
   # @@protoc_insertion_point(class_scope:apphosting.tools.devappserver2.CloudSQL)
+  ))
 
-class Library(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _LIBRARY
-
+Library = _reflection.GeneratedProtocolMessageType('Library', (_message.Message,), dict(
+  DESCRIPTOR = _LIBRARY,
+  __module__ = 'google.appengine.tools.devappserver2.runtime_config_pb2'
   # @@protoc_insertion_point(class_scope:apphosting.tools.devappserver2.Library)
+  ))
 
-class Environ(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _ENVIRON
-
+Environ = _reflection.GeneratedProtocolMessageType('Environ', (_message.Message,), dict(
+  DESCRIPTOR = _ENVIRON,
+  __module__ = 'google.appengine.tools.devappserver2.runtime_config_pb2'
   # @@protoc_insertion_point(class_scope:apphosting.tools.devappserver2.Environ)
+  ))
 
 
 DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), '\n,com.google.appengine.tools.development.proto \002P\001')
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n,com.google.appengine.tools.development.proto \002P\001'))
 # @@protoc_insertion_point(module_scope)
diff --git a/google/appengine/tools/devappserver2/safe_subprocess.py b/google/appengine/tools/devappserver2/safe_subprocess.py
index 5f5bdbf..b3dcdae 100644
--- a/google/appengine/tools/devappserver2/safe_subprocess.py
+++ b/google/appengine/tools/devappserver2/safe_subprocess.py
@@ -59,8 +59,11 @@
     logging.debug('Starting process %r with input=%r, env=%r, cwd=%r',
                   args, input_string, env, cwd)
 
-    if sys.platform == 'win32':
-      # Suppress the display of the console window on Windows.
+    # Suppress the display of the console window on Windows.
+    # Note: subprocess.STARTF_USESHOWWINDOW & subprocess.SW_HIDE are only
+    # availalbe after Python 2.7.2 on Windows.
+    if (hasattr(subprocess, 'SW_HIDE') and
+        hasattr(subprocess, 'STARTF_USESHOWWINDOW')):
       startupinfo = subprocess.STARTUPINFO()
       startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
       startupinfo.wShowWindow = subprocess.SW_HIDE
diff --git a/google/appengine/tools/devappserver2/win32_file_watcher.py b/google/appengine/tools/devappserver2/win32_file_watcher.py
index 4ea80ad..0753627 100644
--- a/google/appengine/tools/devappserver2/win32_file_watcher.py
+++ b/google/appengine/tools/devappserver2/win32_file_watcher.py
@@ -48,6 +48,9 @@
 class Win32FileWatcher(object):
   """Monitors a directory tree for changes using inotify."""
 
+  # TODO: evaluate whether we can directly support multiple directories.
+  SUPPORTS_MULTIPLE_DIRECTORIES = False
+
   def __init__(self, directory):
     """Initializer for InotifyFileWatcher.
 
diff --git a/google/appengine/tools/dispatch_xml_parser.py b/google/appengine/tools/dispatch_xml_parser.py
new file mode 100644
index 0000000..0832b95
--- /dev/null
+++ b/google/appengine/tools/dispatch_xml_parser.py
@@ -0,0 +1,125 @@
+#!/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.
+#
+"""Directly processes text of dispatch.xml.
+
+DispatchXmlParser is called with an XML string to produce a list of
+DispatchEntry objects containing the data from the XML.
+"""
+
+from xml.etree import ElementTree
+
+from google.appengine.tools import xml_parser_utils
+from google.appengine.tools.app_engine_config_exception import AppEngineConfigException
+
+MISSING_URL = '<dispatch> node must contain a <url>'
+MISSING_MODULE = '<dispatch> node must contain a <module>'
+
+
+def GetDispatchYaml(application, dispatch_xml_str):
+  return _MakeDispatchListIntoYaml(
+      application, DispatchXmlParser().ProcessXml(dispatch_xml_str))
+
+
+def _MakeDispatchListIntoYaml(application, dispatch_list):
+  """Converts list of DispatchEntry objects into a YAML string."""
+  statements = [
+      'application: %s' % application,
+      'dispatch:',
+  ]
+  for entry in dispatch_list:
+    statements += entry.ToYaml()
+  return '\n'.join(statements) + '\n'
+
+
+class DispatchXmlParser(object):
+  """Provides logic for walking down XML tree and pulling data."""
+
+  def ProcessXml(self, xml_str):
+    """Parses XML string and returns object representation of relevant info.
+
+    Args:
+      xml_str: The XML string.
+    Returns:
+      A list of DispatchEntry objects defining how URLs are dispatched to
+      modules.
+    Raises:
+      AppEngineConfigException: In case of malformed XML or illegal inputs.
+    """
+
+    try:
+      self.dispatch_entries = []
+      self.errors = []
+      xml_root = ElementTree.fromstring(xml_str)
+      if xml_root.tag != 'dispatch-entries':
+        raise AppEngineConfigException('Root tag must be <dispatch-entries>')
+
+      for child in xml_root.getchildren():
+        self.ProcessDispatchNode(child)
+
+      if self.errors:
+        raise AppEngineConfigException('\n'.join(self.errors))
+
+      return self.dispatch_entries
+    except ElementTree.ParseError:
+      raise AppEngineConfigException('Bad input -- not valid XML')
+
+  def ProcessDispatchNode(self, node):
+    """Processes XML <dispatch> nodes into DispatchEntry objects.
+
+    The following information is parsed out:
+      url: The URL or URL pattern to route.
+      module: The module to route it to.
+    If there are no errors, the data is loaded into a DispatchEntry object
+    and added to a list. Upon error, a description of the error is added to
+    a list and the method terminates.
+
+    Args:
+      node: <dispatch> XML node in dos.xml.
+    """
+    tag = xml_parser_utils.GetTag(node)
+    if tag != 'dispatch':
+      self.errors.append('Unrecognized node: <%s>' % tag)
+      return
+
+    entry = DispatchEntry()
+    entry.url = xml_parser_utils.GetChildNodeText(node, 'url')
+    entry.module = xml_parser_utils.GetChildNodeText(node, 'module')
+
+    validation = self._ValidateEntry(entry)
+    if validation:
+      self.errors.append(validation)
+      return
+    self.dispatch_entries.append(entry)
+
+  def _ValidateEntry(self, entry):
+    if not entry.url:
+      return MISSING_URL
+    if not entry.module:
+      return MISSING_MODULE
+
+
+class DispatchEntry(object):
+  """Instances contain information about individual dispatch entries."""
+
+  def ToYaml(self):
+    return [
+        "- url: '%s'" % self._SanitizeForYaml(self.url),
+        '  module: %s' % self.module,
+    ]
+
+  def _SanitizeForYaml(self, dirty_str):
+    return dirty_str.replace("'", r"\'")
diff --git a/google/appengine/tools/dos_xml_parser.py b/google/appengine/tools/dos_xml_parser.py
new file mode 100644
index 0000000..96de44e
--- /dev/null
+++ b/google/appengine/tools/dos_xml_parser.py
@@ -0,0 +1,135 @@
+#!/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.
+#
+"""Directly processes text of dos.xml.
+
+DosXmlParser is called with an XML string to produce a list of BlackListEntry
+objects containing the data from the XML.
+
+DosXmlParser: converts XML to list of BlackListEntrys.
+BlacklistEntry: describes a blacklisted IP.
+"""
+
+import re
+from xml.etree import ElementTree
+
+import ipaddr
+
+from google.appengine.tools import xml_parser_utils
+from google.appengine.tools.app_engine_config_exception import AppEngineConfigException
+
+MISSING_SUBNET = '<blacklist> node must have a subnet specified'
+BAD_IPV_SUBNET = '"%s" is not a valid IPv4 or IPv6 subnet'
+BAD_PREFIX_LENGTH = ('Prefix length of subnet "%s" must be an integer '
+                     '(quad-dotted masks are not supported)')
+
+
+def GetDosYaml(unused_application, dos_xml_str):
+  return _MakeDosListIntoYaml(DosXmlParser().ProcessXml(dos_xml_str))
+
+
+def _MakeDosListIntoYaml(dos_list):
+  """Converts yaml statement list of blacklisted IP's into a string."""
+  statements = ['blacklist:']
+  for entry in dos_list:
+    statements += entry.ToYaml()
+  return '\n'.join(statements) + '\n'
+
+
+class DosXmlParser(object):
+  """Provides logic for walking down XML tree and pulling data."""
+
+  def ProcessXml(self, xml_str):
+    """Parses XML string and returns object representation of relevant info.
+
+    Args:
+      xml_str: The XML string.
+    Returns:
+      A list of BlacklistEntry objects containing information about blacklisted
+      IP's specified in the XML.
+    Raises:
+      AppEngineConfigException: In case of malformed XML or illegal inputs.
+    """
+
+    try:
+      self.blacklist_entries = []
+      self.errors = []
+      xml_root = ElementTree.fromstring(xml_str)
+      if xml_root.tag != 'blacklistentries':
+        raise AppEngineConfigException('Root tag must be <blacklistentries>')
+
+      for child in xml_root.getchildren():
+        self.ProcessBlacklistNode(child)
+
+      if self.errors:
+        raise AppEngineConfigException('\n'.join(self.errors))
+
+      return self.blacklist_entries
+    except ElementTree.ParseError:
+      raise AppEngineConfigException('Bad input -- not valid XML')
+
+  def ProcessBlacklistNode(self, node):
+    """Processes XML <blacklist> nodes into BlacklistEntry objects.
+
+    The following information is parsed out:
+      subnet: The IP, in CIDR notation.
+      description: (optional)
+    If there are no errors, the data is loaded into a BlackListEntry object
+    and added to a list. Upon error, a description of the error is added to
+    a list and the method terminates.
+
+    Args:
+      node: <blacklist> XML node in dos.xml.
+    """
+    tag = xml_parser_utils.GetTag(node)
+    if tag != 'blacklist':
+      self.errors.append('Unrecognized node: <%s>' % tag)
+      return
+
+    entry = BlacklistEntry()
+    entry.subnet = xml_parser_utils.GetChildNodeText(node, 'subnet')
+    entry.description = xml_parser_utils.GetChildNodeText(node, 'description')
+
+    validation = self._ValidateEntry(entry)
+    if validation:
+      self.errors.append(validation)
+      return
+    self.blacklist_entries.append(entry)
+
+  def _ValidateEntry(self, entry):
+    if not entry.subnet:
+      return MISSING_SUBNET
+    try:
+      ipaddr.IPNetwork(entry.subnet)
+    except ValueError:
+      return BAD_IPV_SUBNET % entry.subnet
+    parts = entry.subnet.split('/')
+    if len(parts) == 2 and not re.match('^[0-9]+$', parts[1]):
+      return BAD_PREFIX_LENGTH % entry.subnet
+
+
+class BlacklistEntry(object):
+  """Instances contain information about individual blacklist entries."""
+
+  def ToYaml(self):
+    statements = ['- subnet: %s' % self.subnet]
+    if self.description:
+      statements.append(
+          '  description: %s' % self._SanitizeForYaml(self.description))
+    return statements
+
+  def _SanitizeForYaml(self, dirty_str):
+    return "'%s'" % dirty_str.replace('\n', ' ')
diff --git a/google/appengine/tools/handler_generator.py b/google/appengine/tools/handler_generator.py
index 97a1535..2edc5bc 100644
--- a/google/appengine/tools/handler_generator.py
+++ b/google/appengine/tools/handler_generator.py
@@ -192,7 +192,7 @@
         handler_patterns.append(new_handler)
         handler_patterns += self.CreateHandlerWithoutTrailingStar(new_handler)
 
-    if has_jsps or self.app_engine_web_xml.use_vm:
+    if has_jsps or self.app_engine_web_xml.vm:
       handler_patterns.append(
           handler.SimpleHandler('*.jsp', {'type': 'dynamic'}))
 
diff --git a/google/appengine/tools/indexes_xml_parser.py b/google/appengine/tools/indexes_xml_parser.py
new file mode 100644
index 0000000..bdfdf63
--- /dev/null
+++ b/google/appengine/tools/indexes_xml_parser.py
@@ -0,0 +1,134 @@
+#!/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.
+#
+"""Directly processes text of datastore-indexes.xml.
+
+IndexesXmlParser is called with an XML string to produce an IndexXml object
+containing the data from the XML.
+
+IndexesXmlParser: converts XML to Index object.
+Index: describes a single index specified in datastore-indexes.xml
+"""
+
+from xml.etree import ElementTree
+
+from google.appengine.tools import xml_parser_utils
+from google.appengine.tools.app_engine_config_exception import AppEngineConfigException
+
+MISSING_KIND = '<datastore-index> node has missing attribute "kind".'
+BAD_DIRECTION = ('<property> tag attribute "direction" must have value "asc"'
+                 ' or "desc", given "%s"')
+NAME_MISSING = ('<datastore-index> node with kind "%s" needs to have a name'
+                ' attribute specified for its <property> node')
+
+
+def GetIndexYaml(unused_application, indexes_xml_str):
+  return _MakeIndexesListIntoYaml(
+      IndexesXmlParser().ProcessXml(indexes_xml_str))
+
+
+def _MakeIndexesListIntoYaml(indexes_list):
+  """Converts list of yaml statements about datastore indexes into a string."""
+  statements = ['indexes:']
+  for index in indexes_list:
+    statements += index.ToYaml()
+  return '\n'.join(statements) + '\n'
+
+
+class IndexesXmlParser(object):
+  """Provides logic for walking down XML tree and pulling data."""
+
+  def ProcessXml(self, xml_str):
+    """Parses XML string and returns object representation of relevant info.
+
+    Args:
+      xml_str: The XML string.
+    Returns:
+      A list of Index objects containing information about datastore indexes
+      from the XML.
+    Raises:
+      AppEngineConfigException: In case of malformed XML or illegal inputs.
+    """
+
+    try:
+      self.indexes = []
+      self.errors = []
+      xml_root = ElementTree.fromstring(xml_str)
+      if xml_parser_utils.GetTag(xml_root) != 'datastore-indexes':
+        raise AppEngineConfigException('Root tag must be <datastore-indexes>')
+
+      for child in xml_root.getchildren():
+        self.ProcessIndexNode(child)
+
+      if self.errors:
+        raise AppEngineConfigException('\n'.join(self.errors))
+
+      return self.indexes
+    except ElementTree.ParseError:
+      raise AppEngineConfigException('Bad input -- not valid XML')
+
+  def ProcessIndexNode(self, node):
+    """Processes XML <datastore-index> nodes into Index objects.
+
+    The following information is parsed out:
+    kind: specifies the kind of entities to index.
+    ancestor: true if the index supports queries that filter by
+      ancestor-key to constraint results to a single entity group.
+    property: represents the entity properties to index, with a name
+      and direction attribute.
+
+    Args:
+      node: <datastore-index> XML node in datastore-indexes.xml.
+    """
+    tag = xml_parser_utils.GetTag(node)
+    if tag != 'datastore-index':
+      self.errors.append('Unrecognized node: <%s>' % tag)
+      return
+
+    index = Index()
+    index.kind = xml_parser_utils.GetAttribute(node, 'kind')
+    if not index.kind:
+      self.errors.append(MISSING_KIND)
+    index.ancestor = xml_parser_utils.BooleanValue(
+        xml_parser_utils.GetAttribute(node, 'ancestor'))
+    index.properties = {}
+    for property_node in xml_parser_utils.GetNodes(node, 'property'):
+      name = xml_parser_utils.GetAttribute(property_node, 'name')
+      if not name:
+        self.errors.append(NAME_MISSING % index.kind)
+        continue
+
+      direction = (xml_parser_utils.GetAttribute(property_node, 'direction')
+                   or 'asc')
+      if direction not in ('asc', 'desc'):
+        self.errors.append(BAD_DIRECTION % direction)
+        continue
+      index.properties[name] = direction
+    self.indexes.append(index)
+
+
+class Index(object):
+
+  def ToYaml(self):
+    statements = ['- kind: "%s"' % self.kind]
+    if self.ancestor:
+      statements.append('  ancestor: yes')
+    if self.properties:
+      statements.append('  properties:')
+      for name in sorted(self.properties):
+        statements += ['  - name: "%s"' % name,
+                       '    direction: %s' % self.properties[name]]
+    return statements
diff --git a/google/appengine/tools/queue_xml_parser.py b/google/appengine/tools/queue_xml_parser.py
new file mode 100644
index 0000000..70b69fc
--- /dev/null
+++ b/google/appengine/tools/queue_xml_parser.py
@@ -0,0 +1,263 @@
+#!/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.
+#
+"""Performs translation of queue.xml to queue.yaml."""
+
+from xml.etree import ElementTree
+
+from google.appengine.tools import xml_parser_utils
+from google.appengine.tools.app_engine_config_exception import AppEngineConfigException
+
+PUSH_QUEUE_TAGS = (
+    'rate', 'bucket-size', 'max-concurrent-requests', 'target')
+
+PUSH_QUEUE_RETRY_PARAMS = (
+    'task-age-limit',
+    'min-backoff-seconds',
+    'max-backoff-seconds',
+    'max-doublings')
+
+RETRY_PARAMETER_TAGS = ('task-retry-limit',) + PUSH_QUEUE_RETRY_PARAMS
+
+BAD_MODE_ERROR_MESSAGE = (
+    'Mode, if specified, must be either push or pull'
+    ' (defaults to push) for queue entries. Bad value'
+    " '%s' in <queue> entry with name '%s'")
+
+PULL_QUEUE_ERROR_MESSAGE = (
+    'The element <%s> is not defined for pull '
+    "queues; bad <queue> entry with name '%s'")
+RETRY_PARAM_ERROR_MESSAGE = (
+    'The element <%s> in <retry-parameters> is not '
+    "defined for pull queue with name '%s'")
+
+
+def GetQueueYaml(unused_application, queue_xml_str):
+  queue_xml = QueueXmlParser().ProcessXml(queue_xml_str)
+  return queue_xml.ToYaml()
+
+
+class QueueXmlParser(object):
+  """Provides logic for walking down XML tree and pulling data."""
+
+  def ProcessXml(self, xml_str):
+    """Parses XML string and returns object representation of relevant info.
+
+    Args:
+      xml_str: The XML string.
+    Returns:
+      A QueueXml object containing information about task queue
+      specifications from the XML.
+    Raises:
+      AppEngineConfigException: In case of malformed XML or illegal inputs.
+    """
+
+    try:
+      self.errors = []
+      xml_root = ElementTree.fromstring(xml_str)
+
+      if xml_parser_utils.GetTag(xml_root) != 'queue-entries':
+        raise AppEngineConfigException('Root tag must be <queue-entries>')
+
+      self.queue_xml = QueueXml()
+      self.queue_xml.queues = []
+      self.queue_xml.total_storage_limit = xml_parser_utils.GetChildNodeText(
+          xml_root, 'total-storage-limit')
+      for child in xml_parser_utils.GetNodes(xml_root, 'queue'):
+        self.ProcessQueueNode(child)
+
+      if self.errors:
+        raise AppEngineConfigException('\n'.join(self.errors))
+
+      return self.queue_xml
+
+    except ElementTree.ParseError:
+      raise AppEngineConfigException('Bad input -- not valid XML')
+
+  def ProcessQueueNode(self, node):
+    """Processes XML <queue> nodes into Queue objects.
+
+    The following information is parsed out:
+      name
+      mode: can be either push or pull
+      retry-parameters:
+        task-retry-limit
+    ---- push queues only ----
+        task-age-limit
+        min-backoff-seconds
+        max-back-off-seconds
+        max-doubling
+      bucket-size
+      max-concurrent-requests
+      rate: how often tasks are processed on this queue.
+      target: version of application on which tasks on this queue will be
+        invoked.
+    ---- pull queues only ----
+      acl: access control list - lists user and writer email addresses.
+
+    Args:
+      node: Current <queue> XML node being processed.
+    """
+
+    name = xml_parser_utils.GetChildNodeText(node, 'name')
+    if not name:
+      self.errors.append('Must specify a name for each <queue> entry')
+      return
+
+
+    mode = xml_parser_utils.GetChildNodeText(node, 'mode', 'push')
+
+    if mode not in ('push', 'pull'):
+      self.errors.append(BAD_MODE_ERROR_MESSAGE % (mode, name))
+      return
+    if mode == 'pull':
+      queue = PullQueue()
+      queue.name = name
+      self._ProcessPullQueueNode(node, queue)
+    else:
+      queue = PushQueue()
+      queue.name = name
+      self._ProcessPushQueueNode(node, queue)
+
+    self.queue_xml.queues.append(queue)
+
+  def _ProcessPushQueueNode(self, node, queue):
+    if xml_parser_utils.GetChild(node, 'acl') is not None:
+      self.errors.append(
+          'The element <acl> is not defined for push '
+          "queues; bad <queue> entry with name '%s'" % queue.name)
+    for tag in PUSH_QUEUE_TAGS:
+      field_name = tag.replace('-', '_')
+      setattr(queue, field_name, xml_parser_utils.GetChildNodeText(node, tag))
+    self._ProcessRetryParametersNode(node, queue)
+
+  def _ProcessPullQueueNode(self, node, queue):
+    """Populates PullQueue-specific fields from parsed XML."""
+    for tag in PUSH_QUEUE_TAGS:
+      if xml_parser_utils.GetChild(node, tag) is not None:
+        self.errors.append(PULL_QUEUE_ERROR_MESSAGE % (tag, queue.name))
+
+    acl_node = xml_parser_utils.GetChild(node, 'acl')
+
+    if acl_node is not None:
+      queue.acl = Acl()
+      queue.acl.user_emails = [
+          sub_node.text for sub_node in
+          xml_parser_utils.GetNodes(acl_node, 'user-email')]
+      queue.acl.writer_emails = [
+          sub_node.text for sub_node in
+          xml_parser_utils.GetNodes(acl_node, 'writer-email')]
+    else:
+      queue.acl = None
+
+    self._ProcessRetryParametersNode(node, queue)
+
+  def _ProcessRetryParametersNode(self, node, queue):
+    """Pulls information out of <retry-parameters> node."""
+    retry_parameters_node = xml_parser_utils.GetChild(
+        node, 'retry-parameters')
+    if retry_parameters_node is None:
+      queue.retry_parameters = None
+      return
+    retry_parameters = RetryParameters()
+    queue.retry_parameters = retry_parameters
+    retry_parameters.task_retry_limit = xml_parser_utils.GetChildNodeText(
+        retry_parameters_node, 'task-retry-limit')
+
+    for tag in PUSH_QUEUE_RETRY_PARAMS:
+
+      if xml_parser_utils.GetChild(retry_parameters_node, tag) is not None:
+        if isinstance(queue, PullQueue):
+          self.errors.append(RETRY_PARAM_ERROR_MESSAGE % (tag, queue.name))
+        else:
+          setattr(
+              retry_parameters,
+              tag.replace('-', '_'),
+              xml_parser_utils.GetChildNodeText(retry_parameters_node, tag))
+
+
+class QueueXml(object):
+
+  def __init__(self):
+    self.queues = []
+    self.total_storage_limit = None
+
+  def ToYaml(self):
+    statements = []
+    if self.total_storage_limit:
+      statements.append('total_storage_limit: %s\n' % self.total_storage_limit)
+    statements.append('queue:')
+    for queue in self.queues:
+      statements += queue.GetYamlStatementsList()
+
+    return '\n'.join(statements) + '\n'
+
+
+class Queue(object):
+
+  def GetYamlStatementsList(self):
+    statements = ['- name: %s' % self.name]
+    statements += self.GetAdditionalYamlStatementsList()
+
+    if self.retry_parameters:
+      statements += self.retry_parameters.GetYamlStatementsList()
+
+    return statements
+
+
+class PushQueue(Queue):
+
+  def GetAdditionalYamlStatementsList(self):
+    statements = ['  mode: push']
+    fields = (tag.replace('-', '_') for tag in PUSH_QUEUE_TAGS)
+    for field in fields:
+      field_value = getattr(self, field)
+      if field_value:
+        statements.append('  %s: %s' % (field, field_value))
+
+    return statements
+
+
+class PullQueue(Queue):
+
+  def GetAdditionalYamlStatementsList(self):
+    statements = ['  mode: pull']
+    if self.acl:
+      statements += self.acl.GetYamlStatementsList()
+    return statements
+
+
+class Acl(object):
+
+  def GetYamlStatementsList(self):
+    statements = ['  acl:']
+    statements += ['  - user_email: %s' % user_email for user_email in
+                   self.user_emails]
+    statements += ['  - writer_email: %s' % writer_email for writer_email in
+                   self.writer_emails]
+    return statements
+
+
+class RetryParameters(object):
+
+  def GetYamlStatementsList(self):
+    statements = ['  retry_parameters:']
+    field_names = (tag.replace('-', '_') for tag in RETRY_PARAMETER_TAGS)
+    for field in field_names:
+      field_value = getattr(self, field, None)
+      if field_value:
+        statements.append('    %s: %s' % (field, field_value))
+    return statements
diff --git a/google/appengine/tools/sdk_update_checker.py b/google/appengine/tools/sdk_update_checker.py
index 542455a..975267f 100644
--- a/google/appengine/tools/sdk_update_checker.py
+++ b/google/appengine/tools/sdk_update_checker.py
@@ -198,11 +198,7 @@
       return
     unsupported_api_versions_found = False
     for runtime, api_versions in self.runtime_to_api_version.items():
-      if 'supported_api_versions' in version:
-        supported_api_versions = version['supported_api_versions'].get(
-            runtime, version)['api_versions']
-      else:
-        supported_api_versions = version['api_versions']
+      supported_api_versions = _GetSupportedApiVersions(version, runtime)
       unsupported_api_versions = sorted(api_versions -
                                         set(supported_api_versions))
       if unsupported_api_versions:
@@ -290,7 +286,7 @@
           return
 
     for runtime, response in responses.items():
-      api_versions = response['api_versions']
+      api_versions = _GetSupportedApiVersions(response, runtime)
       obsolete_versions = sorted(
           self.runtime_to_api_version[runtime] - set(api_versions))
       if len(obsolete_versions) == 1:
@@ -435,3 +431,25 @@
         nag.opt_in = True
       self._WriteNagFile(nag)
     return nag.opt_in
+
+
+def _GetSupportedApiVersions(versions, runtime):
+  """Returns the runtime-specific or general list of supported runtimes.
+
+  The provided 'versions' dict contains a field called 'api_versions'
+  which is the list of default versions supported.  This dict may also
+  contain a 'supported_api_versions' dict which lists api_versions by
+  runtime.  This function will prefer to return the runtime-specific
+  api_versions list, but will default to the general list.
+
+  Args:
+    versions: dict of versions from app.yaml or /api/updatecheck server.
+    runtime: string of current runtime (e.g. 'go').
+
+  Returns:
+    List of supported api_versions (e.g. ['go1']).
+  """
+  if 'supported_api_versions' in versions:
+    return versions['supported_api_versions'].get(
+        runtime, versions)['api_versions']
+  return versions['api_versions']
diff --git a/google/appengine/tools/yaml_translator.py b/google/appengine/tools/yaml_translator.py
index d2b437a..e85ea6e 100644
--- a/google/appengine/tools/yaml_translator.py
+++ b/google/appengine/tools/yaml_translator.py
@@ -128,7 +128,7 @@
             '%s: %s' % (entry_name, self.SanitizeForYaml(field)))
     for entry_name, field in [
         ('runtime', GetRuntime()),
-        ('vm', self.app_engine_web_xml.use_vm),
+        ('vm', self.app_engine_web_xml.vm),
         ('threadsafe', self.app_engine_web_xml.threadsafe),
         ('instance_class', self.app_engine_web_xml.instance_class),
         ('auto_id_policy', self.app_engine_web_xml.auto_id_policy),
@@ -213,7 +213,7 @@
 
   def TranslateVmSettings(self):
     """Translates VM settings in appengine-web.xml to yaml."""
-    if (not self.app_engine_web_xml.use_vm or
+    if (not self.app_engine_web_xml.vm or
         not self.app_engine_web_xml.vm_settings):
       return []
 
diff --git a/google/net/proto2/proto/descriptor_pb2.py b/google/net/proto2/proto/descriptor_pb2.py
index d33d516..eed2c6a 100644
--- a/google/net/proto2/proto/descriptor_pb2.py
+++ b/google/net/proto2/proto/descriptor_pb2.py
@@ -28,7 +28,7 @@
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='net/proto2/proto/descriptor.proto',
   package='proto2',
-  serialized_pb='\n!net/proto2/proto/descriptor.proto\x12\x06proto2\">\n\x11\x46ileDescriptorSet\x12)\n\x04\x66ile\x18\x01 \x03(\x0b\x32\x1b.proto2.FileDescriptorProto\"\x95\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12-\n\x0cmessage_type\x18\x04 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x05 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12/\n\x07service\x18\x06 \x03(\x0b\x32\x1e.proto2.ServiceDescriptorProto\x12/\n\textension\x18\x07 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12$\n\x07options\x18\x08 \x01(\x0b\x32\x13.proto2.FileOptions\x12\x30\n\x10source_code_info\x18\t \x01(\x0b\x32\x16.proto2.SourceCodeInfo\"\xa5\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12/\n\textension\x18\x06 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12,\n\x0bnested_type\x18\x03 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x04 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12?\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32&.proto2.DescriptorProto.ExtensionRange\x12\x30\n\noneof_decl\x18\x08 \x03(\x0b\x32\x1c.proto2.OneofDescriptorProto\x12\'\n\x07options\x18\x07 \x01(\x0b\x32\x16.proto2.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\x8e\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x31\n\x05label\x18\x04 \x01(\x0e\x32\".proto2.FieldDescriptorProto.Label\x12/\n\x04type\x18\x05 \x01(\x0e\x32!.proto2.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12%\n\x07options\x18\x08 \x01(\x0b\x32\x14.proto2.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"z\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x03(\x0b\x32 .proto2.EnumValueDescriptorProto\x12$\n\x07options\x18\x03 \x01(\x0b\x32\x13.proto2.EnumOptions\"c\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12)\n\x07options\x18\x03 \x01(\x0b\x32\x18.proto2.EnumValueOptions\"\xad\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\x06method\x18\x02 \x03(\x0b\x32\x1d.proto2.MethodDescriptorProto\x12-\n\x06stream\x18\x04 \x03(\x0b\x32\x1d.proto2.StreamDescriptorProto\x12\'\n\x07options\x18\x03 \x01(\x0b\x32\x16.proto2.ServiceOptions\"v\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.MethodOptions\"\x87\x01\n\x15StreamDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x63lient_message_type\x18\x02 \x01(\t\x12\x1b\n\x13server_message_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.StreamOptions\"\xdb\t\n\x0b\x46ileOptions\x12\x19\n\x0e\x63\x63_api_version\x18\x02 \x01(\x05:\x01\x32\x12V\n\x14\x63\x63_api_compatibility\x18\x0f \x01(\x0e\x32&.proto2.FileOptions.CompatibilityLevel:\x10NO_COMPATIBILITY\x12\'\n\x19\x63\x63_proto_array_compatible\x18\x16 \x01(\x08:\x04true\x12\"\n\x14\x63\x63_utf8_verification\x18\x18 \x01(\x08:\x04true\x12$\n\x15\x63\x63_proto1_text_format\x18\x19 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x19\n\x0epy_api_version\x18\x04 \x01(\x05:\x01\x32\x12\x1b\n\x10java_api_version\x18\x05 \x01(\x05:\x01\x32\x12!\n\x13java_use_javaproto2\x18\x06 \x01(\x08:\x04true\x12\x1e\n\x10java_java5_enums\x18\x07 \x01(\x08:\x04true\x12)\n\x1ajava_generate_rpc_baseimpl\x18\r \x01(\x08:\x05\x66\x61lse\x12#\n\x14java_use_javastrings\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_alt_api_package\x18\x13 \x01(\t\x12\x33\n%java_enable_dual_generate_mutable_api\x18\x1a \x01(\x08:\x04true\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10java_mutable_api\x18\x1c \x01(\x08:\x05\x66\x61lse\x12=\n\x0coptimize_for\x18\t \x01(\x0e\x32 .proto2.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\x1a\n\x12javascript_package\x18\x0c \x01(\t\x12\x1a\n\x0fszl_api_version\x18\x0e \x01(\x05:\x01\x31\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"c\n\x12\x43ompatibilityLevel\x12\x14\n\x10NO_COMPATIBILITY\x10\x00\x12\x15\n\x11PROTO1_COMPATIBLE\x10\x64\x12 \n\x1c\x44\x45PRECATED_PROTO1_COMPATIBLE\x10\x32\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x02\n\x0eMessageOptions\x12+\n#experimental_java_message_interface\x18\x04 \x03(\t\x12+\n#experimental_java_builder_interface\x18\x05 \x03(\t\x12+\n#experimental_java_interface_extends\x18\x06 \x03(\t\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa0\x05\n\x0c\x46ieldOptions\x12\x31\n\x05\x63type\x18\x01 \x01(\x0e\x32\x1a.proto2.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x31\n\x05jtype\x18\x04 \x01(\x0e\x32\x1a.proto2.FieldOptions.JType:\x06NORMAL\x12\x36\n\x06jstype\x18\x06 \x01(\x0e\x32\x1b.proto2.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14\x65xperimental_map_key\x18\t \x01(\t\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12<\n\x0fupgraded_option\x18\x0b \x03(\x0b\x32#.proto2.FieldOptions.UpgradedOption\x12%\n\x16\x64\x65precated_raw_message\x18\x0c \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\x1a-\n\x0eUpgradedOption\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"<\n\x05JType\x12\n\n\x06NORMAL\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\x1c\n\x18\x45XPERIMENTAL_BYTE_BUFFER\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x99\x01\n\x0b\x45numOptions\x12\x13\n\x0bproto1_name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"t\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xb6\x01\n\x0eServiceOptions\x12\x1d\n\x0emulticast_stub\x18\x14 \x01(\x08:\x05\x66\x61lse\x12#\n\x17\x66\x61ilure_detection_delay\x18\x10 \x01(\x01:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x07\n\rMethodOptions\x12\x35\n\x08protocol\x18\x07 \x01(\x0e\x32\x1e.proto2.MethodOptions.Protocol:\x03TCP\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12$\n\x15\x64uplicate_suppression\x18\t \x01(\x08:\x05\x66\x61lse\x12\x18\n\tfail_fast\x18\n \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0e\x63lient_logging\x18\x0b \x01(\x11:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x0c \x01(\x11:\x03\x32\x35\x36\x12\x41\n\x0esecurity_level\x18\r \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x43\n\x0fresponse_format\x18\x0f \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x42\n\x0erequest_format\x18\x11 \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x13\n\x0bstream_type\x18\x12 \x01(\t\x12\x16\n\x0esecurity_label\x18\x13 \x01(\t\x12\x18\n\x10\x63lient_streaming\x18\x14 \x01(\x08\x12\x18\n\x10server_streaming\x18\x15 \x01(\x08\x12\x1a\n\x12legacy_stream_type\x18\x16 \x01(\t\x12\x1a\n\x12legacy_result_type\x18\x17 \x01(\t\x12(\n\x1clegacy_client_initial_tokens\x18\x18 \x01(\x03:\x02-1\x12(\n\x1clegacy_server_initial_tokens\x18\x19 \x01(\x03:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\x1c\n\x08Protocol\x12\x07\n\x03TCP\x10\x00\x12\x07\n\x03UDP\x10\x01\"e\n\rSecurityLevel\x12\x08\n\x04NONE\x10\x00\x12\r\n\tINTEGRITY\x10\x01\x12\x19\n\x15PRIVACY_AND_INTEGRITY\x10\x02\x12 \n\x1cSTRONG_PRIVACY_AND_INTEGRITY\x10\x03\"0\n\x06\x46ormat\x12\x10\n\x0cUNCOMPRESSED\x10\x00\x12\x14\n\x10ZIPPY_COMPRESSED\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x87\x04\n\rStreamOptions\x12!\n\x15\x63lient_initial_tokens\x18\x01 \x01(\x03:\x02-1\x12!\n\x15server_initial_tokens\x18\x02 \x01(\x03:\x02-1\x12<\n\ntoken_unit\x18\x03 \x01(\x0e\x32\x1f.proto2.StreamOptions.TokenUnit:\x07MESSAGE\x12\x41\n\x0esecurity_level\x18\x04 \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x16\n\x0esecurity_label\x18\x05 \x01(\t\x12\x1b\n\x0e\x63lient_logging\x18\x06 \x01(\x05:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x07 \x01(\x05:\x03\x32\x35\x36\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12\x18\n\tfail_fast\x18\t \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\n \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\"\n\tTokenUnit\x12\x0b\n\x07MESSAGE\x10\x00\x12\x08\n\x04\x42YTE\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x95\x02\n\x13UninterpretedOption\x12\x32\n\x04name\x18\x02 \x03(\x0b\x32$.proto2.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xa8\x01\n\x0eSourceCodeInfo\x12\x31\n\x08location\x18\x01 \x03(\x0b\x32\x1f.proto2.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB)\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01')
+  serialized_pb='\n!net/proto2/proto/descriptor.proto\x12\x06proto2\">\n\x11\x46ileDescriptorSet\x12)\n\x04\x66ile\x18\x01 \x03(\x0b\x32\x1b.proto2.FileDescriptorProto\"\x95\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12-\n\x0cmessage_type\x18\x04 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x05 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12/\n\x07service\x18\x06 \x03(\x0b\x32\x1e.proto2.ServiceDescriptorProto\x12/\n\textension\x18\x07 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12$\n\x07options\x18\x08 \x01(\x0b\x32\x13.proto2.FileOptions\x12\x30\n\x10source_code_info\x18\t \x01(\x0b\x32\x16.proto2.SourceCodeInfo\"\xa5\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12/\n\textension\x18\x06 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12,\n\x0bnested_type\x18\x03 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x04 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12?\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32&.proto2.DescriptorProto.ExtensionRange\x12\x30\n\noneof_decl\x18\x08 \x03(\x0b\x32\x1c.proto2.OneofDescriptorProto\x12\'\n\x07options\x18\x07 \x01(\x0b\x32\x16.proto2.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\x8e\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x31\n\x05label\x18\x04 \x01(\x0e\x32\".proto2.FieldDescriptorProto.Label\x12/\n\x04type\x18\x05 \x01(\x0e\x32!.proto2.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12%\n\x07options\x18\x08 \x01(\x0b\x32\x14.proto2.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"z\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x03(\x0b\x32 .proto2.EnumValueDescriptorProto\x12$\n\x07options\x18\x03 \x01(\x0b\x32\x13.proto2.EnumOptions\"c\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12)\n\x07options\x18\x03 \x01(\x0b\x32\x18.proto2.EnumValueOptions\"\xad\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\x06method\x18\x02 \x03(\x0b\x32\x1d.proto2.MethodDescriptorProto\x12-\n\x06stream\x18\x04 \x03(\x0b\x32\x1d.proto2.StreamDescriptorProto\x12\'\n\x07options\x18\x03 \x01(\x0b\x32\x16.proto2.ServiceOptions\"v\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.MethodOptions\"\x87\x01\n\x15StreamDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x63lient_message_type\x18\x02 \x01(\t\x12\x1b\n\x13server_message_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.StreamOptions\"\xdc\t\n\x0b\x46ileOptions\x12\x19\n\x0e\x63\x63_api_version\x18\x02 \x01(\x05:\x01\x32\x12V\n\x14\x63\x63_api_compatibility\x18\x0f \x01(\x0e\x32&.proto2.FileOptions.CompatibilityLevel:\x10NO_COMPATIBILITY\x12\'\n\x19\x63\x63_proto_array_compatible\x18\x16 \x01(\x08:\x04true\x12\"\n\x14\x63\x63_utf8_verification\x18\x18 \x01(\x08:\x04true\x12$\n\x15\x63\x63_proto1_text_format\x18\x19 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x19\n\x0epy_api_version\x18\x04 \x01(\x05:\x01\x32\x12\x1b\n\x10java_api_version\x18\x05 \x01(\x05:\x01\x32\x12!\n\x13java_use_javaproto2\x18\x06 \x01(\x08:\x04true\x12\x1e\n\x10java_java5_enums\x18\x07 \x01(\x08:\x04true\x12)\n\x1ajava_generate_rpc_baseimpl\x18\r \x01(\x08:\x05\x66\x61lse\x12#\n\x14java_use_javastrings\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_alt_api_package\x18\x13 \x01(\t\x12\x34\n%java_enable_dual_generate_mutable_api\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10java_mutable_api\x18\x1c \x01(\x08:\x05\x66\x61lse\x12=\n\x0coptimize_for\x18\t \x01(\x0e\x32 .proto2.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\x1a\n\x12javascript_package\x18\x0c \x01(\t\x12\x1a\n\x0fszl_api_version\x18\x0e \x01(\x05:\x01\x31\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"c\n\x12\x43ompatibilityLevel\x12\x14\n\x10NO_COMPATIBILITY\x10\x00\x12\x15\n\x11PROTO1_COMPATIBLE\x10\x64\x12 \n\x1c\x44\x45PRECATED_PROTO1_COMPATIBLE\x10\x32\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x02\n\x0eMessageOptions\x12+\n#experimental_java_message_interface\x18\x04 \x03(\t\x12+\n#experimental_java_builder_interface\x18\x05 \x03(\t\x12+\n#experimental_java_interface_extends\x18\x06 \x03(\t\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa0\x05\n\x0c\x46ieldOptions\x12\x31\n\x05\x63type\x18\x01 \x01(\x0e\x32\x1a.proto2.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x31\n\x05jtype\x18\x04 \x01(\x0e\x32\x1a.proto2.FieldOptions.JType:\x06NORMAL\x12\x36\n\x06jstype\x18\x06 \x01(\x0e\x32\x1b.proto2.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14\x65xperimental_map_key\x18\t \x01(\t\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12<\n\x0fupgraded_option\x18\x0b \x03(\x0b\x32#.proto2.FieldOptions.UpgradedOption\x12%\n\x16\x64\x65precated_raw_message\x18\x0c \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\x1a-\n\x0eUpgradedOption\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"<\n\x05JType\x12\n\n\x06NORMAL\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\x1c\n\x18\x45XPERIMENTAL_BYTE_BUFFER\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x99\x01\n\x0b\x45numOptions\x12\x13\n\x0bproto1_name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"t\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xb6\x01\n\x0eServiceOptions\x12\x1d\n\x0emulticast_stub\x18\x14 \x01(\x08:\x05\x66\x61lse\x12#\n\x17\x66\x61ilure_detection_delay\x18\x10 \x01(\x01:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x07\n\rMethodOptions\x12\x35\n\x08protocol\x18\x07 \x01(\x0e\x32\x1e.proto2.MethodOptions.Protocol:\x03TCP\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12$\n\x15\x64uplicate_suppression\x18\t \x01(\x08:\x05\x66\x61lse\x12\x18\n\tfail_fast\x18\n \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0e\x63lient_logging\x18\x0b \x01(\x11:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x0c \x01(\x11:\x03\x32\x35\x36\x12\x41\n\x0esecurity_level\x18\r \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x43\n\x0fresponse_format\x18\x0f \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x42\n\x0erequest_format\x18\x11 \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x13\n\x0bstream_type\x18\x12 \x01(\t\x12\x16\n\x0esecurity_label\x18\x13 \x01(\t\x12\x18\n\x10\x63lient_streaming\x18\x14 \x01(\x08\x12\x18\n\x10server_streaming\x18\x15 \x01(\x08\x12\x1a\n\x12legacy_stream_type\x18\x16 \x01(\t\x12\x1a\n\x12legacy_result_type\x18\x17 \x01(\t\x12(\n\x1clegacy_client_initial_tokens\x18\x18 \x01(\x03:\x02-1\x12(\n\x1clegacy_server_initial_tokens\x18\x19 \x01(\x03:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\x1c\n\x08Protocol\x12\x07\n\x03TCP\x10\x00\x12\x07\n\x03UDP\x10\x01\"e\n\rSecurityLevel\x12\x08\n\x04NONE\x10\x00\x12\r\n\tINTEGRITY\x10\x01\x12\x19\n\x15PRIVACY_AND_INTEGRITY\x10\x02\x12 \n\x1cSTRONG_PRIVACY_AND_INTEGRITY\x10\x03\"0\n\x06\x46ormat\x12\x10\n\x0cUNCOMPRESSED\x10\x00\x12\x14\n\x10ZIPPY_COMPRESSED\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x87\x04\n\rStreamOptions\x12!\n\x15\x63lient_initial_tokens\x18\x01 \x01(\x03:\x02-1\x12!\n\x15server_initial_tokens\x18\x02 \x01(\x03:\x02-1\x12<\n\ntoken_unit\x18\x03 \x01(\x0e\x32\x1f.proto2.StreamOptions.TokenUnit:\x07MESSAGE\x12\x41\n\x0esecurity_level\x18\x04 \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x16\n\x0esecurity_label\x18\x05 \x01(\t\x12\x1b\n\x0e\x63lient_logging\x18\x06 \x01(\x05:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x07 \x01(\x05:\x03\x32\x35\x36\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12\x18\n\tfail_fast\x18\t \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\n \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\"\n\tTokenUnit\x12\x0b\n\x07MESSAGE\x10\x00\x12\x08\n\x04\x42YTE\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x95\x02\n\x13UninterpretedOption\x12\x32\n\x04name\x18\x02 \x03(\x0b\x32$.proto2.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xa8\x01\n\x0eSourceCodeInfo\x12\x31\n\x08location\x18\x01 \x03(\x0b\x32\x1f.proto2.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB,\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01\xe0\x01\x01')
 
 
 
@@ -163,8 +163,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=3369,
-  serialized_end=3468,
+  serialized_start=3370,
+  serialized_end=3469,
 )
 
 _FILEOPTIONS_OPTIMIZEMODE = _descriptor.EnumDescriptor(
@@ -188,8 +188,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=3470,
-  serialized_end=3528,
+  serialized_start=3471,
+  serialized_end=3529,
 )
 
 _FIELDOPTIONS_CTYPE = _descriptor.EnumDescriptor(
@@ -213,8 +213,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=4379,
-  serialized_end=4426,
+  serialized_start=4380,
+  serialized_end=4427,
 )
 
 _FIELDOPTIONS_JTYPE = _descriptor.EnumDescriptor(
@@ -238,8 +238,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=4428,
-  serialized_end=4488,
+  serialized_start=4429,
+  serialized_end=4489,
 )
 
 _FIELDOPTIONS_JSTYPE = _descriptor.EnumDescriptor(
@@ -263,8 +263,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=4490,
-  serialized_end=4543,
+  serialized_start=4491,
+  serialized_end=4544,
 )
 
 _METHODOPTIONS_PROTOCOL = _descriptor.EnumDescriptor(
@@ -284,8 +284,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=5801,
-  serialized_end=5829,
+  serialized_start=5802,
+  serialized_end=5830,
 )
 
 _METHODOPTIONS_SECURITYLEVEL = _descriptor.EnumDescriptor(
@@ -313,8 +313,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=5831,
-  serialized_end=5932,
+  serialized_start=5832,
+  serialized_end=5933,
 )
 
 _METHODOPTIONS_FORMAT = _descriptor.EnumDescriptor(
@@ -334,8 +334,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=5934,
-  serialized_end=5982,
+  serialized_start=5935,
+  serialized_end=5983,
 )
 
 _STREAMOPTIONS_TOKENUNIT = _descriptor.EnumDescriptor(
@@ -355,8 +355,8 @@
   ],
   containing_type=None,
   options=None,
-  serialized_start=6470,
-  serialized_end=6504,
+  serialized_start=6471,
+  serialized_end=6505,
 )
 
 
@@ -1043,7 +1043,7 @@
     _descriptor.FieldDescriptor(
       name='java_enable_dual_generate_mutable_api', full_name='proto2.FileOptions.java_enable_dual_generate_mutable_api', index=13,
       number=26, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=True,
+      has_default_value=True, default_value=False,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1157,7 +1157,7 @@
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
   serialized_start=2296,
-  serialized_end=3539,
+  serialized_end=3540,
 )
 
 
@@ -1226,8 +1226,8 @@
   options=None,
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
-  serialized_start=3542,
-  serialized_end=3879,
+  serialized_start=3543,
+  serialized_end=3880,
 )
 
 
@@ -1261,8 +1261,8 @@
   options=None,
   is_extendable=False,
   extension_ranges=[],
-  serialized_start=4332,
-  serialized_end=4377,
+  serialized_start=4333,
+  serialized_end=4378,
 )
 
 _FIELDOPTIONS = _descriptor.Descriptor(
@@ -1361,8 +1361,8 @@
   options=None,
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
-  serialized_start=3882,
-  serialized_end=4554,
+  serialized_start=3883,
+  serialized_end=4555,
 )
 
 
@@ -1410,8 +1410,8 @@
   options=None,
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
-  serialized_start=4557,
-  serialized_end=4710,
+  serialized_start=4558,
+  serialized_end=4711,
 )
 
 
@@ -1445,8 +1445,8 @@
   options=None,
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
-  serialized_start=4712,
-  serialized_end=4828,
+  serialized_start=4713,
+  serialized_end=4829,
 )
 
 
@@ -1494,8 +1494,8 @@
   options=None,
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
-  serialized_start=4831,
-  serialized_end=5013,
+  serialized_start=4832,
+  serialized_end=5014,
 )
 
 
@@ -1658,8 +1658,8 @@
   options=None,
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
-  serialized_start=5016,
-  serialized_end=5993,
+  serialized_start=5017,
+  serialized_end=5994,
 )
 
 
@@ -1764,8 +1764,8 @@
   options=None,
   is_extendable=True,
   extension_ranges=[(1000, 536870912), ],
-  serialized_start=5996,
-  serialized_end=6515,
+  serialized_start=5997,
+  serialized_end=6516,
 )
 
 
@@ -1799,8 +1799,8 @@
   options=None,
   is_extendable=False,
   extension_ranges=[],
-  serialized_start=6744,
-  serialized_end=6795,
+  serialized_start=6745,
+  serialized_end=6796,
 )
 
 _UNINTERPRETEDOPTION = _descriptor.Descriptor(
@@ -1868,8 +1868,8 @@
   options=None,
   is_extendable=False,
   extension_ranges=[],
-  serialized_start=6518,
-  serialized_end=6795,
+  serialized_start=6519,
+  serialized_end=6796,
 )
 
 
@@ -1917,8 +1917,8 @@
   options=None,
   is_extendable=False,
   extension_ranges=[],
-  serialized_start=6867,
-  serialized_end=6966,
+  serialized_start=6868,
+  serialized_end=6967,
 )
 
 _SOURCECODEINFO = _descriptor.Descriptor(
@@ -1944,8 +1944,8 @@
   options=None,
   is_extendable=False,
   extension_ranges=[],
-  serialized_start=6798,
-  serialized_end=6966,
+  serialized_start=6799,
+  serialized_end=6967,
 )
 
 _FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO
diff --git a/google/net/proto2/python/internal/containers.py b/google/net/proto2/python/internal/containers.py
index d46189c..fbaae4f 100644
--- a/google/net/proto2/python/internal/containers.py
+++ b/google/net/proto2/python/internal/containers.py
@@ -16,8 +16,6 @@
 #
 
 
-
-
 """Contains container classes to represent different protocol buffer types.
 
 This file defines container classes which represent categories of protocol
@@ -132,8 +130,13 @@
 
   def __setitem__(self, key, value):
     """Sets the item on the specified position."""
-    self._values[key] = self._type_checker.CheckValue(value)
-    self._message_listener.Modified()
+    if isinstance(key, slice):
+      if key.step is not None:
+        raise ValueError('Extended slices not supported')
+      self.__setslice__(key.start, key.stop, value)
+    else:
+      self._values[key] = self._type_checker.CheckValue(value)
+      self._message_listener.Modified()
 
   def __getslice__(self, start, stop):
     """Retrieves the subset of items from between the specified indices."""
diff --git a/google/net/proto2/python/internal/decoder.py b/google/net/proto2/python/internal/decoder.py
index 618d19e..bb34fb9 100644
--- a/google/net/proto2/python/internal/decoder.py
+++ b/google/net/proto2/python/internal/decoder.py
@@ -70,6 +70,8 @@
 
 
 import struct
+import sys
+_PY2 = sys.version_info[0] < 3
 from google.net.proto2.python.internal import encoder
 from google.net.proto2.python.internal import wire_format
 from google.net.proto2.python.public import message
@@ -98,11 +100,13 @@
   """
 
   local_ord = ord
+  py2 = _PY2
+
   def DecodeVarint(buffer, pos):
     result = 0
     shift = 0
     while 1:
-      b = local_ord(buffer[pos])
+      b = local_ord(buffer[pos]) if py2 else buffer[pos]
       result |= ((b & 0x7f) << shift)
       pos += 1
       if not (b & 0x80):
@@ -119,11 +123,13 @@
   """Like _VarintDecoder() but decodes signed values."""
 
   local_ord = ord
+  py2 = _PY2
+
   def DecodeVarint(buffer, pos):
     result = 0
     shift = 0
     while 1:
-      b = local_ord(buffer[pos])
+      b = local_ord(buffer[pos]) if py2 else buffer[pos]
       result |= ((b & 0x7f) << shift)
       pos += 1
       if not (b & 0x80):
@@ -162,8 +168,10 @@
   use that, but not in Python.
   """
 
+  py2 = _PY2
+
   start = pos
-  while ord(buffer[pos]) & 0x80:
+  while (ord(buffer[pos]) if py2 else buffer[pos]) & 0x80:
     pos += 1
   pos += 1
   return (buffer[start:pos], pos)
@@ -278,6 +286,7 @@
   """
 
   local_unpack = struct.unpack
+  b = (lambda x:x) if _PY2 else lambda x:x.encode('latin1')
 
   def InnerDecode(buffer, pos):
 
@@ -288,13 +297,17 @@
 
 
 
-    if ((float_bytes[3] in '\x7F\xFF')
-        and (float_bytes[2] >= '\x80')):
+    if ((float_bytes[3:4] in b('\x7F\xFF'))
 
-      if float_bytes[0:3] != '\x00\x00\x80':
+        and (float_bytes[2:3] >= b('\x80'))):
+
+
+      if float_bytes[0:3] != b('\x00\x00\x80'):
+
         return (_NAN, new_pos)
 
-      if float_bytes[3] == '\xFF':
+      if float_bytes[3:4] == b('\xFF'):
+
         return (_NEG_INF, new_pos)
       return (_POS_INF, new_pos)
 
@@ -313,6 +326,7 @@
   """
 
   local_unpack = struct.unpack
+  b = (lambda x:x) if _PY2 else lambda x:x.encode('latin1')
 
   def InnerDecode(buffer, pos):
 
@@ -323,9 +337,12 @@
 
 
 
-    if ((double_bytes[7] in '\x7F\xFF')
-        and (double_bytes[6] >= '\xF0')
-        and (double_bytes[0:7] != '\x00\x00\x00\x00\x00\x00\xF0')):
+
+
+
+    if ((double_bytes[7:8] in b('\x7F\xFF'))
+        and (double_bytes[6:7] >= b('\xF0'))
+        and (double_bytes[0:7] != b('\x00\x00\x00\x00\x00\x00\xF0'))):
       return (_NAN, new_pos)
 
 
@@ -710,7 +727,9 @@
 def _SkipVarint(buffer, pos, end):
   """Skip a varint value.  Returns the new position."""
 
-  while ord(buffer[pos]) & 0x80:
+
+
+  while ord(buffer[pos:pos+1]) & 0x80:
     pos += 1
   pos += 1
   if pos > end:
@@ -777,7 +796,6 @@
       ]
 
   wiretype_mask = wire_format.TAG_TYPE_MASK
-  local_ord = ord
 
   def SkipField(buffer, pos, end, tag_bytes):
     """Skips a field with the specified tag.
@@ -790,7 +808,7 @@
     """
 
 
-    wire_type = local_ord(tag_bytes[0]) & wiretype_mask
+    wire_type = ord(tag_bytes[0:1]) & wiretype_mask
     return WIRETYPE_TO_SKIPPER[wire_type](buffer, pos, end)
 
   return SkipField
diff --git a/google/net/proto2/python/internal/encoder.py b/google/net/proto2/python/internal/encoder.py
index f31d6f2..745a01e 100644
--- a/google/net/proto2/python/internal/encoder.py
+++ b/google/net/proto2/python/internal/encoder.py
@@ -56,6 +56,8 @@
 
 
 import struct
+import sys
+_PY2 = sys.version_info[0] < 3
 from google.net.proto2.python.internal import wire_format
 
 
@@ -329,7 +331,8 @@
 def _VarintEncoder():
   """Return an encoder for a basic varint value (does not include tag)."""
 
-  local_chr = chr
+  local_chr = _PY2 and chr or (lambda x: bytes((x,)))
+
   def EncodeVarint(write, value):
     bits = value & 0x7f
     value >>= 7
@@ -346,7 +349,8 @@
   """Return an encoder for a basic signed varint value (does not include
   tag)."""
 
-  local_chr = chr
+  local_chr = _PY2 and chr or (lambda x: bytes((x,)))
+
   def EncodeSignedVarint(write, value):
     if value < 0:
       value += (1 << 64)
@@ -371,7 +375,8 @@
 
   pieces = []
   _EncodeVarint(pieces.append, value)
-  return "".join(pieces)
+  return "".encode("latin1").join(pieces)
+
 
 
 def TagBytes(field_number, wire_type):
@@ -509,26 +514,33 @@
       format:  The format string to pass to struct.pack().
   """
 
+  b = _PY2 and (lambda x:x) or (lambda x:x.encode('latin1'))
   value_size = struct.calcsize(format)
   if value_size == 4:
     def EncodeNonFiniteOrRaise(write, value):
 
       if value == _POS_INF:
-        write('\x00\x00\x80\x7F')
+        write(b('\x00\x00\x80\x7F'))
+
       elif value == _NEG_INF:
-        write('\x00\x00\x80\xFF')
+        write(b('\x00\x00\x80\xFF'))
+
       elif value != value:
-        write('\x00\x00\xC0\x7F')
+        write(b('\x00\x00\xC0\x7F'))
+
       else:
         raise
   elif value_size == 8:
     def EncodeNonFiniteOrRaise(write, value):
       if value == _POS_INF:
-        write('\x00\x00\x00\x00\x00\x00\xF0\x7F')
+        write(b('\x00\x00\x00\x00\x00\x00\xF0\x7F'))
+
       elif value == _NEG_INF:
-        write('\x00\x00\x00\x00\x00\x00\xF0\xFF')
+        write(b('\x00\x00\x00\x00\x00\x00\xF0\xFF'))
+
       elif value != value:
-        write('\x00\x00\x00\x00\x00\x00\xF8\x7F')
+        write(b('\x00\x00\x00\x00\x00\x00\xF8\x7F'))
+
       else:
         raise
   else:
@@ -604,8 +616,10 @@
 def BoolEncoder(field_number, is_repeated, is_packed):
   """Returns an encoder for a boolean field."""
 
-  false_byte = chr(0)
-  true_byte = chr(1)
+
+
+  false_byte = '\x00'.encode('latin1')
+  true_byte = '\x01'.encode('latin1')
   if is_packed:
     tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
     local_EncodeVarint = _EncodeVarint
@@ -741,7 +755,8 @@
       }
     }
   """
-  start_bytes = "".join([
+  start_bytes = "".encode("latin1").join([
+
       TagBytes(1, wire_format.WIRETYPE_START_GROUP),
       TagBytes(2, wire_format.WIRETYPE_VARINT),
       _VarintBytes(field_number),
diff --git a/google/net/proto2/python/internal/python_message.py b/google/net/proto2/python/internal/python_message.py
index 225404f..cc891bd 100644
--- a/google/net/proto2/python/internal/python_message.py
+++ b/google/net/proto2/python/internal/python_message.py
@@ -39,11 +39,16 @@
 """
 
 
-try:
-  from cStringIO import StringIO
-except ImportError:
-  from StringIO import StringIO
-import copy_reg
+import sys
+if sys.version_info[0] < 3:
+  try:
+    from cStringIO import StringIO as BytesIO
+  except ImportError:
+    from StringIO import StringIO as BytesIO
+  import copy_reg as copyreg
+else:
+  from io import BytesIO
+  import copyreg
 import struct
 import weakref
 
@@ -88,7 +93,7 @@
   _AddStaticMethods(cls)
   _AddMessageMethods(descriptor, cls)
   _AddPrivateHelperMethods(cls)
-  copy_reg.pickle(cls, lambda obj: (cls, (), obj.__getstate__()))
+  copyreg.pickle(cls, lambda obj: (cls, (), obj.__getstate__()))
 
 
 
@@ -762,7 +767,7 @@
   """Helper for _AddMessageMethods()."""
 
   def SerializePartialToString(self):
-    out = StringIO()
+    out = BytesIO()
     self._InternalSerialize(out.write)
     return out.getvalue()
   cls.SerializePartialToString = SerializePartialToString
@@ -785,7 +790,8 @@
 
 
         raise message_mod.DecodeError('Unexpected end-group tag.')
-    except IndexError:
+    except (IndexError, TypeError):
+
       raise message_mod.DecodeError('Truncated message.')
     except struct.error, e:
       raise message_mod.DecodeError(e)
diff --git a/google/net/proto2/python/internal/type_checkers.py b/google/net/proto2/python/internal/type_checkers.py
index c0330e7..0242c2d 100644
--- a/google/net/proto2/python/internal/type_checkers.py
+++ b/google/net/proto2/python/internal/type_checkers.py
@@ -34,6 +34,8 @@
 """
 
 
+import sys
+if sys.version < '2.6': bytes = str
 from google.net.proto2.python.internal import api_implementation
 from google.net.proto2.python.internal import decoder
 from google.net.proto2.python.internal import encoder
@@ -132,18 +134,18 @@
   """
 
   def CheckValue(self, proposed_value):
-    if not isinstance(proposed_value, (str, unicode)):
+    if not isinstance(proposed_value, (bytes, unicode)):
       message = ('%.1024r has type %s, but expected one of: %s' %
-                 (proposed_value, type(proposed_value), (str, unicode)))
+                 (proposed_value, type(proposed_value), (bytes, unicode)))
       raise TypeError(message)
 
 
 
-    if isinstance(proposed_value, str):
+    if isinstance(proposed_value, bytes):
       try:
-        proposed_value = unicode(proposed_value, 'ascii')
+        proposed_value = proposed_value.decode('ascii')
       except UnicodeDecodeError:
-        raise ValueError('%.1024r has type str, but isn\'t in 7-bit ASCII '
+        raise ValueError('%.1024r has type bytes, but isn\'t in 7-bit ASCII '
                          'encoding. Non-ASCII strings must be converted to '
                          'unicode objects before being added.' %
                          (proposed_value))
@@ -187,7 +189,7 @@
     _FieldDescriptor.CPPTYPE_FLOAT: TypeChecker(
         float, int, long),
     _FieldDescriptor.CPPTYPE_BOOL: TypeChecker(bool, int),
-    _FieldDescriptor.CPPTYPE_STRING: TypeChecker(str),
+    _FieldDescriptor.CPPTYPE_STRING: TypeChecker(bytes),
     }
 
 
diff --git a/google/net/proto2/python/public/text_encoding.py b/google/net/proto2/python/public/text_encoding.py
index f586fbf..418141e 100644
--- a/google/net/proto2/python/public/text_encoding.py
+++ b/google/net/proto2/python/public/text_encoding.py
@@ -14,9 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+
+
 """Encoding related utilities."""
 
 import re
+import sys
 
 
 _cescape_utf8_to_str = [chr(i) for i in xrange(0, 256)]
@@ -42,7 +45,7 @@
 
 
 def CEscape(text, as_utf8):
-  """Escape a string for use in an ascii protocol buffer.
+  """Escape a bytes string for use in an ascii protocol buffer.
 
   text.encode('string_escape') does not seem to satisfy our needs as it
   encodes unprintable characters using two-digit hex escapes whereas our
@@ -51,22 +54,26 @@
   decoded in C++ as a single-character string with char code 0x11.
 
   Args:
-    text: A string to be escaped
+    text: A byte string to be escaped
     as_utf8: Specifies if result should be returned in UTF-8 encoding
   Returns:
     Escaped string
   """
 
+
+  Ord = ord if isinstance(text, basestring) else lambda x: x
   if as_utf8:
-    return ''.join(_cescape_utf8_to_str[ord(c)] for c in text)
-  return ''.join(_cescape_byte_to_str[ord(c)] for c in text)
+    return ''.join(_cescape_utf8_to_str[Ord(c)] for c in text)
+  return ''.join(_cescape_byte_to_str[Ord(c)] for c in text)
 
 
 _CUNESCAPE_HEX = re.compile(r'(\\+)x([0-9a-fA-F])(?![0-9a-fA-F])')
+_cescape_highbit_to_str = ([chr(i) for i in range(0, 127)] +
+                           [r'\%03o' % i for i in range(127, 256)])
 
 
 def CUnescape(text):
-  """Unescape a text string with C-style escape sequences."""
+  """Unescape a text string with C-style escape sequences to UTF-8 bytes."""
 
   def ReplaceHex(m):
 
@@ -78,4 +85,12 @@
 
 
   result = _CUNESCAPE_HEX.sub(ReplaceHex, text)
-  return result.decode('string_escape')
+
+  if sys.version_info[0] < 3:
+
+    return result.decode('string_escape')
+  result = ''.join(_cescape_highbit_to_str[ord(c)] for c in result)
+  return (result.encode('ascii')
+          .decode('unicode_escape')
+
+          .encode('raw_unicode_escape'))
diff --git a/google/net/proto2/python/public/text_format.py b/google/net/proto2/python/public/text_format.py
index 7141b27..d199c22 100644
--- a/google/net/proto2/python/public/text_format.py
+++ b/google/net/proto2/python/public/text_format.py
@@ -16,6 +16,8 @@
 #
 
 
+
+
 """Contains routines for printing protocol messages in text format."""
 
 
@@ -183,6 +185,7 @@
   Raises:
     ParseError: On ASCII parsing problems.
   """
+  if not isinstance(text, str): text = text.decode('utf-8')
   return ParseLines(text.split('\n'), message)
 
 
@@ -621,7 +624,8 @@
     the_list = [self._ConsumeSingleByteString()]
     while self.token and self.token[0] in ('\'', '"'):
       the_list.append(self._ConsumeSingleByteString())
-    return ''.join(the_list)
+    return ''.encode('latin1').join(the_list)
+
 
   def _ConsumeSingleByteString(self):
     """Consume one token of a string literal.
diff --git a/google/storage/speckle/proto/client_error_code_pb2.py b/google/storage/speckle/proto/client_error_code_pb2.py
index 8fc322a..180432a 100644
--- a/google/storage/speckle/proto/client_error_code_pb2.py
+++ b/google/storage/speckle/proto/client_error_code_pb2.py
@@ -17,6 +17,8 @@
 
 
 
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.net.proto2.python.public import descriptor as _descriptor
 from google.net.proto2.python.public import message as _message
 from google.net.proto2.python.public import reflection as _reflection
@@ -29,7 +31,8 @@
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='storage/speckle/proto/client_error_code.proto',
   package='speckle.sql',
-  serialized_pb='\n-storage/speckle/proto/client_error_code.proto\x12\x0bspeckle.sql\"\xff\x07\n\x15SqlServiceClientError\"\xe5\x07\n\x0f\x43lientErrorCode\x12\x06\n\x02OK\x10\x00\x12\x13\n\x0fTRANSIENT_ERROR\x10\x01\x12\x12\n\x0eINTERNAL_ERROR\x10\x02\x12\x13\n\x0fINVALID_REQUEST\x10\x03\x12\x16\n\x12\x44\x45PRECATED_TIMEOUT\x10\x04\x12\x1d\n\x19\x44\x45PRECATED_NOT_AUTHORIZED\x10\x05\x12\x1a\n\x16\x44\x45PRECATED_RDBMS_ERROR\x10\x06\x12\"\n\x1d\x45RROR_PUBLIC_ERROR_CODE_START\x10\xe8\x07\x12\x10\n\x0b\x45RROR_RDBMS\x10\xe9\x07\x12\x12\n\rERROR_TIMEOUT\x10\xea\x07\x12\x19\n\x14\x45RROR_NOT_AUTHORIZED\x10\xeb\x07\x12\x1d\n\x18\x45RROR_INSTANCE_SUSPENDED\x10\xec\x07\x12\x1c\n\x17\x45RROR_INVALID_PARAMETER\x10\xed\x07\x12\"\n\x1d\x45RROR_NOT_ALL_VARIABLES_BOUND\x10\xee\x07\x12\x1d\n\x18\x45RROR_UNKNOWN_CONNECTION\x10\xef\x07\x12\x1c\n\x17\x45RROR_UNKNOWN_STATEMENT\x10\xf0\x07\x12\x1a\n\x15\x45RROR_UNKNOWN_CATALOG\x10\xf1\x07\x12\x19\n\x14\x45RROR_UNKNOWN_CURSOR\x10\xf2\x07\x12\x1b\n\x16\x45RROR_CURSOR_EXHAUSTED\x10\xfc\x07\x12\x1e\n\x19\x45RROR_NOT_YET_IMPLEMENTED\x10\x86\x08\x12\x1a\n\x15\x45RROR_NOT_IMPLEMENTED\x10\x87\x08\x12\x1f\n\x1a\x45RROR_INSTANCE_MAINTENANCE\x10\x88\x08\x12\'\n\"ERROR_TOO_MANY_CONCURRENT_REQUESTS\x10\x89\x08\x12\"\n\x1d\x45RROR_RESOURCE_DOES_NOT_EXIST\x10\x8a\x08\x12\"\n\x1d\x45RROR_RESOURCE_ALREADY_EXISTS\x10\x8b\x08\x12\x1c\n\x17\x45RROR_CONNECTION_IN_USE\x10\x8c\x08\x12!\n\x1c\x45RROR_CLIENT_VERSION_TOO_OLD\x10\x8d\x08\x12\x1b\n\x16\x45RROR_RESPONSE_PENDING\x10\x8e\x08\x12(\n#ERROR_INSTANCE_SUSPENDED_BY_BILLING\x10\x8f\x08\x12\x1e\n\x19\x45RROR_RESULTSET_TOO_LARGE\x10\x90\x08\x12)\n$ERROR_ACTIVATION_POLICY_SET_TO_NEVER\x10\x91\x08\x12&\n!ERROR_INSTANCE_SUSPENDED_BY_LEGAL\x10\x92\x08\x12\x19\n\x14\x45RROR_QUOTA_EXCEEDED\x10\x93\x08\x42%\n\x1b\x63om.google.protos.cloud.sql\x10\x02 \x02(\x02P\x01')
+  serialized_pb=_b('\n-storage/speckle/proto/client_error_code.proto\x12\x0bspeckle.sql\"\xff\x07\n\x15SqlServiceClientError\"\xe5\x07\n\x0f\x43lientErrorCode\x12\x06\n\x02OK\x10\x00\x12\x13\n\x0fTRANSIENT_ERROR\x10\x01\x12\x12\n\x0eINTERNAL_ERROR\x10\x02\x12\x13\n\x0fINVALID_REQUEST\x10\x03\x12\x16\n\x12\x44\x45PRECATED_TIMEOUT\x10\x04\x12\x1d\n\x19\x44\x45PRECATED_NOT_AUTHORIZED\x10\x05\x12\x1a\n\x16\x44\x45PRECATED_RDBMS_ERROR\x10\x06\x12\"\n\x1d\x45RROR_PUBLIC_ERROR_CODE_START\x10\xe8\x07\x12\x10\n\x0b\x45RROR_RDBMS\x10\xe9\x07\x12\x12\n\rERROR_TIMEOUT\x10\xea\x07\x12\x19\n\x14\x45RROR_NOT_AUTHORIZED\x10\xeb\x07\x12\x1d\n\x18\x45RROR_INSTANCE_SUSPENDED\x10\xec\x07\x12\x1c\n\x17\x45RROR_INVALID_PARAMETER\x10\xed\x07\x12\"\n\x1d\x45RROR_NOT_ALL_VARIABLES_BOUND\x10\xee\x07\x12\x1d\n\x18\x45RROR_UNKNOWN_CONNECTION\x10\xef\x07\x12\x1c\n\x17\x45RROR_UNKNOWN_STATEMENT\x10\xf0\x07\x12\x1a\n\x15\x45RROR_UNKNOWN_CATALOG\x10\xf1\x07\x12\x19\n\x14\x45RROR_UNKNOWN_CURSOR\x10\xf2\x07\x12\x1b\n\x16\x45RROR_CURSOR_EXHAUSTED\x10\xfc\x07\x12\x1e\n\x19\x45RROR_NOT_YET_IMPLEMENTED\x10\x86\x08\x12\x1a\n\x15\x45RROR_NOT_IMPLEMENTED\x10\x87\x08\x12\x1f\n\x1a\x45RROR_INSTANCE_MAINTENANCE\x10\x88\x08\x12\'\n\"ERROR_TOO_MANY_CONCURRENT_REQUESTS\x10\x89\x08\x12\"\n\x1d\x45RROR_RESOURCE_DOES_NOT_EXIST\x10\x8a\x08\x12\"\n\x1d\x45RROR_RESOURCE_ALREADY_EXISTS\x10\x8b\x08\x12\x1c\n\x17\x45RROR_CONNECTION_IN_USE\x10\x8c\x08\x12!\n\x1c\x45RROR_CLIENT_VERSION_TOO_OLD\x10\x8d\x08\x12\x1b\n\x16\x45RROR_RESPONSE_PENDING\x10\x8e\x08\x12(\n#ERROR_INSTANCE_SUSPENDED_BY_BILLING\x10\x8f\x08\x12\x1e\n\x19\x45RROR_RESULTSET_TOO_LARGE\x10\x90\x08\x12)\n$ERROR_ACTIVATION_POLICY_SET_TO_NEVER\x10\x91\x08\x12&\n!ERROR_INSTANCE_SUSPENDED_BY_LEGAL\x10\x92\x08\x12\x19\n\x14\x45RROR_QUOTA_EXCEEDED\x10\x93\x08\x42%\n\x1b\x63om.google.protos.cloud.sql\x10\x02 \x02(\x02P\x01')
+)
 
 
 
@@ -200,16 +203,16 @@
   serialized_end=1086,
 )
 
-_SQLSERVICECLIENTERROR_CLIENTERRORCODE.containing_type = _SQLSERVICECLIENTERROR;
+_SQLSERVICECLIENTERROR_CLIENTERRORCODE.containing_type = _SQLSERVICECLIENTERROR
 DESCRIPTOR.message_types_by_name['SqlServiceClientError'] = _SQLSERVICECLIENTERROR
 
-class SqlServiceClientError(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _SQLSERVICECLIENTERROR
+SqlServiceClientError = _reflection.GeneratedProtocolMessageType('SqlServiceClientError', (_message.Message,), dict(
+  DESCRIPTOR = _SQLSERVICECLIENTERROR,
+  __module__ = 'google.storage.speckle.proto.client_error_code_pb2'
 
-
+  ))
 
 
 DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), '\n\033com.google.protos.cloud.sql\020\002 \002(\002P\001')
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033com.google.protos.cloud.sql\020\002 \002(\002P\001'))
 
diff --git a/google/storage/speckle/proto/client_pb2.py b/google/storage/speckle/proto/client_pb2.py
index 1ec7079..d91e6b3 100644
--- a/google/storage/speckle/proto/client_pb2.py
+++ b/google/storage/speckle/proto/client_pb2.py
@@ -17,6 +17,8 @@
 
 
 
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.net.proto2.python.internal import enum_type_wrapper
 from google.net.proto2.python.public import descriptor as _descriptor
 from google.net.proto2.python.public import message as _message
@@ -30,7 +32,8 @@
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='storage/speckle/proto/client.proto',
   package='speckle',
-  serialized_pb='\n\"storage/speckle/proto/client.proto\x12\x07speckle\"\xb6\x01\n\x11\x42indVariableProto\x12\r\n\x05value\x18\x01 \x01(\x0c\x12\x0c\n\x04type\x18\x02 \x01(\x05\x12\x10\n\x08position\x18\x03 \x01(\x05\x12\x0c\n\x04name\x18\x04 \x01(\t\x12;\n\tdirection\x18\x05 \x01(\x0e\x32$.speckle.BindVariableProto.Direction:\x02IN\"\'\n\tDirection\x12\x06\n\x02IN\x10\x01\x12\x07\n\x03OUT\x10\x02\x12\t\n\x05INOUT\x10\x03\"\x8c\x03\n\x0bResultProto\x12\"\n\x04rows\x18\x01 \x01(\x0b\x32\x14.speckle.RowSetProto\x12\x14\n\x0crows_updated\x18\x02 \x01(\x03\x12\x16\n\x0egenerated_keys\x18\x03 \x03(\x0c\x12\'\n\x08warnings\x18\x04 \x03(\x0b\x32\x15.speckle.SqlException\x12,\n\rsql_exception\x18\x05 \x01(\x0b\x32\x15.speckle.SqlException\x12\x14\n\x0cstatement_id\x18\x06 \x01(\x04\x12\x18\n\tmore_rows\x18\x07 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cmore_results\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\x33\n\x0foutput_variable\x18\t \x03(\x0b\x32\x1a.speckle.BindVariableProto\x12\x1a\n\x12\x62\x61tch_rows_updated\x18\n \x03(\x03\x12\x36\n\x12parameter_metadata\x18\x0b \x03(\x0b\x32\x1a.speckle.ParameterMetadata\"\xf1\x05\n\x07OpProto\x12%\n\x04type\x18\x01 \x02(\x0e\x32\x17.speckle.OpProto.OpType\x12\x0f\n\x07\x63\x61talog\x18\x02 \x01(\t\x12\x0b\n\x03sql\x18\x03 \x01(\t\x12%\n\tsavepoint\x18\x04 \x01(\x0b\x32\x12.speckle.SavePoint\x12\x13\n\x0b\x61uto_commit\x18\x05 \x01(\x08\x12\x11\n\tread_only\x18\x06 \x01(\x08\x12G\n\x1btransaction_isolation_level\x18\x07 \x01(\x0e\x32\".speckle.TransactionIsolationLevel\x12\x14\n\x0cstatement_id\x18\x08 \x01(\x04\x12\x12\n\nrequest_id\x18\t \x01(\x04\"\xde\x03\n\x06OpType\x12\x0e\n\nNATIVE_SQL\x10\x01\x12\x0c\n\x08ROLLBACK\x10\x02\x12\x11\n\rSET_SAVEPOINT\x10\x03\x12\x13\n\x0fSET_AUTO_COMMIT\x10\x04\x12\x11\n\rSET_READ_ONLY\x10\x05\x12#\n\x1fSET_TRANSACTION_ISOLATION_LEVEL\x10\x06\x12\n\n\x06\x43OMMIT\x10\x07\x12\x0f\n\x0bSET_CATALOG\x10\x08\x12\x13\n\x0f\x43LOSE_STATEMENT\x10\t\x12\x08\n\x04PING\x10\n\x12\x0f\n\x0bNEXT_RESULT\x10\x0b\x12\t\n\x05RETRY\x10\x0c\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE13\x10\r\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE14\x10\x0e\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE15\x10\x0f\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE16\x10\x10\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE17\x10\x11\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE18\x10\x12\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE19\x10\x13\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE20\x10\x14\"%\n\tSavePoint\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x02(\t\"c\n\x0cSqlException\x12\x0f\n\x07message\x18\x01 \x02(\t\x12\x0f\n\x04\x63ode\x18\x02 \x02(\x05:\x01\x30\x12\x11\n\tsql_state\x18\x03 \x01(\t\x12\x1e\n\x16\x61pplication_error_code\x18\x04 \x01(\x05\"+\n\nTupleProto\x12\x0e\n\x06values\x18\x01 \x03(\x0c\x12\r\n\x05nulls\x18\x02 \x03(\x05\"\xc0\x03\n\x0b\x43olumnProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x04type\x18\x03 \x01(\x05:\x02\x31\x32\x12\x12\n\ntable_name\x18\x04 \x01(\t\x12\x13\n\x0bschema_name\x18\x05 \x01(\t\x12\x14\n\x0c\x63\x61talog_name\x18\x06 \x01(\t\x12\x14\n\tprecision\x18\x07 \x01(\x05:\x01\x30\x12\x10\n\x05scale\x18\x08 \x01(\x05:\x01\x30\x12\x10\n\x08nullable\x18\t \x01(\x08\x12\x12\n\nsearchable\x18\n \x01(\x08\x12\x14\n\x0c\x64isplay_size\x18\x0b \x01(\x05\x12\x1d\n\x0e\x61uto_increment\x18\x0c \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x63\x61se_sensitive\x18\r \x01(\x08:\x05\x66\x61lse\x12\x17\n\x08\x63urrency\x18\x0e \x01(\x08:\x05\x66\x61lse\x12\"\n\x13\x64\x65\x66initely_writable\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x18\n\tread_only\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06signed\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\x17\n\x08writable\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x10\x63olumn_type_name\x18\x13 \x01(\t:\x00\"Y\n\x0bRowSetProto\x12%\n\x07\x63olumns\x18\x01 \x03(\x0b\x32\x14.speckle.ColumnProto\x12#\n\x06tuples\x18\x02 \x03(\x0b\x32\x13.speckle.TupleProto\"\xcb\x36\n\x19JdbcDatabaseMetaDataProto\x12*\n\x1b\x61ll_procedures_are_callable\x18\x01 \x01(\x08:\x05\x66\x61lse\x12(\n\x19\x61ll_tables_are_selectable\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x39\n*auto_commit_failure_closes_all_result_sets\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x38\n)data_definition_causes_transaction_commit\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x36\n\'data_definition_ignored_in_transactions\x18\x05 \x01(\x08:\x05\x66\x61lse\x12.\n\x1f\x64oes_max_row_size_include_blobs\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x19\n\x11\x63\x61talog_separator\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61talog_term\x18\x08 \x01(\t\x12!\n\x16\x64\x61tabase_major_version\x18\t \x01(\x05:\x01\x30\x12!\n\x16\x64\x61tabase_minor_version\x18\n \x01(\x05:\x01\x30\x12&\n\x15\x64\x61tabase_product_name\x18\x0b \x01(\t:\x07Speckle\x12\"\n\x18\x64\x61tabase_product_version\x18\x0c \x01(\t:\x00\x12u\n\x1d\x64\x65\x66\x61ult_transaction_isolation\x18\r \x01(\x0e\x32\".speckle.TransactionIsolationLevel:*TRANSACTIONISOLATIONLEVEL_TRANSACTION_NONE\x12\x1f\n\x15\x65xtra_name_characters\x18\x0e \x01(\t:\x00\x12!\n\x17identifier_quote_string\x18\x0f \x01(\t:\x00\x12\x1d\n\x12jdbc_major_version\x18\x10 \x01(\x05:\x01\x31\x12\x1d\n\x12jdbc_minor_version\x18\x11 \x01(\x05:\x01\x30\x12$\n\x19max_binary_literal_length\x18\x12 \x01(\x05:\x01\x30\x12\"\n\x17max_catalog_name_length\x18\x13 \x01(\x05:\x01\x30\x12\"\n\x17max_char_literal_length\x18\x14 \x01(\x05:\x01\x30\x12!\n\x16max_column_name_length\x18\x15 \x01(\x05:\x01\x30\x12\"\n\x17max_columns_in_group_by\x18\x16 \x01(\x05:\x01\x30\x12\x1f\n\x14max_columns_in_index\x18\x17 \x01(\x05:\x01\x30\x12\"\n\x17max_columns_in_order_by\x18\x18 \x01(\x05:\x01\x30\x12 \n\x15max_columns_in_select\x18\x19 \x01(\x05:\x01\x30\x12\x1f\n\x14max_columns_in_table\x18\x1a \x01(\x05:\x01\x30\x12\x1a\n\x0fmax_connections\x18\x1b \x01(\x05:\x01\x30\x12!\n\x16max_cursor_name_length\x18\x1c \x01(\x05:\x01\x30\x12\x1b\n\x10max_index_length\x18\x1d \x01(\x05:\x01\x30\x12$\n\x19max_procedure_name_length\x18\x1e \x01(\x05:\x01\x30\x12\x17\n\x0cmax_row_size\x18\x1f \x01(\x05:\x01\x30\x12!\n\x16max_schema_name_length\x18  \x01(\x05:\x01\x30\x12\x1f\n\x14max_statement_length\x18! \x01(\x05:\x01\x30\x12\x19\n\x0emax_statements\x18\" \x01(\x05:\x01\x30\x12 \n\x15max_table_name_length\x18# \x01(\x05:\x01\x30\x12\x1f\n\x14max_tables_in_select\x18$ \x01(\x05:\x01\x30\x12\x1f\n\x14max_user_name_length\x18% \x01(\x05:\x01\x30\x12\x1b\n\x11numeric_functions\x18& \x01(\t:\x00\x12\x18\n\x0eprocedure_term\x18\' \x01(\t:\x00\x12j\n\x15resultset_holdability\x18( \x01(\x0e\x32\x1d.speckle.ResultSetHoldability:,RESULTSETHOLDABILITY_CLOSE_CURSORS_AT_COMMIT\x12i\n\x0erowid_lifetime\x18) \x01(\x0e\x32\x30.speckle.JdbcDatabaseMetaDataProto.RowIdLifetime:\x1fROWIDLIFETIME_ROWID_UNSUPPORTED\x12\x14\n\x0csql_keywords\x18* \x01(\t\x12\x63\n\x0esql_state_type\x18+ \x01(\x0e\x32/.speckle.JdbcDatabaseMetaDataProto.SqlStateType:\x1aSQLSTATETYPE_SQL_STATE_SQL\x12\x15\n\x0bschema_term\x18, \x01(\t:\x00\x12\x1c\n\x14search_string_escape\x18- \x01(\t\x12\x1a\n\x10string_functions\x18. \x01(\t:\x00\x12\x1a\n\x10system_functions\x18/ \x01(\t:\x00\x12\x1d\n\x13time_date_functions\x18\x30 \x01(\t:\x00\x12\x13\n\tuser_name\x18\x31 \x01(\t:\x00\x12\x1f\n\x10\x63\x61talog_at_start\x18\x32 \x01(\x08:\x05\x66\x61lse\x12#\n\x14locators_update_copy\x18\x33 \x01(\x08:\x05\x66\x61lse\x12)\n\x1anull_plus_non_null_is_null\x18\x34 \x01(\x08:\x05\x66\x61lse\x12&\n\x17nulls_are_sorted_at_end\x18\x35 \x01(\x08:\x05\x66\x61lse\x12(\n\x19nulls_are_sorted_at_start\x18\x36 \x01(\x08:\x05\x66\x61lse\x12$\n\x15nulls_are_sorted_high\x18\x37 \x01(\x08:\x05\x66\x61lse\x12#\n\x14nulls_are_sorted_low\x18\x38 \x01(\x08:\x05\x66\x61lse\x12,\n\x1dstores_lower_case_identifiers\x18\x39 \x01(\x08:\x05\x66\x61lse\x12\x33\n$stores_lower_case_quoted_identifiers\x18: \x01(\x08:\x05\x66\x61lse\x12,\n\x1dstores_mixed_case_identifiers\x18; \x01(\x08:\x05\x66\x61lse\x12\x33\n$stores_mixed_case_quoted_identifiers\x18< \x01(\x08:\x05\x66\x61lse\x12,\n\x1dstores_upper_case_identifiers\x18= \x01(\x08:\x05\x66\x61lse\x12\x33\n$stores_upper_case_quoted_identifiers\x18> \x01(\x08:\x05\x66\x61lse\x12.\n\x1fsupports_ansi92_entry_level_sql\x18? \x01(\x08:\x05\x66\x61lse\x12\'\n\x18supports_ansi92_full_sql\x18@ \x01(\x08:\x05\x66\x61lse\x12/\n supports_ansi92_intermediate_sql\x18\x41 \x01(\x08:\x05\x66\x61lse\x12\x33\n$supports_alter_table_with_add_column\x18\x42 \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_alter_table_with_drop_column\x18\x43 \x01(\x08:\x05\x66\x61lse\x12%\n\x16supports_batch_updates\x18\x44 \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_catalogs_in_data_manipulation\x18\x45 \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_catalogs_in_index_definitions\x18\x46 \x01(\x08:\x05\x66\x61lse\x12\x39\n*supports_catalogs_in_privilege_definitions\x18G \x01(\x08:\x05\x66\x61lse\x12\x33\n$supports_catalogs_in_procedure_calls\x18H \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_catalogs_in_table_definitions\x18I \x01(\x08:\x05\x66\x61lse\x12\'\n\x18supports_column_aliasing\x18J \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10supports_convert\x18K \x01(\x08:\x05\x66\x61lse\x12(\n\x19supports_core_sql_grammar\x18L \x01(\x08:\x05\x66\x61lse\x12-\n\x1esupports_correlated_subqueries\x18M \x01(\x08:\x05\x66\x61lse\x12J\n;supports_data_definition_and_data_manipulation_transactions\x18N \x01(\x08:\x05\x66\x61lse\x12;\n,supports_data_manipulation_transactions_only\x18O \x01(\x08:\x05\x66\x61lse\x12\x39\n*supports_different_table_correlation_names\x18P \x01(\x08:\x05\x66\x61lse\x12/\n supports_expressions_in_order_by\x18Q \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_extended_sql_grammar\x18R \x01(\x08:\x05\x66\x61lse\x12(\n\x19supports_full_outer_joins\x18S \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_get_generated_keys\x18T \x01(\x08:\x05\x66\x61lse\x12 \n\x11supports_group_by\x18U \x01(\x08:\x05\x66\x61lse\x12.\n\x1fsupports_group_by_beyond_select\x18V \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_group_by_unrelated\x18W \x01(\x08:\x05\x66\x61lse\x12\x36\n\'supports_integrity_enhancement_facility\x18X \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_like_escape_clause\x18Y \x01(\x08:\x05\x66\x61lse\x12+\n\x1csupports_limited_outer_joins\x18Z \x01(\x08:\x05\x66\x61lse\x12+\n\x1csupports_minimum_sql_grammar\x18[ \x01(\x08:\x05\x66\x61lse\x12.\n\x1fsupports_mixed_case_identifiers\x18\\ \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_mixed_case_quoted_identifiers\x18] \x01(\x08:\x05\x66\x61lse\x12-\n\x1esupports_multiple_open_results\x18^ \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_multiple_result_sets\x18_ \x01(\x08:\x05\x66\x61lse\x12-\n\x1esupports_multiple_transactions\x18` \x01(\x08:\x05\x66\x61lse\x12(\n\x19supports_named_parameters\x18\x61 \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_non_nullable_columns\x18\x62 \x01(\x08:\x05\x66\x61lse\x12\x32\n#supports_open_cursors_across_commit\x18\x63 \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_open_cursors_across_rollback\x18\x64 \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_open_statements_across_commit\x18\x65 \x01(\x08:\x05\x66\x61lse\x12\x37\n(supports_open_statements_across_rollback\x18\x66 \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_order_by_unrelated\x18g \x01(\x08:\x05\x66\x61lse\x12#\n\x14supports_outer_joins\x18h \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_positioned_delete\x18i \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_positioned_update\x18j \x01(\x08:\x05\x66\x61lse\x12\"\n\x13supports_savepoints\x18k \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_schemas_in_data_manipulation\x18l \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_schemas_in_index_definitions\x18m \x01(\x08:\x05\x66\x61lse\x12\x38\n)supports_schemas_in_privilege_definitions\x18n \x01(\x08:\x05\x66\x61lse\x12\x32\n#supports_schemas_in_procedure_calls\x18o \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_schemas_in_table_definitions\x18p \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_select_for_update\x18q \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_statement_pooling\x18r \x01(\x08:\x05\x66\x61lse\x12:\n+supports_stored_functions_using_call_syntax\x18s \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_stored_procedures\x18t \x01(\x08:\x05\x66\x61lse\x12\x31\n\"supports_subqueries_in_comparisons\x18u \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_subqueries_in_exists\x18v \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_subqueries_in_ins\x18w \x01(\x08:\x05\x66\x61lse\x12\x31\n\"supports_subqueries_in_quantifieds\x18x \x01(\x08:\x05\x66\x61lse\x12/\n supports_table_correlation_names\x18y \x01(\x08:\x05\x66\x61lse\x12$\n\x15supports_transactions\x18z \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0esupports_union\x18{ \x01(\x08:\x05\x66\x61lse\x12!\n\x12supports_union_all\x18| \x01(\x08:\x05\x66\x61lse\x12(\n\x19uses_local_file_per_table\x18} \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10uses_local_files\x18~ \x01(\x08:\x05\x66\x61lse\x12\x18\n\tread_only\x18\x7f \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0btable_types\x18\x80\x01 \x03(\t\x12\x11\n\x08\x63\x61talogs\x18\x81\x01 \x03(\t\x12;\n\x07schemas\x18\x82\x01 \x03(\x0b\x32).speckle.JdbcDatabaseMetaDataProto.Schema\x12\x35\n\x14\x64\x65letes_are_detected\x18\x83\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x35\n\x14inserts_are_detected\x18\x84\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x35\n\x14updates_are_detected\x18\x85\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12;\n\x1aothers_deletes_are_visible\x18\x86\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12;\n\x1aothers_inserts_are_visible\x18\x87\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12;\n\x1aothers_updates_are_visible\x18\x88\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x38\n\x17own_deletes_are_visible\x18\x89\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x38\n\x17own_inserts_are_visible\x18\x8a\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x38\n\x17own_updates_are_visible\x18\x8b\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12J\n)supports_result_set_concurrency_updatable\x18\x8c\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x39\n\x18supports_result_set_type\x18\x8d\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12G\n\x1fsupports_result_set_holdability\x18\x8e\x01 \x03(\x0e\x32\x1d.speckle.ResultSetHoldability\x12Q\n$supports_transaction_isolation_level\x18\x8f\x01 \x03(\x0e\x32\".speckle.TransactionIsolationLevel\x12-\n\x1dgenerated_key_always_returned\x18\x90\x01 \x01(\x08:\x05\x66\x61lse\x1a\x35\n\x06Schema\x12\x14\n\x0ctable_schema\x18\x01 \x01(\t\x12\x15\n\rtable_catalog\x18\x02 \x01(\t\"\xd2\x01\n\rRowIdLifetime\x12#\n\x1fROWIDLIFETIME_ROWID_UNSUPPORTED\x10\x00\x12%\n!ROWIDLIFETIME_ROWID_VALID_FOREVER\x10\x01\x12#\n\x1fROWIDLIFETIME_ROWID_VALID_OTHER\x10\x02\x12%\n!ROWIDLIFETIME_ROWID_VALID_SESSION\x10\x03\x12)\n%ROWIDLIFETIME_ROWID_VALID_TRANSACTION\x10\x04\"r\n\x0cSqlStateType\x12\x1e\n\x1aSQLSTATETYPE_SQL_STATE_SQL\x10\x00\x12 \n\x1cSQLSTATETYPE_SQL_STATE_SQL99\x10\x01\x12 \n\x1cSQLSTATETYPE_SQL_STATE_XOPEN\x10\x02\"&\n\x08Property\x12\x0b\n\x03key\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xd6\x03\n\x0b\x45xecOptions\x12%\n\x16include_generated_keys\x18\x01 \x01(\x08:\x05\x66\x61lse\x12 \n\x18generated_column_indices\x18\x02 \x03(\x05\x12\x1e\n\x16generated_column_names\x18\x03 \x03(\t\x12$\n\x04type\x18\x04 \x01(\x0e\x32\x16.speckle.ResultSetType\x12\x32\n\x0b\x63oncurrency\x18\x05 \x01(\x0e\x32\x1d.speckle.ResultSetConcurrency\x12\x32\n\x0bholdability\x18\x06 \x01(\x0e\x32\x1d.speckle.ResultSetHoldability\x12\x12\n\nfetch_size\x18\x07 \x01(\x05\x12\x10\n\x08max_rows\x18\x08 \x01(\x05\x12\x17\n\x08poolable\x18\t \x01(\x08:\x05\x66\x61lse\x12?\n\x0f\x66\x65tch_direction\x18\n \x01(\x0e\x32\x17.speckle.FetchDirection:\rFETCH_FORWARD\x12\x13\n\x0b\x63ursor_name\x18\x0b \x01(\t\x12\x19\n\x0emax_field_size\x18\x0c \x01(\x05:\x01\x30\x12 \n\x11\x65scape_processing\x18\r \x01(\x08:\x05\x66\x61lse\"K\n\x16\x42\x61tchBindVariableProto\x12\x31\n\rbind_variable\x18\x01 \x03(\x0b\x32\x1a.speckle.BindVariableProto\"]\n\nBatchProto\x12\x11\n\tstatement\x18\x01 \x03(\t\x12<\n\x13\x62\x61tch_bind_variable\x18\x02 \x03(\x0b\x32\x1f.speckle.BatchBindVariableProto\"!\n\x11ParameterMetadata\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\rRpcErrorProto\x12\x12\n\nerror_code\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t*\xb4\x02\n\x19TransactionIsolationLevel\x12.\n*TRANSACTIONISOLATIONLEVEL_TRANSACTION_NONE\x10\x00\x12\x38\n4TRANSACTIONISOLATIONLEVEL_TRANSACTION_READ_COMMITTED\x10\x02\x12:\n6TRANSACTIONISOLATIONLEVEL_TRANSACTION_READ_UNCOMMITTED\x10\x01\x12\x39\n5TRANSACTIONISOLATIONLEVEL_TRANSACTION_REPEATABLE_READ\x10\x04\x12\x36\n2TRANSACTIONISOLATIONLEVEL_TRANSACTION_SERIALIZABLE\x10\x08*\x8b\x01\n\rResultSetType\x12$\n\x1fRESULTSETTYPE_TYPE_FORWARD_ONLY\x10\xeb\x07\x12*\n%RESULTSETTYPE_TYPE_SCROLL_INSENSITIVE\x10\xec\x07\x12(\n#RESULTSETTYPE_TYPE_SCROLL_SENSITIVE\x10\xed\x07*n\n\x14ResultSetConcurrency\x12*\n%RESULTSETCONCURRENCY_CONCUR_READ_ONLY\x10\xef\x07\x12*\n%RESULTSETCONCURRENCY_CONCUR_UPDATABLE\x10\xf0\x07*{\n\x14ResultSetHoldability\x12\x31\n-RESULTSETHOLDABILITY_HOLD_CURSORS_OVER_COMMIT\x10\x01\x12\x30\n,RESULTSETHOLDABILITY_CLOSE_CURSORS_AT_COMMIT\x10\x02*L\n\x0e\x46\x65tchDirection\x12\x12\n\rFETCH_FORWARD\x10\xe8\x07\x12\x12\n\rFETCH_REVERSE\x10\xe9\x07\x12\x12\n\rFETCH_UNKNOWN\x10\xea\x07*\xc4\t\n\x0cMetadataType\x12(\n$METADATATYPE_DATABASE_METADATA_BASIC\x10\x01\x12-\n)METADATATYPE_DATABASE_METADATA_GET_TABLES\x10\x02\x12\x31\n-METADATATYPE_DATABASE_METADATA_GET_PROCEDURES\x10\x03\x12\x38\n4METADATATYPE_DATABASE_METADATA_GET_PROCEDURE_COLUMNS\x10\x04\x12.\n*METADATATYPE_DATABASE_METADATA_GET_COLUMNS\x10\x05\x12\x38\n4METADATATYPE_DATABASE_METADATA_GET_COLUMN_PRIVILEGES\x10\x06\x12\x37\n3METADATATYPE_DATABASE_METADATA_GET_TABLE_PRIVILEGES\x10\x07\x12:\n6METADATATYPE_DATABASE_METADATA_GET_BEST_ROW_IDENTIFIER\x10\x08\x12\x36\n2METADATATYPE_DATABASE_METADATA_GET_VERSION_COLUMNS\x10\t\x12\x33\n/METADATATYPE_DATABASE_METADATA_GET_PRIMARY_KEYS\x10\n\x12\x34\n0METADATATYPE_DATABASE_METADATA_GET_IMPORTED_KEYS\x10\x0b\x12\x34\n0METADATATYPE_DATABASE_METADATA_GET_EXPORTED_KEYS\x10\x0c\x12\x36\n2METADATATYPE_DATABASE_METADATA_GET_CROSS_REFERENCE\x10\r\x12\x31\n-METADATATYPE_DATABASE_METADATA_GET_INDEX_INFO\x10\x0e\x12+\n\'METADATATYPE_DATABASE_METADATA_GET_UDTS\x10\x0f\x12\x32\n.METADATATYPE_DATABASE_METADATA_GET_SUPER_TYPES\x10\x10\x12\x33\n/METADATATYPE_DATABASE_METADATA_GET_SUPER_TABLES\x10\x11\x12\x31\n-METADATATYPE_DATABASE_METADATA_GET_ATTRIBUTES\x10\x12\x12\x30\n,METADATATYPE_DATABASE_METADATA_GET_FUNCTIONS\x10\x13\x12\x37\n3METADATATYPE_DATABASE_METADATA_GET_FUNCTION_COLUMNS\x10\x14\x12\x30\n,METADATATYPE_DATABASE_METADATA_GET_TYPE_INFO\x10\x15\x12.\n*METADATATYPE_DATABASE_METADATA_GET_SCHEMAS\x10\x16\x12\x35\n1METADATATYPE_DATABASE_METADATA_GET_PSEUDO_COLUMNS\x10\x17*\xdb\x02\n\nClientType\x12\x19\n\x15\x43LIENT_TYPE_JAVA_JDBC\x10\x01\x12\x1c\n\x18\x43LIENT_TYPE_PYTHON_DBAPI\x10\x02\x12\x17\n\x13\x43LIENT_TYPE_UNKNOWN\x10\x03\x12\x12\n\x0e\x43LIENT_TYPE_GO\x10\x04\x12\x1e\n\x1a\x43LIENT_TYPE_EXPERIMENTAL_1\x10\x05\x12\x16\n\x12\x43LIENT_TYPE_NATIVE\x10\x06\x12!\n\x1d\x43LIENT_TYPE_UNKNOWN_LANGUAGE7\x10\x07\x12!\n\x1d\x43LIENT_TYPE_UNKNOWN_LANGUAGE8\x10\x08\x12!\n\x1d\x43LIENT_TYPE_UNKNOWN_LANGUAGE9\x10\t\x12\"\n\x1e\x43LIENT_TYPE_UNKNOWN_LANGUAGE10\x10\n\x12\"\n\x1e\x43LIENT_TYPE_UNKNOWN_LANGUAGE11\x10\x0b\x42%\n\x1b\x63om.google.protos.cloud.sql\x10\x02 \x02(\x02xd')
+  serialized_pb=_b('\n\"storage/speckle/proto/client.proto\x12\x07speckle\"\xb6\x01\n\x11\x42indVariableProto\x12\r\n\x05value\x18\x01 \x01(\x0c\x12\x0c\n\x04type\x18\x02 \x01(\x05\x12\x10\n\x08position\x18\x03 \x01(\x05\x12\x0c\n\x04name\x18\x04 \x01(\t\x12;\n\tdirection\x18\x05 \x01(\x0e\x32$.speckle.BindVariableProto.Direction:\x02IN\"\'\n\tDirection\x12\x06\n\x02IN\x10\x01\x12\x07\n\x03OUT\x10\x02\x12\t\n\x05INOUT\x10\x03\"\x8c\x03\n\x0bResultProto\x12\"\n\x04rows\x18\x01 \x01(\x0b\x32\x14.speckle.RowSetProto\x12\x14\n\x0crows_updated\x18\x02 \x01(\x03\x12\x16\n\x0egenerated_keys\x18\x03 \x03(\x0c\x12\'\n\x08warnings\x18\x04 \x03(\x0b\x32\x15.speckle.SqlException\x12,\n\rsql_exception\x18\x05 \x01(\x0b\x32\x15.speckle.SqlException\x12\x14\n\x0cstatement_id\x18\x06 \x01(\x04\x12\x18\n\tmore_rows\x18\x07 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cmore_results\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\x33\n\x0foutput_variable\x18\t \x03(\x0b\x32\x1a.speckle.BindVariableProto\x12\x1a\n\x12\x62\x61tch_rows_updated\x18\n \x03(\x03\x12\x36\n\x12parameter_metadata\x18\x0b \x03(\x0b\x32\x1a.speckle.ParameterMetadata\"\xf1\x05\n\x07OpProto\x12%\n\x04type\x18\x01 \x02(\x0e\x32\x17.speckle.OpProto.OpType\x12\x0f\n\x07\x63\x61talog\x18\x02 \x01(\t\x12\x0b\n\x03sql\x18\x03 \x01(\t\x12%\n\tsavepoint\x18\x04 \x01(\x0b\x32\x12.speckle.SavePoint\x12\x13\n\x0b\x61uto_commit\x18\x05 \x01(\x08\x12\x11\n\tread_only\x18\x06 \x01(\x08\x12G\n\x1btransaction_isolation_level\x18\x07 \x01(\x0e\x32\".speckle.TransactionIsolationLevel\x12\x14\n\x0cstatement_id\x18\x08 \x01(\x04\x12\x12\n\nrequest_id\x18\t \x01(\x04\"\xde\x03\n\x06OpType\x12\x0e\n\nNATIVE_SQL\x10\x01\x12\x0c\n\x08ROLLBACK\x10\x02\x12\x11\n\rSET_SAVEPOINT\x10\x03\x12\x13\n\x0fSET_AUTO_COMMIT\x10\x04\x12\x11\n\rSET_READ_ONLY\x10\x05\x12#\n\x1fSET_TRANSACTION_ISOLATION_LEVEL\x10\x06\x12\n\n\x06\x43OMMIT\x10\x07\x12\x0f\n\x0bSET_CATALOG\x10\x08\x12\x13\n\x0f\x43LOSE_STATEMENT\x10\t\x12\x08\n\x04PING\x10\n\x12\x0f\n\x0bNEXT_RESULT\x10\x0b\x12\t\n\x05RETRY\x10\x0c\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE13\x10\r\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE14\x10\x0e\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE15\x10\x0f\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE16\x10\x10\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE17\x10\x11\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE18\x10\x12\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE19\x10\x13\x12\x1e\n\x1aVALUE_ENUM_UNKNOWN_VALUE20\x10\x14\"%\n\tSavePoint\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x02(\t\"c\n\x0cSqlException\x12\x0f\n\x07message\x18\x01 \x02(\t\x12\x0f\n\x04\x63ode\x18\x02 \x02(\x05:\x01\x30\x12\x11\n\tsql_state\x18\x03 \x01(\t\x12\x1e\n\x16\x61pplication_error_code\x18\x04 \x01(\x05\"+\n\nTupleProto\x12\x0e\n\x06values\x18\x01 \x03(\x0c\x12\r\n\x05nulls\x18\x02 \x03(\x05\"\xc0\x03\n\x0b\x43olumnProto\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x04type\x18\x03 \x01(\x05:\x02\x31\x32\x12\x12\n\ntable_name\x18\x04 \x01(\t\x12\x13\n\x0bschema_name\x18\x05 \x01(\t\x12\x14\n\x0c\x63\x61talog_name\x18\x06 \x01(\t\x12\x14\n\tprecision\x18\x07 \x01(\x05:\x01\x30\x12\x10\n\x05scale\x18\x08 \x01(\x05:\x01\x30\x12\x10\n\x08nullable\x18\t \x01(\x08\x12\x12\n\nsearchable\x18\n \x01(\x08\x12\x14\n\x0c\x64isplay_size\x18\x0b \x01(\x05\x12\x1d\n\x0e\x61uto_increment\x18\x0c \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x63\x61se_sensitive\x18\r \x01(\x08:\x05\x66\x61lse\x12\x17\n\x08\x63urrency\x18\x0e \x01(\x08:\x05\x66\x61lse\x12\"\n\x13\x64\x65\x66initely_writable\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x18\n\tread_only\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06signed\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\x17\n\x08writable\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x10\x63olumn_type_name\x18\x13 \x01(\t:\x00\"Y\n\x0bRowSetProto\x12%\n\x07\x63olumns\x18\x01 \x03(\x0b\x32\x14.speckle.ColumnProto\x12#\n\x06tuples\x18\x02 \x03(\x0b\x32\x13.speckle.TupleProto\"\xcb\x36\n\x19JdbcDatabaseMetaDataProto\x12*\n\x1b\x61ll_procedures_are_callable\x18\x01 \x01(\x08:\x05\x66\x61lse\x12(\n\x19\x61ll_tables_are_selectable\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x39\n*auto_commit_failure_closes_all_result_sets\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x38\n)data_definition_causes_transaction_commit\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x36\n\'data_definition_ignored_in_transactions\x18\x05 \x01(\x08:\x05\x66\x61lse\x12.\n\x1f\x64oes_max_row_size_include_blobs\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x19\n\x11\x63\x61talog_separator\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61talog_term\x18\x08 \x01(\t\x12!\n\x16\x64\x61tabase_major_version\x18\t \x01(\x05:\x01\x30\x12!\n\x16\x64\x61tabase_minor_version\x18\n \x01(\x05:\x01\x30\x12&\n\x15\x64\x61tabase_product_name\x18\x0b \x01(\t:\x07Speckle\x12\"\n\x18\x64\x61tabase_product_version\x18\x0c \x01(\t:\x00\x12u\n\x1d\x64\x65\x66\x61ult_transaction_isolation\x18\r \x01(\x0e\x32\".speckle.TransactionIsolationLevel:*TRANSACTIONISOLATIONLEVEL_TRANSACTION_NONE\x12\x1f\n\x15\x65xtra_name_characters\x18\x0e \x01(\t:\x00\x12!\n\x17identifier_quote_string\x18\x0f \x01(\t:\x00\x12\x1d\n\x12jdbc_major_version\x18\x10 \x01(\x05:\x01\x31\x12\x1d\n\x12jdbc_minor_version\x18\x11 \x01(\x05:\x01\x30\x12$\n\x19max_binary_literal_length\x18\x12 \x01(\x05:\x01\x30\x12\"\n\x17max_catalog_name_length\x18\x13 \x01(\x05:\x01\x30\x12\"\n\x17max_char_literal_length\x18\x14 \x01(\x05:\x01\x30\x12!\n\x16max_column_name_length\x18\x15 \x01(\x05:\x01\x30\x12\"\n\x17max_columns_in_group_by\x18\x16 \x01(\x05:\x01\x30\x12\x1f\n\x14max_columns_in_index\x18\x17 \x01(\x05:\x01\x30\x12\"\n\x17max_columns_in_order_by\x18\x18 \x01(\x05:\x01\x30\x12 \n\x15max_columns_in_select\x18\x19 \x01(\x05:\x01\x30\x12\x1f\n\x14max_columns_in_table\x18\x1a \x01(\x05:\x01\x30\x12\x1a\n\x0fmax_connections\x18\x1b \x01(\x05:\x01\x30\x12!\n\x16max_cursor_name_length\x18\x1c \x01(\x05:\x01\x30\x12\x1b\n\x10max_index_length\x18\x1d \x01(\x05:\x01\x30\x12$\n\x19max_procedure_name_length\x18\x1e \x01(\x05:\x01\x30\x12\x17\n\x0cmax_row_size\x18\x1f \x01(\x05:\x01\x30\x12!\n\x16max_schema_name_length\x18  \x01(\x05:\x01\x30\x12\x1f\n\x14max_statement_length\x18! \x01(\x05:\x01\x30\x12\x19\n\x0emax_statements\x18\" \x01(\x05:\x01\x30\x12 \n\x15max_table_name_length\x18# \x01(\x05:\x01\x30\x12\x1f\n\x14max_tables_in_select\x18$ \x01(\x05:\x01\x30\x12\x1f\n\x14max_user_name_length\x18% \x01(\x05:\x01\x30\x12\x1b\n\x11numeric_functions\x18& \x01(\t:\x00\x12\x18\n\x0eprocedure_term\x18\' \x01(\t:\x00\x12j\n\x15resultset_holdability\x18( \x01(\x0e\x32\x1d.speckle.ResultSetHoldability:,RESULTSETHOLDABILITY_CLOSE_CURSORS_AT_COMMIT\x12i\n\x0erowid_lifetime\x18) \x01(\x0e\x32\x30.speckle.JdbcDatabaseMetaDataProto.RowIdLifetime:\x1fROWIDLIFETIME_ROWID_UNSUPPORTED\x12\x14\n\x0csql_keywords\x18* \x01(\t\x12\x63\n\x0esql_state_type\x18+ \x01(\x0e\x32/.speckle.JdbcDatabaseMetaDataProto.SqlStateType:\x1aSQLSTATETYPE_SQL_STATE_SQL\x12\x15\n\x0bschema_term\x18, \x01(\t:\x00\x12\x1c\n\x14search_string_escape\x18- \x01(\t\x12\x1a\n\x10string_functions\x18. \x01(\t:\x00\x12\x1a\n\x10system_functions\x18/ \x01(\t:\x00\x12\x1d\n\x13time_date_functions\x18\x30 \x01(\t:\x00\x12\x13\n\tuser_name\x18\x31 \x01(\t:\x00\x12\x1f\n\x10\x63\x61talog_at_start\x18\x32 \x01(\x08:\x05\x66\x61lse\x12#\n\x14locators_update_copy\x18\x33 \x01(\x08:\x05\x66\x61lse\x12)\n\x1anull_plus_non_null_is_null\x18\x34 \x01(\x08:\x05\x66\x61lse\x12&\n\x17nulls_are_sorted_at_end\x18\x35 \x01(\x08:\x05\x66\x61lse\x12(\n\x19nulls_are_sorted_at_start\x18\x36 \x01(\x08:\x05\x66\x61lse\x12$\n\x15nulls_are_sorted_high\x18\x37 \x01(\x08:\x05\x66\x61lse\x12#\n\x14nulls_are_sorted_low\x18\x38 \x01(\x08:\x05\x66\x61lse\x12,\n\x1dstores_lower_case_identifiers\x18\x39 \x01(\x08:\x05\x66\x61lse\x12\x33\n$stores_lower_case_quoted_identifiers\x18: \x01(\x08:\x05\x66\x61lse\x12,\n\x1dstores_mixed_case_identifiers\x18; \x01(\x08:\x05\x66\x61lse\x12\x33\n$stores_mixed_case_quoted_identifiers\x18< \x01(\x08:\x05\x66\x61lse\x12,\n\x1dstores_upper_case_identifiers\x18= \x01(\x08:\x05\x66\x61lse\x12\x33\n$stores_upper_case_quoted_identifiers\x18> \x01(\x08:\x05\x66\x61lse\x12.\n\x1fsupports_ansi92_entry_level_sql\x18? \x01(\x08:\x05\x66\x61lse\x12\'\n\x18supports_ansi92_full_sql\x18@ \x01(\x08:\x05\x66\x61lse\x12/\n supports_ansi92_intermediate_sql\x18\x41 \x01(\x08:\x05\x66\x61lse\x12\x33\n$supports_alter_table_with_add_column\x18\x42 \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_alter_table_with_drop_column\x18\x43 \x01(\x08:\x05\x66\x61lse\x12%\n\x16supports_batch_updates\x18\x44 \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_catalogs_in_data_manipulation\x18\x45 \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_catalogs_in_index_definitions\x18\x46 \x01(\x08:\x05\x66\x61lse\x12\x39\n*supports_catalogs_in_privilege_definitions\x18G \x01(\x08:\x05\x66\x61lse\x12\x33\n$supports_catalogs_in_procedure_calls\x18H \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_catalogs_in_table_definitions\x18I \x01(\x08:\x05\x66\x61lse\x12\'\n\x18supports_column_aliasing\x18J \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10supports_convert\x18K \x01(\x08:\x05\x66\x61lse\x12(\n\x19supports_core_sql_grammar\x18L \x01(\x08:\x05\x66\x61lse\x12-\n\x1esupports_correlated_subqueries\x18M \x01(\x08:\x05\x66\x61lse\x12J\n;supports_data_definition_and_data_manipulation_transactions\x18N \x01(\x08:\x05\x66\x61lse\x12;\n,supports_data_manipulation_transactions_only\x18O \x01(\x08:\x05\x66\x61lse\x12\x39\n*supports_different_table_correlation_names\x18P \x01(\x08:\x05\x66\x61lse\x12/\n supports_expressions_in_order_by\x18Q \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_extended_sql_grammar\x18R \x01(\x08:\x05\x66\x61lse\x12(\n\x19supports_full_outer_joins\x18S \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_get_generated_keys\x18T \x01(\x08:\x05\x66\x61lse\x12 \n\x11supports_group_by\x18U \x01(\x08:\x05\x66\x61lse\x12.\n\x1fsupports_group_by_beyond_select\x18V \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_group_by_unrelated\x18W \x01(\x08:\x05\x66\x61lse\x12\x36\n\'supports_integrity_enhancement_facility\x18X \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_like_escape_clause\x18Y \x01(\x08:\x05\x66\x61lse\x12+\n\x1csupports_limited_outer_joins\x18Z \x01(\x08:\x05\x66\x61lse\x12+\n\x1csupports_minimum_sql_grammar\x18[ \x01(\x08:\x05\x66\x61lse\x12.\n\x1fsupports_mixed_case_identifiers\x18\\ \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_mixed_case_quoted_identifiers\x18] \x01(\x08:\x05\x66\x61lse\x12-\n\x1esupports_multiple_open_results\x18^ \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_multiple_result_sets\x18_ \x01(\x08:\x05\x66\x61lse\x12-\n\x1esupports_multiple_transactions\x18` \x01(\x08:\x05\x66\x61lse\x12(\n\x19supports_named_parameters\x18\x61 \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_non_nullable_columns\x18\x62 \x01(\x08:\x05\x66\x61lse\x12\x32\n#supports_open_cursors_across_commit\x18\x63 \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_open_cursors_across_rollback\x18\x64 \x01(\x08:\x05\x66\x61lse\x12\x35\n&supports_open_statements_across_commit\x18\x65 \x01(\x08:\x05\x66\x61lse\x12\x37\n(supports_open_statements_across_rollback\x18\x66 \x01(\x08:\x05\x66\x61lse\x12*\n\x1bsupports_order_by_unrelated\x18g \x01(\x08:\x05\x66\x61lse\x12#\n\x14supports_outer_joins\x18h \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_positioned_delete\x18i \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_positioned_update\x18j \x01(\x08:\x05\x66\x61lse\x12\"\n\x13supports_savepoints\x18k \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_schemas_in_data_manipulation\x18l \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_schemas_in_index_definitions\x18m \x01(\x08:\x05\x66\x61lse\x12\x38\n)supports_schemas_in_privilege_definitions\x18n \x01(\x08:\x05\x66\x61lse\x12\x32\n#supports_schemas_in_procedure_calls\x18o \x01(\x08:\x05\x66\x61lse\x12\x34\n%supports_schemas_in_table_definitions\x18p \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_select_for_update\x18q \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_statement_pooling\x18r \x01(\x08:\x05\x66\x61lse\x12:\n+supports_stored_functions_using_call_syntax\x18s \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_stored_procedures\x18t \x01(\x08:\x05\x66\x61lse\x12\x31\n\"supports_subqueries_in_comparisons\x18u \x01(\x08:\x05\x66\x61lse\x12,\n\x1dsupports_subqueries_in_exists\x18v \x01(\x08:\x05\x66\x61lse\x12)\n\x1asupports_subqueries_in_ins\x18w \x01(\x08:\x05\x66\x61lse\x12\x31\n\"supports_subqueries_in_quantifieds\x18x \x01(\x08:\x05\x66\x61lse\x12/\n supports_table_correlation_names\x18y \x01(\x08:\x05\x66\x61lse\x12$\n\x15supports_transactions\x18z \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0esupports_union\x18{ \x01(\x08:\x05\x66\x61lse\x12!\n\x12supports_union_all\x18| \x01(\x08:\x05\x66\x61lse\x12(\n\x19uses_local_file_per_table\x18} \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10uses_local_files\x18~ \x01(\x08:\x05\x66\x61lse\x12\x18\n\tread_only\x18\x7f \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0btable_types\x18\x80\x01 \x03(\t\x12\x11\n\x08\x63\x61talogs\x18\x81\x01 \x03(\t\x12;\n\x07schemas\x18\x82\x01 \x03(\x0b\x32).speckle.JdbcDatabaseMetaDataProto.Schema\x12\x35\n\x14\x64\x65letes_are_detected\x18\x83\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x35\n\x14inserts_are_detected\x18\x84\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x35\n\x14updates_are_detected\x18\x85\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12;\n\x1aothers_deletes_are_visible\x18\x86\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12;\n\x1aothers_inserts_are_visible\x18\x87\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12;\n\x1aothers_updates_are_visible\x18\x88\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x38\n\x17own_deletes_are_visible\x18\x89\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x38\n\x17own_inserts_are_visible\x18\x8a\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x38\n\x17own_updates_are_visible\x18\x8b\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12J\n)supports_result_set_concurrency_updatable\x18\x8c\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12\x39\n\x18supports_result_set_type\x18\x8d\x01 \x03(\x0e\x32\x16.speckle.ResultSetType\x12G\n\x1fsupports_result_set_holdability\x18\x8e\x01 \x03(\x0e\x32\x1d.speckle.ResultSetHoldability\x12Q\n$supports_transaction_isolation_level\x18\x8f\x01 \x03(\x0e\x32\".speckle.TransactionIsolationLevel\x12-\n\x1dgenerated_key_always_returned\x18\x90\x01 \x01(\x08:\x05\x66\x61lse\x1a\x35\n\x06Schema\x12\x14\n\x0ctable_schema\x18\x01 \x01(\t\x12\x15\n\rtable_catalog\x18\x02 \x01(\t\"\xd2\x01\n\rRowIdLifetime\x12#\n\x1fROWIDLIFETIME_ROWID_UNSUPPORTED\x10\x00\x12%\n!ROWIDLIFETIME_ROWID_VALID_FOREVER\x10\x01\x12#\n\x1fROWIDLIFETIME_ROWID_VALID_OTHER\x10\x02\x12%\n!ROWIDLIFETIME_ROWID_VALID_SESSION\x10\x03\x12)\n%ROWIDLIFETIME_ROWID_VALID_TRANSACTION\x10\x04\"r\n\x0cSqlStateType\x12\x1e\n\x1aSQLSTATETYPE_SQL_STATE_SQL\x10\x00\x12 \n\x1cSQLSTATETYPE_SQL_STATE_SQL99\x10\x01\x12 \n\x1cSQLSTATETYPE_SQL_STATE_XOPEN\x10\x02\"&\n\x08Property\x12\x0b\n\x03key\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\xd6\x03\n\x0b\x45xecOptions\x12%\n\x16include_generated_keys\x18\x01 \x01(\x08:\x05\x66\x61lse\x12 \n\x18generated_column_indices\x18\x02 \x03(\x05\x12\x1e\n\x16generated_column_names\x18\x03 \x03(\t\x12$\n\x04type\x18\x04 \x01(\x0e\x32\x16.speckle.ResultSetType\x12\x32\n\x0b\x63oncurrency\x18\x05 \x01(\x0e\x32\x1d.speckle.ResultSetConcurrency\x12\x32\n\x0bholdability\x18\x06 \x01(\x0e\x32\x1d.speckle.ResultSetHoldability\x12\x12\n\nfetch_size\x18\x07 \x01(\x05\x12\x10\n\x08max_rows\x18\x08 \x01(\x05\x12\x17\n\x08poolable\x18\t \x01(\x08:\x05\x66\x61lse\x12?\n\x0f\x66\x65tch_direction\x18\n \x01(\x0e\x32\x17.speckle.FetchDirection:\rFETCH_FORWARD\x12\x13\n\x0b\x63ursor_name\x18\x0b \x01(\t\x12\x19\n\x0emax_field_size\x18\x0c \x01(\x05:\x01\x30\x12 \n\x11\x65scape_processing\x18\r \x01(\x08:\x05\x66\x61lse\"K\n\x16\x42\x61tchBindVariableProto\x12\x31\n\rbind_variable\x18\x01 \x03(\x0b\x32\x1a.speckle.BindVariableProto\"]\n\nBatchProto\x12\x11\n\tstatement\x18\x01 \x03(\t\x12<\n\x13\x62\x61tch_bind_variable\x18\x02 \x03(\x0b\x32\x1f.speckle.BatchBindVariableProto\"!\n\x11ParameterMetadata\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\rRpcErrorProto\x12\x12\n\nerror_code\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t*\xb4\x02\n\x19TransactionIsolationLevel\x12.\n*TRANSACTIONISOLATIONLEVEL_TRANSACTION_NONE\x10\x00\x12\x38\n4TRANSACTIONISOLATIONLEVEL_TRANSACTION_READ_COMMITTED\x10\x02\x12:\n6TRANSACTIONISOLATIONLEVEL_TRANSACTION_READ_UNCOMMITTED\x10\x01\x12\x39\n5TRANSACTIONISOLATIONLEVEL_TRANSACTION_REPEATABLE_READ\x10\x04\x12\x36\n2TRANSACTIONISOLATIONLEVEL_TRANSACTION_SERIALIZABLE\x10\x08*\x8b\x01\n\rResultSetType\x12$\n\x1fRESULTSETTYPE_TYPE_FORWARD_ONLY\x10\xeb\x07\x12*\n%RESULTSETTYPE_TYPE_SCROLL_INSENSITIVE\x10\xec\x07\x12(\n#RESULTSETTYPE_TYPE_SCROLL_SENSITIVE\x10\xed\x07*n\n\x14ResultSetConcurrency\x12*\n%RESULTSETCONCURRENCY_CONCUR_READ_ONLY\x10\xef\x07\x12*\n%RESULTSETCONCURRENCY_CONCUR_UPDATABLE\x10\xf0\x07*{\n\x14ResultSetHoldability\x12\x31\n-RESULTSETHOLDABILITY_HOLD_CURSORS_OVER_COMMIT\x10\x01\x12\x30\n,RESULTSETHOLDABILITY_CLOSE_CURSORS_AT_COMMIT\x10\x02*L\n\x0e\x46\x65tchDirection\x12\x12\n\rFETCH_FORWARD\x10\xe8\x07\x12\x12\n\rFETCH_REVERSE\x10\xe9\x07\x12\x12\n\rFETCH_UNKNOWN\x10\xea\x07*\xc4\t\n\x0cMetadataType\x12(\n$METADATATYPE_DATABASE_METADATA_BASIC\x10\x01\x12-\n)METADATATYPE_DATABASE_METADATA_GET_TABLES\x10\x02\x12\x31\n-METADATATYPE_DATABASE_METADATA_GET_PROCEDURES\x10\x03\x12\x38\n4METADATATYPE_DATABASE_METADATA_GET_PROCEDURE_COLUMNS\x10\x04\x12.\n*METADATATYPE_DATABASE_METADATA_GET_COLUMNS\x10\x05\x12\x38\n4METADATATYPE_DATABASE_METADATA_GET_COLUMN_PRIVILEGES\x10\x06\x12\x37\n3METADATATYPE_DATABASE_METADATA_GET_TABLE_PRIVILEGES\x10\x07\x12:\n6METADATATYPE_DATABASE_METADATA_GET_BEST_ROW_IDENTIFIER\x10\x08\x12\x36\n2METADATATYPE_DATABASE_METADATA_GET_VERSION_COLUMNS\x10\t\x12\x33\n/METADATATYPE_DATABASE_METADATA_GET_PRIMARY_KEYS\x10\n\x12\x34\n0METADATATYPE_DATABASE_METADATA_GET_IMPORTED_KEYS\x10\x0b\x12\x34\n0METADATATYPE_DATABASE_METADATA_GET_EXPORTED_KEYS\x10\x0c\x12\x36\n2METADATATYPE_DATABASE_METADATA_GET_CROSS_REFERENCE\x10\r\x12\x31\n-METADATATYPE_DATABASE_METADATA_GET_INDEX_INFO\x10\x0e\x12+\n\'METADATATYPE_DATABASE_METADATA_GET_UDTS\x10\x0f\x12\x32\n.METADATATYPE_DATABASE_METADATA_GET_SUPER_TYPES\x10\x10\x12\x33\n/METADATATYPE_DATABASE_METADATA_GET_SUPER_TABLES\x10\x11\x12\x31\n-METADATATYPE_DATABASE_METADATA_GET_ATTRIBUTES\x10\x12\x12\x30\n,METADATATYPE_DATABASE_METADATA_GET_FUNCTIONS\x10\x13\x12\x37\n3METADATATYPE_DATABASE_METADATA_GET_FUNCTION_COLUMNS\x10\x14\x12\x30\n,METADATATYPE_DATABASE_METADATA_GET_TYPE_INFO\x10\x15\x12.\n*METADATATYPE_DATABASE_METADATA_GET_SCHEMAS\x10\x16\x12\x35\n1METADATATYPE_DATABASE_METADATA_GET_PSEUDO_COLUMNS\x10\x17*\xdb\x02\n\nClientType\x12\x19\n\x15\x43LIENT_TYPE_JAVA_JDBC\x10\x01\x12\x1c\n\x18\x43LIENT_TYPE_PYTHON_DBAPI\x10\x02\x12\x17\n\x13\x43LIENT_TYPE_UNKNOWN\x10\x03\x12\x12\n\x0e\x43LIENT_TYPE_GO\x10\x04\x12\x1e\n\x1a\x43LIENT_TYPE_EXPERIMENTAL_1\x10\x05\x12\x16\n\x12\x43LIENT_TYPE_NATIVE\x10\x06\x12!\n\x1d\x43LIENT_TYPE_UNKNOWN_LANGUAGE7\x10\x07\x12!\n\x1d\x43LIENT_TYPE_UNKNOWN_LANGUAGE8\x10\x08\x12!\n\x1d\x43LIENT_TYPE_UNKNOWN_LANGUAGE9\x10\t\x12\"\n\x1e\x43LIENT_TYPE_UNKNOWN_LANGUAGE10\x10\n\x12\"\n\x1e\x43LIENT_TYPE_UNKNOWN_LANGUAGE11\x10\x0b\x42#\n\x1b\x63om.google.protos.cloud.sql\x10\x02 \x02(\x02')
+)
 
 _TRANSACTIONISOLATIONLEVEL = _descriptor.EnumDescriptor(
   name='TransactionIsolationLevel',
@@ -564,7 +567,7 @@
     _descriptor.FieldDescriptor(
       name='value', full_name='speckle.BindVariableProto.value', index=0,
       number=1, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -585,7 +588,7 @@
     _descriptor.FieldDescriptor(
       name='name', full_name='speckle.BindVariableProto.name', index=3,
       number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -726,14 +729,14 @@
     _descriptor.FieldDescriptor(
       name='catalog', full_name='speckle.OpProto.catalog', index=1,
       number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='sql', full_name='speckle.OpProto.sql', index=2,
       number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -811,7 +814,7 @@
     _descriptor.FieldDescriptor(
       name='name', full_name='speckle.SavePoint.name', index=1,
       number=2, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -839,7 +842,7 @@
     _descriptor.FieldDescriptor(
       name='message', full_name='speckle.SqlException.message', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -853,7 +856,7 @@
     _descriptor.FieldDescriptor(
       name='sql_state', full_name='speckle.SqlException.sql_state', index=2,
       number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -923,14 +926,14 @@
     _descriptor.FieldDescriptor(
       name='name', full_name='speckle.ColumnProto.name', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='label', full_name='speckle.ColumnProto.label', index=1,
       number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -944,21 +947,21 @@
     _descriptor.FieldDescriptor(
       name='table_name', full_name='speckle.ColumnProto.table_name', index=3,
       number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='schema_name', full_name='speckle.ColumnProto.schema_name', index=4,
       number=5, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='catalog_name', full_name='speckle.ColumnProto.catalog_name', index=5,
       number=6, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1049,7 +1052,7 @@
     _descriptor.FieldDescriptor(
       name='column_type_name', full_name='speckle.ColumnProto.column_type_name', index=18,
       number=19, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1112,14 +1115,14 @@
     _descriptor.FieldDescriptor(
       name='table_schema', full_name='speckle.JdbcDatabaseMetaDataProto.Schema.table_schema', index=0,
       number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='table_catalog', full_name='speckle.JdbcDatabaseMetaDataProto.Schema.table_catalog', index=1,
       number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1188,14 +1191,14 @@
     _descriptor.FieldDescriptor(
       name='catalog_separator', full_name='speckle.JdbcDatabaseMetaDataProto.catalog_separator', index=6,
       number=7, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='catalog_term', full_name='speckle.JdbcDatabaseMetaDataProto.catalog_term', index=7,
       number=8, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1216,14 +1219,14 @@
     _descriptor.FieldDescriptor(
       name='database_product_name', full_name='speckle.JdbcDatabaseMetaDataProto.database_product_name', index=10,
       number=11, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("Speckle", "utf-8"),
+      has_default_value=True, default_value=_b("Speckle").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='database_product_version', full_name='speckle.JdbcDatabaseMetaDataProto.database_product_version', index=11,
       number=12, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1237,14 +1240,14 @@
     _descriptor.FieldDescriptor(
       name='extra_name_characters', full_name='speckle.JdbcDatabaseMetaDataProto.extra_name_characters', index=13,
       number=14, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='identifier_quote_string', full_name='speckle.JdbcDatabaseMetaDataProto.identifier_quote_string', index=14,
       number=15, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1405,14 +1408,14 @@
     _descriptor.FieldDescriptor(
       name='numeric_functions', full_name='speckle.JdbcDatabaseMetaDataProto.numeric_functions', index=37,
       number=38, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='procedure_term', full_name='speckle.JdbcDatabaseMetaDataProto.procedure_term', index=38,
       number=39, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1433,7 +1436,7 @@
     _descriptor.FieldDescriptor(
       name='sql_keywords', full_name='speckle.JdbcDatabaseMetaDataProto.sql_keywords', index=41,
       number=42, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -1447,42 +1450,42 @@
     _descriptor.FieldDescriptor(
       name='schema_term', full_name='speckle.JdbcDatabaseMetaDataProto.schema_term', index=43,
       number=44, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='search_string_escape', full_name='speckle.JdbcDatabaseMetaDataProto.search_string_escape', index=44,
       number=45, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='string_functions', full_name='speckle.JdbcDatabaseMetaDataProto.string_functions', index=45,
       number=46, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='system_functions', full_name='speckle.JdbcDatabaseMetaDataProto.system_functions', index=46,
       number=47, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='time_date_functions', full_name='speckle.JdbcDatabaseMetaDataProto.time_date_functions', index=47,
       number=48, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='user_name', full_name='speckle.JdbcDatabaseMetaDataProto.user_name', index=48,
       number=49, type=9, cpp_type=9, label=1,
-      has_default_value=True, default_value=unicode("", "utf-8"),
+      has_default_value=True, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -2177,14 +2180,14 @@
     _descriptor.FieldDescriptor(
       name='key', full_name='speckle.Property.key', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='value', full_name='speckle.Property.value', index=1,
       number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -2282,7 +2285,7 @@
     _descriptor.FieldDescriptor(
       name='cursor_name', full_name='speckle.ExecOptions.cursor_name', index=10,
       number=11, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -2387,7 +2390,7 @@
     _descriptor.FieldDescriptor(
       name='name', full_name='speckle.ParameterMetadata.name', index=0,
       number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -2422,7 +2425,7 @@
     _descriptor.FieldDescriptor(
       name='error_message', full_name='speckle.RpcErrorProto.error_message', index=1,
       number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -2440,7 +2443,7 @@
 )
 
 _BINDVARIABLEPROTO.fields_by_name['direction'].enum_type = _BINDVARIABLEPROTO_DIRECTION
-_BINDVARIABLEPROTO_DIRECTION.containing_type = _BINDVARIABLEPROTO;
+_BINDVARIABLEPROTO_DIRECTION.containing_type = _BINDVARIABLEPROTO
 _RESULTPROTO.fields_by_name['rows'].message_type = _ROWSETPROTO
 _RESULTPROTO.fields_by_name['warnings'].message_type = _SQLEXCEPTION
 _RESULTPROTO.fields_by_name['sql_exception'].message_type = _SQLEXCEPTION
@@ -2449,10 +2452,10 @@
 _OPPROTO.fields_by_name['type'].enum_type = _OPPROTO_OPTYPE
 _OPPROTO.fields_by_name['savepoint'].message_type = _SAVEPOINT
 _OPPROTO.fields_by_name['transaction_isolation_level'].enum_type = _TRANSACTIONISOLATIONLEVEL
-_OPPROTO_OPTYPE.containing_type = _OPPROTO;
+_OPPROTO_OPTYPE.containing_type = _OPPROTO
 _ROWSETPROTO.fields_by_name['columns'].message_type = _COLUMNPROTO
 _ROWSETPROTO.fields_by_name['tuples'].message_type = _TUPLEPROTO
-_JDBCDATABASEMETADATAPROTO_SCHEMA.containing_type = _JDBCDATABASEMETADATAPROTO;
+_JDBCDATABASEMETADATAPROTO_SCHEMA.containing_type = _JDBCDATABASEMETADATAPROTO
 _JDBCDATABASEMETADATAPROTO.fields_by_name['default_transaction_isolation'].enum_type = _TRANSACTIONISOLATIONLEVEL
 _JDBCDATABASEMETADATAPROTO.fields_by_name['resultset_holdability'].enum_type = _RESULTSETHOLDABILITY
 _JDBCDATABASEMETADATAPROTO.fields_by_name['rowid_lifetime'].enum_type = _JDBCDATABASEMETADATAPROTO_ROWIDLIFETIME
@@ -2471,8 +2474,8 @@
 _JDBCDATABASEMETADATAPROTO.fields_by_name['supports_result_set_type'].enum_type = _RESULTSETTYPE
 _JDBCDATABASEMETADATAPROTO.fields_by_name['supports_result_set_holdability'].enum_type = _RESULTSETHOLDABILITY
 _JDBCDATABASEMETADATAPROTO.fields_by_name['supports_transaction_isolation_level'].enum_type = _TRANSACTIONISOLATIONLEVEL
-_JDBCDATABASEMETADATAPROTO_ROWIDLIFETIME.containing_type = _JDBCDATABASEMETADATAPROTO;
-_JDBCDATABASEMETADATAPROTO_SQLSTATETYPE.containing_type = _JDBCDATABASEMETADATAPROTO;
+_JDBCDATABASEMETADATAPROTO_ROWIDLIFETIME.containing_type = _JDBCDATABASEMETADATAPROTO
+_JDBCDATABASEMETADATAPROTO_SQLSTATETYPE.containing_type = _JDBCDATABASEMETADATAPROTO
 _EXECOPTIONS.fields_by_name['type'].enum_type = _RESULTSETTYPE
 _EXECOPTIONS.fields_by_name['concurrency'].enum_type = _RESULTSETCONCURRENCY
 _EXECOPTIONS.fields_by_name['holdability'].enum_type = _RESULTSETHOLDABILITY
@@ -2502,103 +2505,104 @@
 DESCRIPTOR.enum_types_by_name['MetadataType'] = _METADATATYPE
 DESCRIPTOR.enum_types_by_name['ClientType'] = _CLIENTTYPE
 
-class BindVariableProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _BINDVARIABLEPROTO
+BindVariableProto = _reflection.GeneratedProtocolMessageType('BindVariableProto', (_message.Message,), dict(
+  DESCRIPTOR = _BINDVARIABLEPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
+ResultProto = _reflection.GeneratedProtocolMessageType('ResultProto', (_message.Message,), dict(
+  DESCRIPTOR = _RESULTPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
-class ResultProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _RESULTPROTO
+  ))
 
+OpProto = _reflection.GeneratedProtocolMessageType('OpProto', (_message.Message,), dict(
+  DESCRIPTOR = _OPPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
-class OpProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _OPPROTO
+SavePoint = _reflection.GeneratedProtocolMessageType('SavePoint', (_message.Message,), dict(
+  DESCRIPTOR = _SAVEPOINT,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
+SqlException = _reflection.GeneratedProtocolMessageType('SqlException', (_message.Message,), dict(
+  DESCRIPTOR = _SQLEXCEPTION,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
-class SavePoint(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _SAVEPOINT
+  ))
 
+TupleProto = _reflection.GeneratedProtocolMessageType('TupleProto', (_message.Message,), dict(
+  DESCRIPTOR = _TUPLEPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
-class SqlException(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _SQLEXCEPTION
+ColumnProto = _reflection.GeneratedProtocolMessageType('ColumnProto', (_message.Message,), dict(
+  DESCRIPTOR = _COLUMNPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
+RowSetProto = _reflection.GeneratedProtocolMessageType('RowSetProto', (_message.Message,), dict(
+  DESCRIPTOR = _ROWSETPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
-class TupleProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _TUPLEPROTO
+  ))
 
+JdbcDatabaseMetaDataProto = _reflection.GeneratedProtocolMessageType('JdbcDatabaseMetaDataProto', (_message.Message,), dict(
 
+  Schema = _reflection.GeneratedProtocolMessageType('Schema', (_message.Message,), dict(
+    DESCRIPTOR = _JDBCDATABASEMETADATAPROTO_SCHEMA,
+    __module__ = 'google.storage.speckle.proto.client_pb2'
 
-class ColumnProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _COLUMNPROTO
+    ))
+  ,
+  DESCRIPTOR = _JDBCDATABASEMETADATAPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
+Property = _reflection.GeneratedProtocolMessageType('Property', (_message.Message,), dict(
+  DESCRIPTOR = _PROPERTY,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
-class RowSetProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _ROWSETPROTO
+  ))
 
+ExecOptions = _reflection.GeneratedProtocolMessageType('ExecOptions', (_message.Message,), dict(
+  DESCRIPTOR = _EXECOPTIONS,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
-class JdbcDatabaseMetaDataProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
+BatchBindVariableProto = _reflection.GeneratedProtocolMessageType('BatchBindVariableProto', (_message.Message,), dict(
+  DESCRIPTOR = _BATCHBINDVARIABLEPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
-  class Schema(_message.Message):
-    __metaclass__ = _reflection.GeneratedProtocolMessageType
-    DESCRIPTOR = _JDBCDATABASEMETADATAPROTO_SCHEMA
+  ))
 
+BatchProto = _reflection.GeneratedProtocolMessageType('BatchProto', (_message.Message,), dict(
+  DESCRIPTOR = _BATCHPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
-  DESCRIPTOR = _JDBCDATABASEMETADATAPROTO
+  ))
 
+ParameterMetadata = _reflection.GeneratedProtocolMessageType('ParameterMetadata', (_message.Message,), dict(
+  DESCRIPTOR = _PARAMETERMETADATA,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
+  ))
 
-class Property(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _PROPERTY
+RpcErrorProto = _reflection.GeneratedProtocolMessageType('RpcErrorProto', (_message.Message,), dict(
+  DESCRIPTOR = _RPCERRORPROTO,
+  __module__ = 'google.storage.speckle.proto.client_pb2'
 
-
-
-class ExecOptions(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _EXECOPTIONS
-
-
-
-class BatchBindVariableProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _BATCHBINDVARIABLEPROTO
-
-
-
-class BatchProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _BATCHPROTO
-
-
-
-class ParameterMetadata(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _PARAMETERMETADATA
-
-
-
-class RpcErrorProto(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _RPCERRORPROTO
-
-
+  ))
 
 
 DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), '\n\033com.google.protos.cloud.sql\020\002 \002(\002xd')
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033com.google.protos.cloud.sql\020\002 \002(\002'))
 
diff --git a/google/storage/speckle/proto/sql_pb2.py b/google/storage/speckle/proto/sql_pb2.py
index 1e0f662..404cfb3 100644
--- a/google/storage/speckle/proto/sql_pb2.py
+++ b/google/storage/speckle/proto/sql_pb2.py
@@ -17,6 +17,8 @@
 
 
 
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.net.proto2.python.public import descriptor as _descriptor
 from google.net.proto2.python.public import message as _message
 from google.net.proto2.python.public import reflection as _reflection
@@ -45,7 +47,8 @@
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='storage/speckle/proto/sql.proto',
   package='speckle.sql',
-  serialized_pb='\n\x1fstorage/speckle/proto/sql.proto\x12\x0bspeckle.sql\x1a\"storage/speckle/proto/client.proto\"\x8c\x03\n\x0b\x45xecRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\x14\n\x0cstatement_id\x18\x02 \x01(\x04\x12\x11\n\tstatement\x18\x03 \x01(\t\x12\x31\n\rbind_variable\x18\x04 \x03(\x0b\x32\x1a.speckle.BindVariableProto\x12\x15\n\rconnection_id\x18\x05 \x02(\x0c\x12%\n\x07options\x18\x06 \x01(\x0b\x32\x14.speckle.ExecOptions\x12I\n\x0estatement_type\x18\t \x01(\x0e\x32&.speckle.sql.ExecRequest.StatementType:\tSTATEMENT\x12\"\n\x05\x62\x61tch\x18\n \x01(\x0b\x32\x13.speckle.BatchProto\x12\x12\n\nrequest_id\x18\x0b \x01(\x04\"N\n\rStatementType\x12\r\n\tSTATEMENT\x10\x01\x12\x16\n\x12PREPARED_STATEMENT\x10\x02\x12\x16\n\x12\x43\x41LLABLE_STATEMENT\x10\x03\"b\n\x0c\x45xecResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.speckle.ResultProto\x12,\n\rsql_exception\x18\x02 \x01(\x0b\x32\x15.speckle.SqlException\"j\n\rExecOpRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\x15\n\rconnection_id\x18\x02 \x02(\x0c\x12\x1c\n\x02op\x18\x03 \x02(\x0b\x32\x10.speckle.OpProto\x12\x12\n\nrequest_id\x18\x08 \x01(\x04\"\xed\x01\n\x0e\x45xecOpResponse\x12\x12\n\nnative_sql\x18\x01 \x01(\t\x12%\n\tsavepoint\x18\x02 \x01(\x0b\x32\x12.speckle.SavePoint\x12,\n\rsql_exception\x18\x03 \x01(\x0b\x32\x15.speckle.SqlException\x12$\n\x06result\x18\x04 \x01(\x0b\x32\x14.speckle.ResultProto\x12\x30\n\x10\x63\x61\x63hed_rpc_error\x18\x05 \x01(\x0b\x32\x16.speckle.RpcErrorProto\x12\x1a\n\x0e\x63\x61\x63hed_payload\x18\x06 \x01(\x0c\x42\x02\x08\x01\"\xaa\x01\n\x0fMetadataRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\'\n\x08metadata\x18\x03 \x02(\x0e\x32\x15.speckle.MetadataType\x12\x31\n\rbind_variable\x18\x04 \x03(\x0b\x32\x1a.speckle.BindVariableProto\x12\x15\n\rconnection_id\x18\x05 \x02(\x0c\x12\x12\n\nrequest_id\x18\x08 \x01(\x04\"\xaa\x01\n\x10MetadataResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.speckle.ResultProto\x12\x42\n\x16jdbc_database_metadata\x18\x02 \x01(\x0b\x32\".speckle.JdbcDatabaseMetaDataProto\x12,\n\rsql_exception\x18\x03 \x01(\x0b\x32\x15.speckle.SqlException\"\xac\x01\n\x15OpenConnectionRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12#\n\x08property\x18\x02 \x03(\x0b\x32\x11.speckle.Property\x12\x1b\n\x10protocol_version\x18\x05 \x01(\x04:\x01\x31\x12?\n\x0b\x63lient_type\x18\x06 \x01(\x0e\x32\x13.speckle.ClientType:\x15\x43LIENT_TYPE_JAVA_JDBC\"\x86\x01\n\x16OpenConnectionResponse\x12\x15\n\rconnection_id\x18\x01 \x01(\x0c\x12,\n\rsql_exception\x18\x02 \x01(\x0b\x32\x15.speckle.SqlException\x12\'\n\x08warnings\x18\x06 \x03(\x0b\x32\x15.speckle.SqlException\"A\n\x16\x43loseConnectionRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\x15\n\rconnection_id\x18\x02 \x02(\x0c\"G\n\x17\x43loseConnectionResponse\x12,\n\rsql_exception\x18\x01 \x01(\x0b\x32\x15.speckle.SqlException2\xa5\x03\n\nSqlService\x12?\n\x04\x45xec\x12\x18.speckle.sql.ExecRequest\x1a\x19.speckle.sql.ExecResponse\"\x02P\x01\x12\x45\n\x06\x45xecOp\x12\x1a.speckle.sql.ExecOpRequest\x1a\x1b.speckle.sql.ExecOpResponse\"\x02P\x01\x12N\n\x0bGetMetadata\x12\x1c.speckle.sql.MetadataRequest\x1a\x1d.speckle.sql.MetadataResponse\"\x02P\x01\x12]\n\x0eOpenConnection\x12\".speckle.sql.OpenConnectionRequest\x1a#.speckle.sql.OpenConnectionResponse\"\x02P\x01\x12`\n\x0f\x43loseConnection\x12#.speckle.sql.CloseConnectionRequest\x1a$.speckle.sql.CloseConnectionResponse\"\x02P\x01\x42\x30\n\x1b\x63om.google.protos.cloud.sql\x10\x02 \x02(\x02P\x01xd\x80\x01\x00\x88\x01\x00\x90\x01\x00')
+  serialized_pb=_b('\n\x1fstorage/speckle/proto/sql.proto\x12\x0bspeckle.sql\x1a\"storage/speckle/proto/client.proto\"\x8c\x03\n\x0b\x45xecRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\x14\n\x0cstatement_id\x18\x02 \x01(\x04\x12\x11\n\tstatement\x18\x03 \x01(\t\x12\x31\n\rbind_variable\x18\x04 \x03(\x0b\x32\x1a.speckle.BindVariableProto\x12\x15\n\rconnection_id\x18\x05 \x02(\x0c\x12%\n\x07options\x18\x06 \x01(\x0b\x32\x14.speckle.ExecOptions\x12I\n\x0estatement_type\x18\t \x01(\x0e\x32&.speckle.sql.ExecRequest.StatementType:\tSTATEMENT\x12\"\n\x05\x62\x61tch\x18\n \x01(\x0b\x32\x13.speckle.BatchProto\x12\x12\n\nrequest_id\x18\x0b \x01(\x04\"N\n\rStatementType\x12\r\n\tSTATEMENT\x10\x01\x12\x16\n\x12PREPARED_STATEMENT\x10\x02\x12\x16\n\x12\x43\x41LLABLE_STATEMENT\x10\x03\"b\n\x0c\x45xecResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.speckle.ResultProto\x12,\n\rsql_exception\x18\x02 \x01(\x0b\x32\x15.speckle.SqlException\"j\n\rExecOpRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\x15\n\rconnection_id\x18\x02 \x02(\x0c\x12\x1c\n\x02op\x18\x03 \x02(\x0b\x32\x10.speckle.OpProto\x12\x12\n\nrequest_id\x18\x08 \x01(\x04\"\xed\x01\n\x0e\x45xecOpResponse\x12\x12\n\nnative_sql\x18\x01 \x01(\t\x12%\n\tsavepoint\x18\x02 \x01(\x0b\x32\x12.speckle.SavePoint\x12,\n\rsql_exception\x18\x03 \x01(\x0b\x32\x15.speckle.SqlException\x12$\n\x06result\x18\x04 \x01(\x0b\x32\x14.speckle.ResultProto\x12\x30\n\x10\x63\x61\x63hed_rpc_error\x18\x05 \x01(\x0b\x32\x16.speckle.RpcErrorProto\x12\x1a\n\x0e\x63\x61\x63hed_payload\x18\x06 \x01(\x0c\x42\x02\x08\x01\"\xaa\x01\n\x0fMetadataRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\'\n\x08metadata\x18\x03 \x02(\x0e\x32\x15.speckle.MetadataType\x12\x31\n\rbind_variable\x18\x04 \x03(\x0b\x32\x1a.speckle.BindVariableProto\x12\x15\n\rconnection_id\x18\x05 \x02(\x0c\x12\x12\n\nrequest_id\x18\x08 \x01(\x04\"\xaa\x01\n\x10MetadataResponse\x12$\n\x06result\x18\x01 \x01(\x0b\x32\x14.speckle.ResultProto\x12\x42\n\x16jdbc_database_metadata\x18\x02 \x01(\x0b\x32\".speckle.JdbcDatabaseMetaDataProto\x12,\n\rsql_exception\x18\x03 \x01(\x0b\x32\x15.speckle.SqlException\"\xac\x01\n\x15OpenConnectionRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12#\n\x08property\x18\x02 \x03(\x0b\x32\x11.speckle.Property\x12\x1b\n\x10protocol_version\x18\x05 \x01(\x04:\x01\x31\x12?\n\x0b\x63lient_type\x18\x06 \x01(\x0e\x32\x13.speckle.ClientType:\x15\x43LIENT_TYPE_JAVA_JDBC\"\x86\x01\n\x16OpenConnectionResponse\x12\x15\n\rconnection_id\x18\x01 \x01(\x0c\x12,\n\rsql_exception\x18\x02 \x01(\x0b\x32\x15.speckle.SqlException\x12\'\n\x08warnings\x18\x06 \x03(\x0b\x32\x15.speckle.SqlException\"A\n\x16\x43loseConnectionRequest\x12\x10\n\x08instance\x18\x01 \x02(\t\x12\x15\n\rconnection_id\x18\x02 \x02(\x0c\"G\n\x17\x43loseConnectionResponse\x12,\n\rsql_exception\x18\x01 \x01(\x0b\x32\x15.speckle.SqlException2\xa5\x03\n\nSqlService\x12?\n\x04\x45xec\x12\x18.speckle.sql.ExecRequest\x1a\x19.speckle.sql.ExecResponse\"\x02P\x01\x12\x45\n\x06\x45xecOp\x12\x1a.speckle.sql.ExecOpRequest\x1a\x1b.speckle.sql.ExecOpResponse\"\x02P\x01\x12N\n\x0bGetMetadata\x12\x1c.speckle.sql.MetadataRequest\x1a\x1d.speckle.sql.MetadataResponse\"\x02P\x01\x12]\n\x0eOpenConnection\x12\".speckle.sql.OpenConnectionRequest\x1a#.speckle.sql.OpenConnectionResponse\"\x02P\x01\x12`\n\x0f\x43loseConnection\x12#.speckle.sql.CloseConnectionRequest\x1a$.speckle.sql.CloseConnectionResponse\"\x02P\x01\x42.\n\x1b\x63om.google.protos.cloud.sql\x10\x02 \x02(\x02P\x01\x80\x01\x00\x88\x01\x00\x90\x01\x00')
+)
 
 
 
@@ -85,7 +88,7 @@
     _descriptor.FieldDescriptor(
       name='instance', full_name='speckle.sql.ExecRequest.instance', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -99,7 +102,7 @@
     _descriptor.FieldDescriptor(
       name='statement', full_name='speckle.sql.ExecRequest.statement', index=2,
       number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -113,7 +116,7 @@
     _descriptor.FieldDescriptor(
       name='connection_id', full_name='speckle.sql.ExecRequest.connection_id', index=4,
       number=5, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -205,14 +208,14 @@
     _descriptor.FieldDescriptor(
       name='instance', full_name='speckle.sql.ExecOpRequest.instance', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='connection_id', full_name='speckle.sql.ExecOpRequest.connection_id', index=1,
       number=2, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -254,7 +257,7 @@
     _descriptor.FieldDescriptor(
       name='native_sql', full_name='speckle.sql.ExecOpResponse.native_sql', index=0,
       number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -289,10 +292,10 @@
     _descriptor.FieldDescriptor(
       name='cached_payload', full_name='speckle.sql.ExecOpResponse.cached_payload', index=5,
       number=6, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), '\010\001')),
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\010\001'))),
   ],
   extensions=[
   ],
@@ -317,7 +320,7 @@
     _descriptor.FieldDescriptor(
       name='instance', full_name='speckle.sql.MetadataRequest.instance', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -338,7 +341,7 @@
     _descriptor.FieldDescriptor(
       name='connection_id', full_name='speckle.sql.MetadataRequest.connection_id', index=3,
       number=5, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -415,7 +418,7 @@
     _descriptor.FieldDescriptor(
       name='instance', full_name='speckle.sql.OpenConnectionRequest.instance', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -464,7 +467,7 @@
     _descriptor.FieldDescriptor(
       name='connection_id', full_name='speckle.sql.OpenConnectionResponse.connection_id', index=0,
       number=1, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -506,14 +509,14 @@
     _descriptor.FieldDescriptor(
       name='instance', full_name='speckle.sql.CloseConnectionRequest.instance', index=0,
       number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=unicode("", "utf-8"),
+      has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
     _descriptor.FieldDescriptor(
       name='connection_id', full_name='speckle.sql.CloseConnectionRequest.connection_id', index=1,
       number=2, type=12, cpp_type=9, label=2,
-      has_default_value=False, default_value="",
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
@@ -562,7 +565,7 @@
 _EXECREQUEST.fields_by_name['options'].message_type = google.storage.speckle.proto.client_pb2._EXECOPTIONS
 _EXECREQUEST.fields_by_name['statement_type'].enum_type = _EXECREQUEST_STATEMENTTYPE
 _EXECREQUEST.fields_by_name['batch'].message_type = google.storage.speckle.proto.client_pb2._BATCHPROTO
-_EXECREQUEST_STATEMENTTYPE.containing_type = _EXECREQUEST;
+_EXECREQUEST_STATEMENTTYPE.containing_type = _EXECREQUEST
 _EXECRESPONSE.fields_by_name['result'].message_type = google.storage.speckle.proto.client_pb2._RESULTPROTO
 _EXECRESPONSE.fields_by_name['sql_exception'].message_type = google.storage.speckle.proto.client_pb2._SQLEXCEPTION
 _EXECOPREQUEST.fields_by_name['op'].message_type = google.storage.speckle.proto.client_pb2._OPPROTO
@@ -591,71 +594,71 @@
 DESCRIPTOR.message_types_by_name['CloseConnectionRequest'] = _CLOSECONNECTIONREQUEST
 DESCRIPTOR.message_types_by_name['CloseConnectionResponse'] = _CLOSECONNECTIONRESPONSE
 
-class ExecRequest(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _EXECREQUEST
+ExecRequest = _reflection.GeneratedProtocolMessageType('ExecRequest', (_message.Message,), dict(
+  DESCRIPTOR = _EXECREQUEST,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
+  ))
 
+ExecResponse = _reflection.GeneratedProtocolMessageType('ExecResponse', (_message.Message,), dict(
+  DESCRIPTOR = _EXECRESPONSE,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
-class ExecResponse(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _EXECRESPONSE
+  ))
 
+ExecOpRequest = _reflection.GeneratedProtocolMessageType('ExecOpRequest', (_message.Message,), dict(
+  DESCRIPTOR = _EXECOPREQUEST,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
+  ))
 
-class ExecOpRequest(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _EXECOPREQUEST
+ExecOpResponse = _reflection.GeneratedProtocolMessageType('ExecOpResponse', (_message.Message,), dict(
+  DESCRIPTOR = _EXECOPRESPONSE,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
+  ))
 
+MetadataRequest = _reflection.GeneratedProtocolMessageType('MetadataRequest', (_message.Message,), dict(
+  DESCRIPTOR = _METADATAREQUEST,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
-class ExecOpResponse(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _EXECOPRESPONSE
+  ))
 
+MetadataResponse = _reflection.GeneratedProtocolMessageType('MetadataResponse', (_message.Message,), dict(
+  DESCRIPTOR = _METADATARESPONSE,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
+  ))
 
-class MetadataRequest(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _METADATAREQUEST
+OpenConnectionRequest = _reflection.GeneratedProtocolMessageType('OpenConnectionRequest', (_message.Message,), dict(
+  DESCRIPTOR = _OPENCONNECTIONREQUEST,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
+  ))
 
+OpenConnectionResponse = _reflection.GeneratedProtocolMessageType('OpenConnectionResponse', (_message.Message,), dict(
+  DESCRIPTOR = _OPENCONNECTIONRESPONSE,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
-class MetadataResponse(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _METADATARESPONSE
+  ))
 
+CloseConnectionRequest = _reflection.GeneratedProtocolMessageType('CloseConnectionRequest', (_message.Message,), dict(
+  DESCRIPTOR = _CLOSECONNECTIONREQUEST,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
+  ))
 
-class OpenConnectionRequest(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _OPENCONNECTIONREQUEST
+CloseConnectionResponse = _reflection.GeneratedProtocolMessageType('CloseConnectionResponse', (_message.Message,), dict(
+  DESCRIPTOR = _CLOSECONNECTIONRESPONSE,
+  __module__ = 'google.storage.speckle.proto.sql_pb2'
 
-
-
-class OpenConnectionResponse(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _OPENCONNECTIONRESPONSE
-
-
-
-class CloseConnectionRequest(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _CLOSECONNECTIONREQUEST
-
-
-
-class CloseConnectionResponse(_message.Message):
-  __metaclass__ = _reflection.GeneratedProtocolMessageType
-  DESCRIPTOR = _CLOSECONNECTIONRESPONSE
-
-
+  ))
 
 
 DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), '\n\033com.google.protos.cloud.sql\020\002 \002(\002P\001xd\200\001\000\210\001\000\220\001\000')
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033com.google.protos.cloud.sql\020\002 \002(\002P\001\200\001\000\210\001\000\220\001\000'))
 _EXECOPRESPONSE.fields_by_name['cached_payload'].has_options = True
-_EXECOPRESPONSE.fields_by_name['cached_payload']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), '\010\001')
+_EXECOPRESPONSE.fields_by_name['cached_payload']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\010\001'))
 
 
 class _SqlService_ClientBaseStub(_client_stub_base_class):
@@ -852,6 +855,7 @@
 
   @classmethod
   def _MethodSignatures(cls):
+    """Returns a dict of {<method-name>: (<request-type>, <response-type>)}."""
     return {
       'Exec': (ExecRequest, ExecResponse),
       'ExecOp': (ExecOpRequest, ExecOpResponse),
@@ -860,6 +864,12 @@
       'CloseConnection': (CloseConnectionRequest, CloseConnectionResponse),
       }
 
+  @classmethod
+  def _StreamMethodSignatures(cls):
+    """Returns a dict of {<method-name>: (<request-type>, <stream-type>, <response-type>)}."""
+    return {
+      }
+
   def __init__(self, *args, **kwargs):
     """Creates a Stubby RPC server.
 
diff --git a/google_sql.py b/google_sql.py
index b15365d..2af168b 100644
--- a/google_sql.py
+++ b/google_sql.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/lib/cacerts/urlfetch_cacerts.txt b/lib/cacerts/urlfetch_cacerts.txt
index 379cc4f..c575060 100644
--- a/lib/cacerts/urlfetch_cacerts.txt
+++ b/lib/cacerts/urlfetch_cacerts.txt
@@ -3986,26 +3986,6 @@
 
 # ***** END LICENSE BLOCK *****
 
-subject= /C=US/O=Google Inc/CN=Google Internet Authority
-serial=1577E1
------BEGIN CERTIFICATE-----
-MIICsDCCAhmgAwIBAgIDFXfhMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
-MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
-aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTIxMjEyMTU1ODUwWhcNMTMxMjMxMTU1ODUw
-WjBGMQswCQYDVQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZ
-R29vZ2xlIEludGVybmV0IEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
-gYkCgYEAye23pIucV+eEPkB9hPSP0XFjU5nneXQUr0SZMyCSjXvlKAy6rWxJfoNf
-NFlOCnowzdDXxFdF7dWq1nMmzq0yE7jXDx07393cCDaob1FEm8rWIFJztyaHNWrb
-qeXUWaUr/GcZOfqTGBhs3t0lig4zFEfC7wFQeeT9adGnwKziV28CAwEAAaOBozCB
-oDAfBgNVHSMEGDAWgBRI5mj5K9KylddH2CMgEE8zmJCf1DAdBgNVHQ4EFgQUv8Aw
-6/VDET5nup6R+/xq2uNrEiQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
-BAMCAQYwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20v
-Y3Jscy9zZWN1cmVjYS5jcmwwDQYJKoZIhvcNAQEFBQADgYEAvprjecFG+iJsxzEF
-ZUNgujFQodUovxOWZshcnDW7fZ7mTlk3zpeVJrGPZzhaDhvuJjIfKqHweFB7gwB+
-ARlIjNvrPq86fpVg0NOTawALkSqOUMl3MynBQO+spR7EHcRbADQ/JemfTEh2Ycfl
-vZqhEFBfurZkX0eTANq98ZvVfpg=
------END CERTIFICATE-----
-
 subject= /O=RSA Security Inc./CN=RSA Public Root CA v1/emailAddress=rsakeonrootsign@rsasecurity.com
 serial=BE1C9FD2CDB584A680736366D565F82D
 -----BEGIN CERTIFICATE-----
@@ -5901,34 +5881,6 @@
 L3og4cCI8/6TS51b0BvIJ7txvfo4BjuKhRO/LIp/9uaTxdceZG5ne/69fqr6za/f
 -----END CERTIFICATE-----
 
-subject= /C=US/O=SecureTrust Corporation/CN=SecureTrust CA
-serial=428740AA
------BEGIN CERTIFICATE-----
-MIIEHjCCA4egAwIBAgIEQodAqjANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
-VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
-ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
-KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
-ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw
-MDEwNTAwMDBaFw0xMzExMjYxODI1NDhaMEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQK
-ExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3Qg
-Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrpIHllc319hSOwk/K
-1OJ4lVicQeENmUAkFzmRM2bpvuGDr2JcidH8JFths+AREUEcHW7wuLv43qeBuqZI
-xp8dvb6OqUE+uJTtKRrUjtIDHQPvbQ1nHFfXBq3KyPX+Dq9mJUgElgtdo7oWwwhP
-0Ub4FFzyyF4BmW39iMyGqMFvMUJsUj5oy/MZNN+7hxhWgCbE0NzAb9/eoMKRFqBk
-EUtEvB725/pj3masdqRxo+w2lGh6d6Sx5w4vgXritXKG76Jri/AP29NZP7pyvEQk
-nONzs/evVy9CJp2pdLoAUvJLzVN8Rws2hQ5mqQiXFjRXwWb3gOPtcFTHk+AuKBVZ
-h7q7AgMBAAGjggETMIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4G
-CCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMG
-CCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCig
-JqAkhiJodHRwOi8vY3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQW
-BBRCMrYW+gT9/l1LesP990xAHVpDrzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU
-8BdiE1U9s/8KAGv7UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEw
-DQYJKoZIhvcNAQEFBQADgYEArT1rXpFPxAuQWhR4FsoUrB1eo71DCd+/orWynnlP
-2LwqJsA43iHzFaKRlhIrcLdiLpcAzjyeAuQM1yQQ9c5YxUkEPHc737/Pg4lPXOET
-vr2yXqlG6s0NJMTlXqvTKH11LDKkFG+D84c4gaPkCPP3lRewbkCrv/tQuNO+Ym1A
-rKw=
------END CERTIFICATE-----
-
 subject= /CN=Cadence Internet Authority
 serial=072711F7
 -----BEGIN CERTIFICATE-----
@@ -8418,35 +8370,6 @@
 +mKhcZKMCha3PbVKZVsC
 -----END CERTIFICATE-----
 
-subject= /O=Bayer Group/OU=Bayer Business Services/CN=Bayer Group External Server CA
-serial=0727120C
------BEGIN CERTIFICATE-----
-MIIEWzCCA8SgAwIBAgIEBycSDDANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
-UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
-cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
-b2JhbCBSb290MB4XDTA2MTIxMzE1NDYzOFoXDTEzMTIxMzE1NDYwMFowYTEUMBIG
-A1UEChMLQmF5ZXIgR3JvdXAxIDAeBgNVBAsTF0JheWVyIEJ1c2luZXNzIFNlcnZp
-Y2VzMScwJQYDVQQDEx5CYXllciBHcm91cCBFeHRlcm5hbCBTZXJ2ZXIgQ0EwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHt9rLrtGpiggesix5M5/dOOkl
-UqMCD6fvJ2Lrw6BsJUWypDfiJwZEfcePsrs3a7rRNT6ZD8du7aQsiNBqe5+seGle
-8BRnoPpKmAg7C2YRE3wQqruuMfM8E0uEjiKjo4UnsCq8NLqwpGOJP97df8EUVZ+u
-jw6rXf/XhTBPeSvtFXvc/cUeZrflDF0Zry6Sy7o6cT/4G+E+N/ZTJdzKa9SOnvri
-+3AYwTzzrQ5uEA/QclO2CQXb3ocMbY/Rg5gOeGf1Nod3rLMID31FjyWUkHrTTxqy
-V77lwR+2HoV/lig28fFc0h/fOaWqK/RogRkJf6YyDVN+MToLOkV/Aci3qfLLAgMB
-AAGjggGGMIIBgjASBgNVHRMBAf8ECDAGAQH/AgEAMFMGA1UdIARMMEowSAYJKwYB
-BAGxPgEAMDswOQYIKwYBBQUHAgEWLWh0dHA6Ly93d3cucHVibGljLXRydXN0LmNv
-bS9DUFMvT21uaVJvb3QuaHRtbDAOBgNVHQ8BAf8EBAMCAcYwgaAGA1UdIwSBmDCB
-lYAUpgwdn2H/Bxe1vzhG20Mw1Y6wUgaheaR3MHUxCzAJBgNVBAYTAlVTMRgwFgYD
-VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
-bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
-b3SCAgGlMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVibGljLXRydXN0
-LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwHQYDVR0OBBYEFOtc7ji244pr
-qsvWhZSEWPmJ7cnyMA0GCSqGSIb3DQEBBQUAA4GBAISCDE5q/1DSKN/hEfNJeFEC
-N5x6lo/nhheoNA1IJ9BmXgWtMLfe27QnpLakXL3RaCsSsfB6MA1hu00ZgAo9JoVj
-XOUJJO9bd3/P3PtM0fVsKvqtcLtRrPnxe6rJvcwVnECW+0Pu3hj8HnqKPUlN8Uw3
-6rIxCXw8oe9Ymf5wJi+J
------END CERTIFICATE-----
-
 subject= /C=BE/OU=Trusted Root/O=GlobalSign nv-sa/CN=Trusted Root CA G2
 serial=04000000000136E93A3AB3
 -----BEGIN CERTIFICATE-----
@@ -9433,35 +9356,6 @@
 F9UCjiJE/JDErEafSDkqqQ5qlwYgPz9a+qyVI3rchsjq2Tt4jCZE
 -----END CERTIFICATE-----
 
-subject= /DC=com/O=Deutsche Post World Net/OU=IT Services/CN=DPWN Root CA R2 PS
-serial=0400000000010F3E9E34AD
------BEGIN CERTIFICATE-----
-MIIEdDCCA1ygAwIBAgILBAAAAAABDz6eNK0wDQYJKoZIhvcNAQEFBQAwVzELMAkG
-A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
-b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0wNjEyMDExMjAw
-MDBaFw0xMzEyMDEwMDAwMDBaMGoxEzARBgoJkiaJk/IsZAEZFgNjb20xIDAeBgNV
-BAoTF0RldXRzY2hlIFBvc3QgV29ybGQgTmV0MRQwEgYDVQQLEwtJVCBTZXJ2aWNl
-czEbMBkGA1UEAxMSRFBXTiBSb290IENBIFIyIFBTMIIBIDANBgkqhkiG9w0BAQEF
-AAOCAQ0AMIIBCAKCAQEA0COdsL4NK45rUcfHUImUWgM077Y3sNRC+gv/ejr65jb1
-I0zWUkZ01TAaZ8YcIn1Q0P0Gg4rbsCPf60uji+aec38dDBAatx23AR8a2qLUyjUD
-ee7HRCXQGjbZuX3FO3JsP+qjKCOAj4lZ9F0CS/OZRFdipi7od+O1/pcTWx5oCAC3
-XnEdRrVU3bEvIh1AkZR/+nZ+u8OyW8X2BZbZOR3+MoGAlbF2Ge4rsR7GvqU1zGbI
-9s8YtRNZUW9vsYDmxTq+hOXakprCYqsnBcW0EemASWcCEipOsjQCAxUYPHFGs0WV
-N9f20MCbhu31n+JglFgwlbyBHIvjJDqVDFqJAlvNhwIBA6OCAS4wggEqMA4GA1Ud
-DwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEDMB0GA1UdDgQWBBRB5ktv/qwq
-O4CsC+KQp+AJGSufTjAVBgNVHSAEDjAMMAoGCCsGAQQBp0gEMD8GA1UdHwQ4MDYw
-NKAyoDCGLmh0dHA6Ly9rZXlzZXJ2ZXIuZHB3bi5uZXQvcGtpL1IyL2Rwd25fcjJw
-cy5jcmwwbAYIKwYBBQUHAQEEYDBeMDoGCCsGAQUFBzAChi5odHRwOi8va2V5c2Vy
-dmVyLmRwd24ubmV0L3BraS9SMi9kcHduX3IycHMuY3J0MCAGCCsGAQUFBzABhhRo
-dHRwOi8vb2NzcC5kcHduLm5ldDAfBgNVHSMEGDAWgBRge2YaRQ2XyolQL30EzTSo
-//z9SzANBgkqhkiG9w0BAQUFAAOCAQEAsnXLiKkcXfM2V44rzV6dGyctHk83hbKm
-mYEwsneeMSqUriocjWGP0sEFPYTR0gRFCm3+mMYXhvSmnjGu5BseD7OyQLidJKk7
-PI9gSbKpzZVAp8X/Tdrdtwo1LMbJPRr7r273HG8OFmUjABLLNTDw6ewGSOljgly2
-csL18a7H+2sSMTTSJox7N1Jb0hAF5LMKURYJ2xel66ECwff/3w+JUn6Q2bG5YEPb
-0P6Xa+m355Okuoj2IvPVs4EwwlFvslGS619S2Ep/vmFPcBsShvXp0cr4sWVu60+L
-387Lq9Xmc2/PwgsvpzBYCU6VnUWBiiL34z55Hqxom6m900z6HIJPAA==
------END CERTIFICATE-----
-
 subject= /C=AT/O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH/OU=a-sign-SSL-03/CN=a-sign-SSL-03
 serial=026538
 -----BEGIN CERTIFICATE-----
@@ -10325,36 +10219,6 @@
 EBLDPeE3tKiCGbtJ/OMcU/Z7kYSa
 -----END CERTIFICATE-----
 
-subject= /O=Deutsche Post World Net/OU=I2 PS/CN=DPWN SSL CA I2 PS
-serial=1AEA8B42000000000006
------BEGIN CERTIFICATE-----
-MIIEkjCCA3qgAwIBAgIKGuqLQgAAAAAABjANBgkqhkiG9w0BAQUFADBqMRMwEQYK
-CZImiZPyLGQBGRYDY29tMSAwHgYDVQQKExdEZXV0c2NoZSBQb3N0IFdvcmxkIE5l
-dDEUMBIGA1UECxMLSVQgU2VydmljZXMxGzAZBgNVBAMTEkRQV04gUm9vdCBDQSBS
-MiBQUzAeFw0wNjEyMDExNjQ0MTdaFw0xMzEyMDEwMDAwMDBaME4xIDAeBgNVBAoT
-F0RldXRzY2hlIFBvc3QgV29ybGQgTmV0MQ4wDAYDVQQLEwVJMiBQUzEaMBgGA1UE
-AxMRRFBXTiBTU0wgQ0EgSTIgUFMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQDex2rcRh6ykUTytpDqXC2aLsL64X9tW5QoKFWXms4CH+T3bsS0hRESSCph
-fDkyiZnGiqcXYxKklHWQLuybWZCLvZZmgUyVNLdPPuRMRYlwtZGU/eOZZklqUgMl
-LRXM9yGVLlgUXIOwW13owTCxG9KAqdb4RHjQ/Mrufd2uiK+krGEdv/KQHPL8EFqj
-IXtxoijQVv4bBi1jRY7n1wM77s5AJBqbCEcCK5fAqpKyD3bN8V6yW+aFRH8sOq2q
-JLdFU1B0IrX2VRV/MjPOieRn5dD+I6j3wjem5gBDSJ3B7mCs/EUArD2vwqfTMQ+Z
-qe68EDyHSrwcENYhg2WEccaH8gVBAgMBAAGjggFUMIIBUDASBgNVHRMBAf8ECDAG
-AQH/AgECMB0GA1UdDgQWBBQYScEy06jfQRgmoQGDvxlq0hlVajALBgNVHQ8EBAMC
-AYYwEAYJKwYBBAGCNxUBBAMCAQAwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEw
-HwYDVR0jBBgwFoAUQeZLb/6sKjuArAvikKfgCRkrn04wSAYDVR0fBEEwPzA9oDug
-OYY3aHR0cDovL2tleXNlcnZlci5kcHduLm5ldC9wa2kvUjIvZHB3bl9yb290X2Nh
-X3IyX3BzLmNybDB2BggrBgEFBQcBAQRqMGgwQwYIKwYBBQUHMAKGN2h0dHA6Ly9r
-ZXlzZXJ2ZXIuZHB3bi5uZXQvcGtpL1IyL2Rwd25fcm9vdF9jYV9yMl9wcy5jcnQw
-IQYIKwYBBQUHMAGGFWh0dHA6Ly9vY3NwLmRwd24ubmV0LzANBgkqhkiG9w0BAQUF
-AAOCAQEACtONJCXS2Vvz/4RcmvO7P4dAq7G4DCaeNxAfHSdg0IBrl698Sezjk/B8
-73LC7Hwu9xYcxDhVIuqI5p3NB1fDcfR7sBYBL8lXL3vvlZh6V4TOCLUEmpQhg3Sy
-ApTl06Evymz0l1cfxBKCh7IjqRPzvy0nhwldcT048pacWcRMy1Y70VU9ks3Z4cXG
-RzhjUrZ8hvWC6PGjd2MX4Q4kOYISYmlVGvSGxkU2gX7Y5qlwWHJWc+5tPgDe4Ke7
-6zydLsR1vQ9bBGphWDJviBVfAUuyK3pZWhCpiOK04kEFcqlBoGdeUeC/fs6mpRkL
-tl9Dk6MO1hzuoHG/PO4KEBIDJJzA/w==
------END CERTIFICATE-----
-
 subject= /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
 serial=01FDA3EB6ECA75C888438B724BCFBC91
 -----BEGIN CERTIFICATE-----
@@ -10985,36 +10849,6 @@
 3grhHConeyQ8+1g1DrmWSL1Er6mXt0bTOZsKlXETH9BIjSeasqh3f6w=
 -----END CERTIFICATE-----
 
-subject= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
-serial=7E76C57758DF1AC23ECC32FBF6151AD5
------BEGIN CERTIFICATE-----
-MIIEpTCCBA6gAwIBAgIQfnbFd1jfGsI+zDL79hUa1TANBgkqhkiG9w0BAQUFADBf
-MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
-LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
-HhcNMTAwOTMwMDAwMDAwWhcNMTQwMTAxMjM1OTU5WjCBtTELMAkGA1UEBhMCVVMx
-FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
-dCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6Ly93d3cu
-dmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVyaVNpZ24gQ2xhc3Mg
-MyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG5btljkRPTc5v7QlK
-1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8f0MmV1gzgzszChew
-0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDKtpo9yus3nABINYYp
-UHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAboGLSa6Dxugf3kzTU2
-s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RVM5l/JJs/U0V/hhrz
-PPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggGFMIIBgTASBgNVHRMBAf8E
-CDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4RQEHFwMwVjAoBggrBgEFBQcC
-ARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczAqBggrBgEFBQcCAjAeGhxo
-dHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMA4GA1UdDwEB/wQEAwIBBjBtBggr
-BgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFnZS9naWYwITAfMAcGBSsOAwIaBBSP
-5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRwOi8vbG9nby52ZXJpc2lnbi5jb20v
-dnNsb2dvLmdpZjAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJ
-LTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+HSCrJfQBY9i+eaUwMQYDVR0fBCowKDAm
-oCSgIoYgaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwDQYJKoZIhvcN
-AQEFBQADgYEALCbJk3A/lLYWx3aEmMiqkWjX3h00sBOmGUDc4wqwGR4aZdQ9ih8g
-KYjzFNWfxAaVZB5dDyc6ojY4wh8OsP4GWDYZfeeaHWBoczew8mukbaVpqrE97dTQ
-hRRcY4t+sIjMaXfRJi2w8dTeYSEMce6e3XVhijp5eJm8RlWXZJE9J0M=
------END CERTIFICATE-----
-
 subject= /C=US/O=Network Solutions L.L.C./CN=Network Solutions Certificate Authority
 serial=10E776E8A65A6E377E050306D43C25EA
 -----BEGIN CERTIFICATE-----
@@ -12402,37 +12236,6 @@
 LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB
 -----END CERTIFICATE-----
 
-subject= /C=US/O=SecureTrust Corporation/CN=SecureTrust CA
-serial=42870DA3
------BEGIN CERTIFICATE-----
-MIIE0DCCBDmgAwIBAgIEQocNozANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
-VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
-ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
-KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
-ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
-MjMxNzM5MTZaFw0xMzExMjMxODA5MTZaMEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQK
-ExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3Qg
-Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrpIHllc319hSOwk/K
-1OJ4lVicQeENmUAkFzmRM2bpvuGDr2JcidH8JFths+AREUEcHW7wuLv43qeBuqZI
-xp8dvb6OqUE+uJTtKRrUjtIDHQPvbQ1nHFfXBq3KyPX+Dq9mJUgElgtdo7oWwwhP
-0Ub4FFzyyF4BmW39iMyGqMFvMUJsUj5oy/MZNN+7hxhWgCbE0NzAb9/eoMKRFqBk
-EUtEvB725/pj3masdqRxo+w2lGh6d6Sx5w4vgXritXKG76Jri/AP29NZP7pyvEQk
-nONzs/evVy9CJp2pdLoAUvJLzVN8Rws2hQ5mqQiXFjRXwWb3gOPtcFTHk+AuKBVZ
-h7q7AgMBAAGjggHFMIIBwTASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4G
-CCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwQwHQYDVR0OBBYEFEIythb6BP3+
-XUt6w/33TEAdWkOvMIIBGAYDVR0fBIIBDzCCAQswKKAmoCSGImh0dHA6Ly9jcmwu
-ZW50cnVzdC5uZXQvc2VydmVyMS5jcmwwgd6ggduggdikgdUwgdIxCzAJBgNVBAYT
-AlVTMRQwEgYDVQQKEwtFbnRydXN0Lm5ldDE7MDkGA1UECxMyd3d3LmVudHJ1c3Qu
-bmV0L0NQUyBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
-HChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxOjA4BgNVBAMTMUVudHJ1c3Qu
-bmV0IFNlY3VyZSBTZXJ2ZXIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNV
-BAMTBENSTDEwCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFPAXYhNVPbP/CgBr+1CE
-l/PtYtAaMBkGCSqGSIb2fQdBAAQMMAobBFY3LjEDAgCBMA0GCSqGSIb3DQEBBQUA
-A4GBACaDtFsKmI6fmZs5lOOJoQO4KZou/Ewr4Qx7/+6UolWTPdmOjFQjY3RMAXQu
-RjzYYKh5gWUdM3Ac3xP0bd8MApKOLv2jUSXn1kGMqAaOZ5cNcgGhhCWKzLLLQxpM
-YbpKSWVZL9UcqPLJVEN2MKvQett0bmyDZne52tRbKFPchZbH
------END CERTIFICATE-----
-
 subject= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
 serial=250CE8E030612E9F2B89F7054D7CF8FD
 -----BEGIN CERTIFICATE-----
@@ -13028,38 +12831,6 @@
 1N3PIxL4
 -----END CERTIFICATE-----
 
-subject= /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3
-serial=6E46306ED337970EE2AB856D58CA2820
------BEGIN CERTIFICATE-----
-MIIE4jCCBEugAwIBAgIQbkYwbtM3lw7iq4VtWMooIDANBgkqhkiG9w0BAQUFADBf
-MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
-LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
-HhcNMTAwOTMwMDAwMDAwWhcNMTQwMTAxMjM1OTU5WjCBvDELMAkGA1UEBhMCVVMx
-FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
-dCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6Ly93d3cu
-dmVyaXNpZ24uY29tL3JwYSAoYykxMDE2MDQGA1UEAxMtVmVyaVNpZ24gQ2xhc3Mg
-MyBJbnRlcm5hdGlvbmFsIFNlcnZlciBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEAmdacYvAV9IGaQQhZjxOdF8mfUdzasVLv/+NB3eDfxCjG
-4615HycQmLi7IJfBKERBD+qpqFLPTU4bi7u1xHbZzFYG7rNVICreFY1xy1TIbxfN
-iQDk3P/hwB9ocenHKS5+vDv85burJlSLZpDN9pK5MSSAvJ5s1fx+0uFLjNxC+kRL
-X/gYtS4w9D0SmNNiBXNUppyiHb5SgzoHRsQ7AlYhv/JRT9CmmTnprqU/iZucff5N
-YAclIPe712mDK4KTQzfZg0EbawurSmaET0qO3n40mY5o1so5BptMs5pITRNGtFgh
-BMT7oE2sLktiEuP7TfbJUQABH/weaoEqOOC5T9YtRQIDAQABo4IBuzCCAbcwEgYD
-VR0TAQH/BAgwBgEB/wIBADBwBgNVHSAEaTBnMGUGC2CGSAGG+EUBBxcDMFYwKAYI
-KwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9jcHMwKgYIKwYBBQUH
-AgIwHhocaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYTAOBgNVHQ8BAf8EBAMC
-AQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUr
-DgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNp
-Z24uY29tL3ZzbG9nby5naWYwNAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMC
-BglghkgBhvhCBAEGCmCGSAGG+EUBCAEwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMT
-EFZlcmlTaWduTVBLSS0yLTcwHQYDVR0OBBYEFNebfNgioBX33a1fzimbWMO8RgC1
-MDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMu
-Y3JsMA0GCSqGSIb3DQEBBQUAA4GBADIqiNy9lGFTysvHHuJl1nS9kFFAvz59OIP4
-6BglVRoCcX5y5jO5Jl+tjetk1aaq9qMjWInCT8wKk/cqTsqekvQAAZBmlSWEDyyV
-i6lTz53J55r3dXW0ItIC7RgB4zUO61ZRwJLsdE06C5Y+cEgfbL8+Up2lHkqBInH/
-vDR+AAdR
------END CERTIFICATE-----
-
 subject= /C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO SSL CA 2
 serial=4E6C488836BB28CE2BE35AC3798F4A24
 -----BEGIN CERTIFICATE-----
diff --git a/lib/endpoints-1.0/endpoints/users_id_token.py b/lib/endpoints-1.0/endpoints/users_id_token.py
index eb4e309..bce00ba 100644
--- a/lib/endpoints-1.0/endpoints/users_id_token.py
+++ b/lib/endpoints-1.0/endpoints/users_id_token.py
@@ -543,7 +543,11 @@
   segments = jwt.split('.')
 
   if len(segments) != 3:
-    raise _AppIdentityError('Wrong number of segments in token: %s' % jwt)
+
+
+
+
+    raise _AppIdentityError('Wrong number of segments in token: %r' % jwt)
   signed = '%s.%s' % (segments[0], segments[1])
 
   signature = _urlsafe_b64decode(segments[2])
@@ -557,9 +561,9 @@
   try:
     header = json.loads(header_body)
   except:
-    raise _AppIdentityError('Can\'t parse header: %s' % header_body)
+    raise _AppIdentityError("Can't parse header: %r" % header_body)
   if header.get('alg') != 'RS256':
-    raise _AppIdentityError('Unexpected encryption algorithm: %s' %
+    raise _AppIdentityError('Unexpected encryption algorithm: %r' %
                             header.get('alg'))
 
 
@@ -567,12 +571,12 @@
   try:
     parsed = json.loads(json_body)
   except:
-    raise _AppIdentityError('Can\'t parse token: %s' % json_body)
+    raise _AppIdentityError("Can't parse token body: %r" % json_body)
 
   certs = _get_cached_certs(cert_uri, cache)
   if certs is None:
     raise _AppIdentityError(
-        'Unable to retrieve certs needed to verify the signed JWT: %s' % jwt)
+        'Unable to retrieve certs needed to verify the signed JWT: %r' % jwt)
 
 
 
@@ -603,27 +607,27 @@
     if verified:
       break
   if not verified:
-    raise _AppIdentityError('Invalid token signature: %s' % jwt)
+    raise _AppIdentityError('Invalid token signature: %r' % jwt)
 
 
   iat = parsed.get('iat')
   if iat is None:
-    raise _AppIdentityError('No iat field in token: %s' % json_body)
+    raise _AppIdentityError('No iat field in token: %r' % json_body)
   earliest = iat - _CLOCK_SKEW_SECS
 
 
   exp = parsed.get('exp')
   if exp is None:
-    raise _AppIdentityError('No exp field in token: %s' % json_body)
+    raise _AppIdentityError('No exp field in token: %r' % json_body)
   if exp >= time_now + _MAX_TOKEN_LIFETIME_SECS:
-    raise _AppIdentityError('exp field too far in future: %s' % json_body)
+    raise _AppIdentityError('exp field too far in future: %r' % json_body)
   latest = exp + _CLOCK_SKEW_SECS
 
   if time_now < earliest:
-    raise _AppIdentityError('Token used too early, %d < %d: %s' %
+    raise _AppIdentityError('Token used too early, %d < %d: %r' %
                             (time_now, earliest, json_body))
   if time_now > latest:
-    raise _AppIdentityError('Token used too late, %d > %d: %s' %
+    raise _AppIdentityError('Token used too late, %d > %d: %r' %
                             (time_now, latest, json_body))
 
   return parsed
diff --git a/lib/pyasn1/pyasn1/LICENSE b/lib/pyasn1/pyasn1/LICENSE
new file mode 100644
index 0000000..7b45051
--- /dev/null
+++ b/lib/pyasn1/pyasn1/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2005-2012 Ilya Etingof <ilya@glas.net>, all rights reserved.
+
+THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION
+ENDANGERING HUMAN LIFE OR PROPERTY.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, 
+    this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+  * The name of the authors may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
diff --git a/lib/pyasn1/pyasn1/__init__.py b/lib/pyasn1/pyasn1/__init__.py
new file mode 100644
index 0000000..7de39fe
--- /dev/null
+++ b/lib/pyasn1/pyasn1/__init__.py
@@ -0,0 +1 @@
+majorVersionId = '1'
diff --git a/lib/pyasn1/pyasn1/codec/ber/decoder.py b/lib/pyasn1/pyasn1/codec/ber/decoder.py
new file mode 100644
index 0000000..da1a293
--- /dev/null
+++ b/lib/pyasn1/pyasn1/codec/ber/decoder.py
@@ -0,0 +1,751 @@
+# BER decoder
+from pyasn1.type import tag, base, univ, char, useful, tagmap
+from pyasn1.codec.ber import eoo
+from pyasn1.compat.octets import oct2int, octs2ints
+from pyasn1 import debug, error
+
+class AbstractDecoder:
+    protoComponent = None
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,))
+
+    def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,))
+
+class AbstractSimpleDecoder(AbstractDecoder):
+    def _createComponent(self, asn1Spec, tagSet, value=None):
+        if asn1Spec is None:
+            return self.protoComponent.clone(value, tagSet)
+        elif value is None:
+            return asn1Spec
+        else:
+            return asn1Spec.clone(value)
+        
+class AbstractConstructedDecoder(AbstractDecoder):
+    def _createComponent(self, asn1Spec, tagSet, value=None):
+        if asn1Spec is None:
+            return self.protoComponent.clone(tagSet)
+        else:
+            return asn1Spec.clone()
+                                
+class EndOfOctetsDecoder(AbstractSimpleDecoder):
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        return eoo.endOfOctets, substrate[:length]
+
+class ExplicitTagDecoder(AbstractSimpleDecoder):
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        return decodeFun(substrate[:length], asn1Spec, tagSet, length)
+
+    def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                             length, state, decodeFun):
+        value, substrate = decodeFun(substrate, asn1Spec, tagSet, length)
+        terminator, substrate = decodeFun(substrate)
+        if terminator == eoo.endOfOctets:
+            return value, substrate
+        else:
+            raise error.PyAsn1Error('Missing end-of-octets terminator')
+
+explicitTagDecoder = ExplicitTagDecoder()
+
+class IntegerDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Integer(0)
+    precomputedValues = {
+        '\x00':  0,
+        '\x01':  1,
+        '\x02':  2,
+        '\x03':  3,
+        '\x04':  4,
+        '\x05':  5,
+        '\x06':  6,
+        '\x07':  7,
+        '\x08':  8,
+        '\x09':  9,
+        '\xff': -1,
+        '\xfe': -2,
+        '\xfd': -3,
+        '\xfc': -4,
+        '\xfb': -5
+        }
+    
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+                     state, decodeFun):
+        substrate = substrate[:length]
+        if not substrate:
+            raise error.PyAsn1Error('Empty substrate')
+        if substrate in self.precomputedValues:
+            value = self.precomputedValues[substrate]
+        else:
+            firstOctet = oct2int(substrate[0])
+            if firstOctet & 0x80:
+                value = -1
+            else:
+                value = 0
+            for octet in substrate:
+                value = value << 8 | oct2int(octet)
+        return self._createComponent(asn1Spec, tagSet, value), substrate
+
+class BooleanDecoder(IntegerDecoder):
+    protoComponent = univ.Boolean(0)
+    def _createComponent(self, asn1Spec, tagSet, value=None):
+        return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0)
+
+class BitStringDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.BitString(())
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+                     state, decodeFun):
+        substrate = substrate[:length]        
+        if tagSet[0][1] == tag.tagFormatSimple:    # XXX what tag to check?
+            if not substrate:
+                raise error.PyAsn1Error('Missing initial octet')
+            trailingBits = oct2int(substrate[0])
+            if trailingBits > 7:
+                raise error.PyAsn1Error(
+                    'Trailing bits overflow %s' % trailingBits
+                    )
+            substrate = substrate[1:]
+            lsb = p = 0; l = len(substrate)-1; b = ()
+            while p <= l:
+                if p == l:
+                    lsb = trailingBits
+                j = 7                    
+                o = oct2int(substrate[p])
+                while j >= lsb:
+                    b = b + ((o>>j)&0x01,)
+                    j = j - 1
+                p = p + 1
+            return self._createComponent(asn1Spec, tagSet, b), ''
+        r = self._createComponent(asn1Spec, tagSet, ())
+        if not decodeFun:
+            return r, substrate
+        while substrate:
+            component, substrate = decodeFun(substrate)
+            r = r + component
+        return r, substrate
+
+    def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                             length, state, decodeFun):
+        r = self._createComponent(asn1Spec, tagSet, '')
+        if not decodeFun:
+            return r, substrate
+        while substrate:
+            component, substrate = decodeFun(substrate)
+            if component == eoo.endOfOctets:
+                break
+            r = r + component
+        else:
+            raise error.SubstrateUnderrunError(
+                'No EOO seen before substrate ends'
+                )
+        return r, substrate
+
+class OctetStringDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.OctetString('')
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+                     state, decodeFun):
+        substrate = substrate[:length]
+        if tagSet[0][1] == tag.tagFormatSimple:    # XXX what tag to check?
+            return self._createComponent(asn1Spec, tagSet, substrate), ''
+        r = self._createComponent(asn1Spec, tagSet, '')
+        if not decodeFun:
+            return r, substrate
+        while substrate:
+            component, substrate = decodeFun(substrate)
+            r = r + component
+        return r, substrate
+
+    def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                             length, state, decodeFun):
+        r = self._createComponent(asn1Spec, tagSet, '')
+        if not decodeFun:
+            return r, substrate        
+        while substrate:
+            component, substrate = decodeFun(substrate)
+            if component == eoo.endOfOctets:
+                break
+            r = r + component
+        else:
+            raise error.SubstrateUnderrunError(
+                'No EOO seen before substrate ends'
+                )
+        return r, substrate
+
+class NullDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Null('')
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        substrate = substrate[:length]        
+        r = self._createComponent(asn1Spec, tagSet)
+        if length:
+            raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
+        return r, substrate
+
+class ObjectIdentifierDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.ObjectIdentifier(())
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+                     state, decodeFun):
+        substrate = substrate[:length]        
+        if not substrate:
+            raise error.PyAsn1Error('Empty substrate')
+
+        # Get the first subid
+        subId = oct2int(substrate[0])
+        oid = divmod(subId, 40)
+
+        index = 1
+        substrateLen = len(substrate)
+        while index < substrateLen:
+            subId = oct2int(substrate[index])
+            index = index + 1
+            if subId > 127:
+                # Construct subid from a number of octets
+                nextSubId = subId
+                subId = 0
+                while nextSubId >= 128:
+                    subId = (subId << 7) + (nextSubId & 0x7F)
+                    if index >= substrateLen:
+                        raise error.SubstrateUnderrunError(
+                            'Short substrate for sub-OID past %s' % (oid,)
+                            )
+                    nextSubId = oct2int(substrate[index])
+                    index = index + 1
+                subId = (subId << 7) + nextSubId
+            oid = oid + (subId,)
+        return self._createComponent(asn1Spec, tagSet, oid), substrate[index:]
+
+class RealDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Real()
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        substrate = substrate[:length]
+        if not length:
+            raise error.SubstrateUnderrunError('Short substrate for Real')
+        fo = oct2int(substrate[0]); substrate = substrate[1:]
+        if fo & 0x40:  # infinite value
+            value = fo & 0x01 and '-inf' or 'inf'
+        elif fo & 0x80:  # binary enoding
+            if fo & 0x11 == 0:
+                n = 1
+            elif fo & 0x01:
+                n = 2
+            elif fo & 0x02:
+                n = 3
+            else:
+                n = oct2int(substrate[0])
+            eo, substrate = substrate[:n], substrate[n:]
+            if not eo or not substrate:
+                raise error.PyAsn1Error('Real exponent screwed')
+            e = 0
+            while eo:         # exponent
+                e <<= 8
+                e |= oct2int(eo[0])
+                eo = eo[1:]
+            p = 0
+            while substrate:  # value
+                p <<= 8
+                p |= oct2int(substrate[0])
+                substrate = substrate[1:]
+            if fo & 0x40:    # sign bit
+                p = -p
+            value = (p, 2, e)
+        elif fo & 0xc0 == 0:  # character encoding
+            try:
+                if fo & 0x3 == 0x1:  # NR1
+                    value = (int(substrate), 10, 0)
+                elif fo & 0x3 == 0x2:  # NR2
+                    value = float(substrate)
+                elif fo & 0x3 == 0x3:  # NR3
+                    value = float(substrate)
+                else:
+                    raise error.SubstrateUnderrunError(
+                        'Unknown NR (tag %s)' % fo
+                        )
+            except ValueError:
+                raise error.SubstrateUnderrunError(
+                    'Bad character Real syntax'
+                    )
+        elif fo & 0xc0 == 0x40:  # special real value
+            pass
+        else:
+            raise error.SubstrateUnderrunError(
+                'Unknown encoding (tag %s)' % fo
+                )
+        return self._createComponent(asn1Spec, tagSet, value), substrate
+        
+class SequenceDecoder(AbstractConstructedDecoder):
+    protoComponent = univ.Sequence()
+    def _getComponentTagMap(self, r, idx):
+        try:
+            return r.getComponentTagMapNearPosition(idx)
+        except error.PyAsn1Error:
+            return
+
+    def _getComponentPositionByType(self, r, t, idx):
+        return r.getComponentPositionNearType(t, idx)
+    
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        substrate = substrate[:length]
+        r = self._createComponent(asn1Spec, tagSet)
+        idx = 0
+        if not decodeFun:
+            return r, substrate
+        while substrate:
+            asn1Spec = self._getComponentTagMap(r, idx)
+            component, substrate = decodeFun(
+                substrate, asn1Spec
+                )
+            idx = self._getComponentPositionByType(
+                r, component.getEffectiveTagSet(), idx
+                )
+            r.setComponentByPosition(idx, component, asn1Spec is None)
+            idx = idx + 1
+        r.setDefaultComponents()
+        r.verifySizeSpec()
+        return r, substrate
+
+    def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                             length, state, decodeFun):
+        r = self._createComponent(asn1Spec, tagSet)
+        idx = 0
+        while substrate:
+            asn1Spec = self._getComponentTagMap(r, idx)
+            if not decodeFun:
+                return r, substrate
+            component, substrate = decodeFun(substrate, asn1Spec)
+            if component == eoo.endOfOctets:
+                break
+            idx = self._getComponentPositionByType(
+                r, component.getEffectiveTagSet(), idx
+                )            
+            r.setComponentByPosition(idx, component, asn1Spec is None)
+            idx = idx + 1                
+        else:
+            raise error.SubstrateUnderrunError(
+                'No EOO seen before substrate ends'
+                )
+        r.setDefaultComponents()
+        r.verifySizeSpec()
+        return r, substrate
+
+class SequenceOfDecoder(AbstractConstructedDecoder):
+    protoComponent = univ.SequenceOf()    
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        substrate = substrate[:length]
+        r = self._createComponent(asn1Spec, tagSet)
+        asn1Spec = r.getComponentType()
+        idx = 0
+        if not decodeFun:
+            return r, substrate
+        while substrate:
+            component, substrate = decodeFun(
+                substrate, asn1Spec
+                )
+            r.setComponentByPosition(idx, component, asn1Spec is None)
+            idx = idx + 1
+        r.verifySizeSpec()
+        return r, substrate
+
+    def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                             length, state, decodeFun):
+        r = self._createComponent(asn1Spec, tagSet)
+        asn1Spec = r.getComponentType()
+        idx = 0
+        if not decodeFun:
+            return r, substrate
+        while substrate:
+            component, substrate = decodeFun(substrate, asn1Spec)
+            if component == eoo.endOfOctets:
+                break
+            r.setComponentByPosition(idx, component, asn1Spec is None)
+            idx = idx + 1                
+        else:
+            raise error.SubstrateUnderrunError(
+                'No EOO seen before substrate ends'
+                )
+        r.verifySizeSpec()
+        return r, substrate
+
+class SetDecoder(SequenceDecoder):
+    protoComponent = univ.Set()
+    def _getComponentTagMap(self, r, idx):
+        return r.getComponentTagMap()
+
+    def _getComponentPositionByType(self, r, t, idx):
+        nextIdx = r.getComponentPositionByType(t)
+        if nextIdx is None:
+            return idx
+        else:
+            return nextIdx
+    
+class SetOfDecoder(SequenceOfDecoder):
+    protoComponent = univ.SetOf()
+    
+class ChoiceDecoder(AbstractConstructedDecoder):
+    protoComponent = univ.Choice()
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        substrate = substrate[:length]        
+        r = self._createComponent(asn1Spec, tagSet)
+        if not decodeFun:
+            return r, substrate
+        if r.getTagSet() == tagSet: # explicitly tagged Choice
+            component, substrate = decodeFun(
+                substrate, r.getComponentTagMap()
+                )
+        else:
+            component, substrate = decodeFun(
+                substrate, r.getComponentTagMap(), tagSet, length, state
+                )
+        if isinstance(component, univ.Choice):
+            effectiveTagSet = component.getEffectiveTagSet()
+        else:
+            effectiveTagSet = component.getTagSet()
+        r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None)
+        return r, substrate
+
+    indefLenValueDecoder = valueDecoder
+
+class AnyDecoder(AbstractSimpleDecoder):
+    protoComponent = univ.Any()
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                     length, state, decodeFun):
+        if asn1Spec is None or \
+               asn1Spec is not None and tagSet != asn1Spec.getTagSet():
+            # untagged Any container, recover inner header substrate
+            length = length + len(fullSubstrate) - len(substrate)
+            substrate = fullSubstrate
+        substrate = substrate[:length]
+        return self._createComponent(asn1Spec, tagSet, value=substrate), ''
+
+    def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+                             length, state, decodeFun):
+        if asn1Spec is not None and tagSet == asn1Spec.getTagSet():
+            # tagged Any type -- consume header substrate
+            header = ''
+        else:
+            # untagged Any, recover header substrate
+            header = fullSubstrate[:-len(substrate)]
+
+        r = self._createComponent(asn1Spec, tagSet, header)
+
+        # Any components do not inherit initial tag
+        asn1Spec = self.protoComponent
+        
+        if not decodeFun:
+            return r, substrate
+        while substrate:
+            component, substrate = decodeFun(substrate, asn1Spec)
+            if component == eoo.endOfOctets:
+                break
+            r = r + component
+        else:
+            raise error.SubstrateUnderrunError(
+                'No EOO seen before substrate ends'
+                )
+        return r, substrate
+
+# character string types
+class UTF8StringDecoder(OctetStringDecoder):
+    protoComponent = char.UTF8String()
+class NumericStringDecoder(OctetStringDecoder):
+    protoComponent = char.NumericString()
+class PrintableStringDecoder(OctetStringDecoder):
+    protoComponent = char.PrintableString()
+class TeletexStringDecoder(OctetStringDecoder):
+    protoComponent = char.TeletexString()
+class VideotexStringDecoder(OctetStringDecoder):
+    protoComponent = char.VideotexString()
+class IA5StringDecoder(OctetStringDecoder):
+    protoComponent = char.IA5String()
+class GraphicStringDecoder(OctetStringDecoder):
+    protoComponent = char.GraphicString()
+class VisibleStringDecoder(OctetStringDecoder):
+    protoComponent = char.VisibleString()
+class GeneralStringDecoder(OctetStringDecoder):
+    protoComponent = char.GeneralString()
+class UniversalStringDecoder(OctetStringDecoder):
+    protoComponent = char.UniversalString()
+class BMPStringDecoder(OctetStringDecoder):
+    protoComponent = char.BMPString()
+
+# "useful" types
+class GeneralizedTimeDecoder(OctetStringDecoder):
+    protoComponent = useful.GeneralizedTime()
+class UTCTimeDecoder(OctetStringDecoder):
+    protoComponent = useful.UTCTime()
+
+tagMap = {
+    eoo.endOfOctets.tagSet: EndOfOctetsDecoder(),
+    univ.Integer.tagSet: IntegerDecoder(),
+    univ.Boolean.tagSet: BooleanDecoder(),
+    univ.BitString.tagSet: BitStringDecoder(),
+    univ.OctetString.tagSet: OctetStringDecoder(),
+    univ.Null.tagSet: NullDecoder(),
+    univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(),
+    univ.Enumerated.tagSet: IntegerDecoder(),
+    univ.Real.tagSet: RealDecoder(),
+    univ.Sequence.tagSet: SequenceDecoder(),  # conflicts with SequenceOf
+    univ.Set.tagSet: SetDecoder(),            # conflicts with SetOf
+    univ.Choice.tagSet: ChoiceDecoder(),      # conflicts with Any
+    # character string types
+    char.UTF8String.tagSet: UTF8StringDecoder(),
+    char.NumericString.tagSet: NumericStringDecoder(),
+    char.PrintableString.tagSet: PrintableStringDecoder(),
+    char.TeletexString.tagSet: TeletexStringDecoder(),
+    char.VideotexString.tagSet: VideotexStringDecoder(),
+    char.IA5String.tagSet: IA5StringDecoder(),
+    char.GraphicString.tagSet: GraphicStringDecoder(),
+    char.VisibleString.tagSet: VisibleStringDecoder(),
+    char.GeneralString.tagSet: GeneralStringDecoder(),
+    char.UniversalString.tagSet: UniversalStringDecoder(),
+    char.BMPString.tagSet: BMPStringDecoder(),
+    # useful types
+    useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(),
+    useful.UTCTime.tagSet: UTCTimeDecoder()
+    }
+
+# Type-to-codec map for ambiguous ASN.1 types
+typeMap = {
+    univ.Set.typeId: SetDecoder(),
+    univ.SetOf.typeId: SetOfDecoder(),
+    univ.Sequence.typeId: SequenceDecoder(),
+    univ.SequenceOf.typeId: SequenceOfDecoder(),
+    univ.Choice.typeId: ChoiceDecoder(),
+    univ.Any.typeId: AnyDecoder()
+    }
+
+( stDecodeTag, stDecodeLength, stGetValueDecoder, stGetValueDecoderByAsn1Spec,
+  stGetValueDecoderByTag, stTryAsExplicitTag, stDecodeValue,
+  stDumpRawValue, stErrorCondition, stStop ) = [x for x in range(10)]
+
+class Decoder:
+    defaultErrorState = stErrorCondition
+#    defaultErrorState = stDumpRawValue
+    defaultRawDecoder = AnyDecoder()
+    def __init__(self, tagMap, typeMap={}):
+        self.__tagMap = tagMap
+        self.__typeMap = typeMap
+        self.__endOfOctetsTagSet = eoo.endOfOctets.getTagSet()
+        # Tag & TagSet objects caches
+        self.__tagCache = {}
+        self.__tagSetCache = {}
+        
+    def __call__(self, substrate, asn1Spec=None, tagSet=None,
+                 length=None, state=stDecodeTag, recursiveFlag=1):
+        debug.logger & debug.flagDecoder and debug.logger('decoder called with state %d, working with up to %d octets of substrate: %s' % (state, len(substrate), debug.hexdump(substrate)))
+        fullSubstrate = substrate
+        while state != stStop:
+            if state == stDecodeTag:
+                # Decode tag
+                if not substrate:
+                    raise error.SubstrateUnderrunError(
+                        'Short octet stream on tag decoding'
+                        )
+                
+                firstOctet = substrate[0]
+                substrate = substrate[1:]
+                if firstOctet in self.__tagCache:
+                    lastTag = self.__tagCache[firstOctet]
+                else:
+                    t = oct2int(firstOctet)
+                    tagClass = t&0xC0
+                    tagFormat = t&0x20
+                    tagId = t&0x1F
+                    if tagId == 0x1F:
+                        tagId = 0
+                        while 1:
+                            if not substrate:
+                                raise error.SubstrateUnderrunError(
+                                    'Short octet stream on long tag decoding'
+                                    )
+                            t = oct2int(substrate[0])
+                            tagId = tagId << 7 | (t&0x7F)
+                            substrate = substrate[1:]
+                            if not t&0x80:
+                                break
+                    lastTag = tag.Tag(
+                        tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
+                        )
+                    if tagId < 31:
+                        # cache short tags
+                        self.__tagCache[firstOctet] = lastTag
+                if tagSet is None:
+                    if firstOctet in self.__tagSetCache:
+                        tagSet = self.__tagSetCache[firstOctet]
+                    else:
+                        # base tag not recovered
+                        tagSet = tag.TagSet((), lastTag)
+                        if firstOctet in self.__tagCache:
+                            self.__tagSetCache[firstOctet] = tagSet
+                else:
+                    tagSet = lastTag + tagSet
+                state = stDecodeLength
+                debug.logger and debug.logger & debug.flagDecoder and debug.logger('tag decoded into %r, decoding length' % tagSet)
+            if state == stDecodeLength:
+                # Decode length
+                if not substrate:
+                     raise error.SubstrateUnderrunError(
+                         'Short octet stream on length decoding'
+                         )
+                firstOctet  = oct2int(substrate[0])
+                if firstOctet == 128:
+                    size = 1
+                    length = -1
+                elif firstOctet < 128:
+                    length, size = firstOctet, 1
+                else:
+                    size = firstOctet & 0x7F
+                    # encoded in size bytes
+                    length = 0
+                    lengthString = substrate[1:size+1]
+                    # missing check on maximum size, which shouldn't be a
+                    # problem, we can handle more than is possible
+                    if len(lengthString) != size:
+                        raise error.SubstrateUnderrunError(
+                            '%s<%s at %s' %
+                            (size, len(lengthString), tagSet)
+                            )
+                    for char in lengthString:
+                        length = (length << 8) | oct2int(char)
+                    size = size + 1
+                substrate = substrate[size:]
+                if length != -1 and len(substrate) < length:
+                    raise error.SubstrateUnderrunError(
+                        '%d-octet short' % (length - len(substrate))
+                        )
+                state = stGetValueDecoder
+                debug.logger and debug.logger & debug.flagDecoder and debug.logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(substrate)))
+            if state == stGetValueDecoder:
+                if asn1Spec is None:
+                    state = stGetValueDecoderByTag
+                else:
+                    state = stGetValueDecoderByAsn1Spec
+            #
+            # There're two ways of creating subtypes in ASN.1 what influences
+            # decoder operation. These methods are:
+            # 1) Either base types used in or no IMPLICIT tagging has been
+            #    applied on subtyping.
+            # 2) Subtype syntax drops base type information (by means of
+            #    IMPLICIT tagging.
+            # The first case allows for complete tag recovery from substrate
+            # while the second one requires original ASN.1 type spec for
+            # decoding.
+            #
+            # In either case a set of tags (tagSet) is coming from substrate
+            # in an incremental, tag-by-tag fashion (this is the case of
+            # EXPLICIT tag which is most basic). Outermost tag comes first
+            # from the wire.
+            #            
+            if state == stGetValueDecoderByTag:
+                if tagSet in self.__tagMap:
+                    concreteDecoder = self.__tagMap[tagSet]
+                else:
+                    concreteDecoder = None
+                if concreteDecoder:
+                    state = stDecodeValue
+                else:
+                    _k = tagSet[:1]
+                    if _k in self.__tagMap:
+                        concreteDecoder = self.__tagMap[_k]
+                    else:
+                        concreteDecoder = None
+                    if concreteDecoder:
+                        state = stDecodeValue
+                    else:
+                        state = stTryAsExplicitTag
+                debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'explicit tag'))
+            if state == stGetValueDecoderByAsn1Spec:
+                if isinstance(asn1Spec, (dict, tagmap.TagMap)):
+                    if tagSet in asn1Spec:
+                        __chosenSpec = asn1Spec[tagSet]
+                    else:
+                        __chosenSpec = None
+                else:
+                    __chosenSpec = asn1Spec
+                if __chosenSpec is not None and (
+                       tagSet == __chosenSpec.getTagSet() or \
+                       tagSet in __chosenSpec.getTagMap()
+                       ):
+                    # use base type for codec lookup to recover untagged types
+                    baseTagSet = __chosenSpec.baseTagSet
+                    if __chosenSpec.typeId is not None and \
+                           __chosenSpec.typeId in self.__typeMap:
+                        # ambiguous type
+                        concreteDecoder = self.__typeMap[__chosenSpec.typeId]
+                    elif baseTagSet in self.__tagMap:
+                        # base type or tagged subtype
+                        concreteDecoder = self.__tagMap[baseTagSet]
+                    else:
+                        concreteDecoder = None
+                    if concreteDecoder:
+                        asn1Spec = __chosenSpec
+                        state = stDecodeValue
+                    else:
+                        state = stTryAsExplicitTag
+                elif tagSet == self.__endOfOctetsTagSet:
+                    concreteDecoder = self.__tagMap[tagSet]
+                    state = stDecodeValue
+                else:
+                    state = stTryAsExplicitTag
+                if debug.logger and debug.logger & debug.flagDecoder:
+                    if isinstance(asn1Spec, base.Asn1Item):
+                        debug.logger('choosing value codec by ASN.1 spec:\n  %r -> %r' % (asn1Spec.getTagSet(), asn1Spec.__class__.__name__))
+                    else:
+                        debug.logger('choosing value codec by ASN.1 spec that offers either of the following: ')
+                        for t, v in asn1Spec.getPosMap().items():
+                            debug.logger('  %r -> %s' % (t, v.__class__.__name__))
+                        debug.logger('but neither of: ')
+                        for i in asn1Spec.getNegMap().items():
+                            debug.logger('  %r -> %s' % (t, v.__class__.__name__))
+                    debug.logger('codec %s chosen by ASN.1 spec, decoding %s' % (state == stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'explicit tag'))
+            if state == stTryAsExplicitTag:
+                if tagSet and \
+                       tagSet[0][1] == tag.tagFormatConstructed and \
+                       tagSet[0][0] != tag.tagClassUniversal:
+                    # Assume explicit tagging
+                    concreteDecoder = explicitTagDecoder
+                    state = stDecodeValue
+                else:                    
+                    state = self.defaultErrorState
+                debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as failure'))
+            if state == stDumpRawValue:
+                concreteDecoder = self.defaultRawDecoder
+                debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
+                state = stDecodeValue
+            if state == stDecodeValue:
+                if recursiveFlag:
+                    decodeFun = self
+                else:
+                    decodeFun = None
+                if length == -1:  # indef length
+                    value, substrate = concreteDecoder.indefLenValueDecoder(
+                        fullSubstrate, substrate, asn1Spec, tagSet, length,
+                        stGetValueDecoder, decodeFun
+                        )
+                else:
+                    value, _substrate = concreteDecoder.valueDecoder(
+                        fullSubstrate, substrate, asn1Spec, tagSet, length,
+                        stGetValueDecoder, decodeFun
+                        )
+                    if recursiveFlag:
+                        substrate = substrate[length:]
+                    else:
+                        substrate = _substrate
+                state = stStop
+                debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, value.prettyPrint(), substrate and debug.hexdump(substrate) or '<none>'))
+            if state == stErrorCondition:
+                raise error.PyAsn1Error(
+                    '%r not in asn1Spec: %r' % (tagSet, asn1Spec)
+                    )
+        debug.logger and debug.logger & debug.flagDecoder and debug.logger('decoder call completed')
+        return value, substrate
+            
+decode = Decoder(tagMap, typeMap)
+
+# XXX
+# non-recursive decoding; return position rather than substrate
diff --git a/lib/pyasn1/pyasn1/codec/ber/eoo.py b/lib/pyasn1/pyasn1/codec/ber/eoo.py
new file mode 100644
index 0000000..379be19
--- /dev/null
+++ b/lib/pyasn1/pyasn1/codec/ber/eoo.py
@@ -0,0 +1,8 @@
+from pyasn1.type import base, tag
+
+class EndOfOctets(base.AbstractSimpleAsn1Item):
+    defaultValue = 0
+    tagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
+        )
+endOfOctets = EndOfOctets()
diff --git a/lib/pyasn1/pyasn1/codec/cer/decoder.py b/lib/pyasn1/pyasn1/codec/cer/decoder.py
new file mode 100644
index 0000000..71395d2
--- /dev/null
+++ b/lib/pyasn1/pyasn1/codec/cer/decoder.py
@@ -0,0 +1,32 @@
+# CER decoder
+from pyasn1.type import univ
+from pyasn1.codec.ber import decoder
+from pyasn1.compat.octets import oct2int
+from pyasn1 import error
+
+class BooleanDecoder(decoder.AbstractSimpleDecoder):
+    protoComponent = univ.Boolean(0)
+    def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+                     state, decodeFun):
+        substrate = substrate[:length]
+        if not substrate:
+            raise error.PyAsn1Error('Empty substrate')
+        byte = oct2int(substrate[0])
+        if byte == 0xff:
+            value = 1
+        elif byte == 0x00:
+            value = 0
+        else:
+            raise error.PyAsn1Error('Boolean CER violation: %s' % byte)
+        return self._createComponent(asn1Spec, tagSet, value), substrate[1:]
+
+tagMap = decoder.tagMap.copy()
+tagMap.update({
+    univ.Boolean.tagSet: BooleanDecoder(),
+    })
+
+typeMap = decoder.typeMap
+
+class Decoder(decoder.Decoder): pass
+
+decode = Decoder(tagMap, decoder.typeMap)
diff --git a/lib/pyasn1/pyasn1/codec/der/decoder.py b/lib/pyasn1/pyasn1/codec/der/decoder.py
new file mode 100644
index 0000000..0f5a24c
--- /dev/null
+++ b/lib/pyasn1/pyasn1/codec/der/decoder.py
@@ -0,0 +1,5 @@
+# DER decoder
+from pyasn1.type import univ
+from pyasn1.codec.cer import decoder
+
+decode = decoder.Decoder(decoder.tagMap, decoder.typeMap)
diff --git a/lib/pyasn1/pyasn1/compat/octets.py b/lib/pyasn1/pyasn1/compat/octets.py
new file mode 100644
index 0000000..d0303ea
--- /dev/null
+++ b/lib/pyasn1/pyasn1/compat/octets.py
@@ -0,0 +1,18 @@
+from sys import version_info
+
+if version_info[0] <= 2:
+    int2oct = chr
+    ints2octs = lambda s: ''.join([ int2oct(x) for x in s ])
+    null = ''
+    oct2int = ord
+    octs2ints = lambda s: [ oct2int(x) for x in s ]
+    str2octs = lambda x: x
+    octs2str = lambda x: x
+else:
+    ints2octs = bytes
+    int2oct = lambda x: ints2octs((x,))
+    null = ints2octs()
+    oct2int = lambda x: x
+    octs2ints = lambda s: [ x for x in s ]
+    str2octs = lambda x: x.encode()
+    octs2str = lambda x: x.decode()
diff --git a/lib/pyasn1/pyasn1/debug.py b/lib/pyasn1/pyasn1/debug.py
new file mode 100644
index 0000000..da55bdd
--- /dev/null
+++ b/lib/pyasn1/pyasn1/debug.py
@@ -0,0 +1,49 @@
+import sys
+from pyasn1.compat.octets import octs2ints
+from pyasn1 import error
+
+flagNone     = 0x0000
+flagEncoder  = 0x0001
+flagDecoder  = 0x0002
+flagAll      = 0xffff
+
+flagMap = {
+    'encoder': flagEncoder,
+    'decoder': flagDecoder,
+    'all': flagAll
+    }
+
+class Debug:
+    defaultPrinter = sys.stderr.write
+    def __init__(self, *flags):
+        self._flags = flagNone
+        self._printer = self.defaultPrinter
+        for f in flags:
+            if f not in flagMap:
+                raise error.PyAsn1Error('bad debug flag %s' % (f,))
+            self._flags = self._flags | flagMap[f]
+            self('debug category %s enabled' % f)
+        
+    def __str__(self):
+        return 'logger %s, flags %x' % (self._printer, self._flags)
+    
+    def __call__(self, msg):
+        self._printer('DBG: %s\n' % msg)
+
+    def __and__(self, flag):
+        return self._flags & flag
+
+    def __rand__(self, flag):
+        return flag & self._flags
+
+logger = 0
+
+def setLogger(l):
+    global logger
+    logger = l
+
+def hexdump(octets):
+    return ' '.join(
+            [ '%s%.2X' % (n%16 == 0 and ('\n%.5d: ' % n) or '', x) 
+              for n,x in zip(range(len(octets)), octs2ints(octets)) ]
+        )
diff --git a/lib/pyasn1/pyasn1/error.py b/lib/pyasn1/pyasn1/error.py
new file mode 100644
index 0000000..716406f
--- /dev/null
+++ b/lib/pyasn1/pyasn1/error.py
@@ -0,0 +1,3 @@
+class PyAsn1Error(Exception): pass
+class ValueConstraintError(PyAsn1Error): pass
+class SubstrateUnderrunError(PyAsn1Error): pass
diff --git a/lib/pyasn1/pyasn1/type/base.py b/lib/pyasn1/pyasn1/type/base.py
new file mode 100644
index 0000000..db31671
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/base.py
@@ -0,0 +1,244 @@
+# Base classes for ASN.1 types
+import sys
+from pyasn1.type import constraint, tagmap
+from pyasn1 import error
+
+class Asn1Item: pass
+
+class Asn1ItemBase(Asn1Item):
+    # Set of tags for this ASN.1 type
+    tagSet = ()
+    
+    # A list of constraint.Constraint instances for checking values
+    subtypeSpec = constraint.ConstraintsIntersection()
+
+    # Used for ambiguous ASN.1 types identification
+    typeId = None
+    
+    def __init__(self, tagSet=None, subtypeSpec=None):
+        if tagSet is None:
+            self._tagSet = self.tagSet
+        else:
+            self._tagSet = tagSet
+        if subtypeSpec is None:
+            self._subtypeSpec = self.subtypeSpec
+        else:
+            self._subtypeSpec = subtypeSpec
+
+    def _verifySubtypeSpec(self, value, idx=None):
+        try:
+            self._subtypeSpec(value, idx)
+        except error.PyAsn1Error:
+            c, i, t = sys.exc_info()
+            raise c('%s at %s' % (i, self.__class__.__name__))
+        
+    def getSubtypeSpec(self): return self._subtypeSpec
+    
+    def getTagSet(self): return self._tagSet
+    def getEffectiveTagSet(self): return self._tagSet  # used by untagged types
+    def getTagMap(self): return tagmap.TagMap({self._tagSet: self})
+    
+    def isSameTypeWith(self, other):
+        return self is other or \
+               self._tagSet == other.getTagSet() and \
+               self._subtypeSpec == other.getSubtypeSpec()
+    def isSuperTypeOf(self, other):
+        """Returns true if argument is a ASN1 subtype of ourselves"""
+        return self._tagSet.isSuperTagSetOf(other.getTagSet()) and \
+               self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec())
+
+class __NoValue:
+    def __getattr__(self, attr):
+        raise error.PyAsn1Error('No value for %s()' % attr)
+    def __getitem__(self, i):
+        raise error.PyAsn1Error('No value')
+    
+noValue = __NoValue()
+
+# Base class for "simple" ASN.1 objects. These are immutable.
+class AbstractSimpleAsn1Item(Asn1ItemBase):    
+    defaultValue = noValue
+    def __init__(self, value=None, tagSet=None, subtypeSpec=None):
+        Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
+        if value is None or value is noValue:
+            value = self.defaultValue
+        if value is None or value is noValue:
+            self.__hashedValue = value = noValue
+        else:
+            value = self.prettyIn(value)
+            self._verifySubtypeSpec(value)
+            self.__hashedValue = hash(value)
+        self._value = value
+        self._len = None
+        
+    def __repr__(self):
+        if self._value is noValue:
+            return self.__class__.__name__ + '()'
+        else:
+            return self.__class__.__name__ + '(%s)' % (self.prettyOut(self._value),)
+    def __str__(self): return str(self._value)
+    def __eq__(self, other):
+        return self is other and True or self._value == other
+    def __ne__(self, other): return self._value != other
+    def __lt__(self, other): return self._value < other
+    def __le__(self, other): return self._value <= other
+    def __gt__(self, other): return self._value > other
+    def __ge__(self, other): return self._value >= other
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self): return bool(self._value)
+    else:
+        def __bool__(self): return bool(self._value)
+    def __hash__(self): return self.__hashedValue
+
+    def clone(self, value=None, tagSet=None, subtypeSpec=None):
+        if value is None and tagSet is None and subtypeSpec is None:
+            return self
+        if value is None:
+            value = self._value
+        if tagSet is None:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        return self.__class__(value, tagSet, subtypeSpec)
+
+    def subtype(self, value=None, implicitTag=None, explicitTag=None,
+                subtypeSpec=None):
+        if value is None:
+            value = self._value
+        if implicitTag is not None:
+            tagSet = self._tagSet.tagImplicitly(implicitTag)
+        elif explicitTag is not None:
+            tagSet = self._tagSet.tagExplicitly(explicitTag)
+        else:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        else:
+            subtypeSpec = subtypeSpec + self._subtypeSpec
+        return self.__class__(value, tagSet, subtypeSpec)
+
+    def prettyIn(self, value): return value
+    def prettyOut(self, value): return str(value)
+
+    def prettyPrint(self, scope=0): return self.prettyOut(self._value)
+    # XXX Compatibility stub
+    def prettyPrinter(self, scope=0): return self.prettyPrint(scope)
+    
+#
+# Constructed types:
+# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
+# * ASN1 types and values are represened by Python class instances
+# * Value initialization is made for defaulted components only
+# * Primary method of component addressing is by-position. Data model for base
+#   type is Python sequence. Additional type-specific addressing methods
+#   may be implemented for particular types.
+# * SequenceOf and SetOf types do not implement any additional methods
+# * Sequence, Set and Choice types also implement by-identifier addressing
+# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
+# * Sequence and Set types may include optional and defaulted
+#   components
+# * Constructed types hold a reference to component types used for value
+#   verification and ordering.
+# * Component type is a scalar type for SequenceOf/SetOf types and a list
+#   of types for Sequence/Set/Choice.
+#
+
+class AbstractConstructedAsn1Item(Asn1ItemBase):
+    componentType = None
+    sizeSpec = constraint.ConstraintsIntersection()
+    def __init__(self, componentType=None, tagSet=None,
+                 subtypeSpec=None, sizeSpec=None):
+        Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
+        if componentType is None:
+            self._componentType = self.componentType
+        else:
+            self._componentType = componentType
+        if sizeSpec is None:
+            self._sizeSpec = self.sizeSpec
+        else:
+            self._sizeSpec = sizeSpec
+        self._componentValues = []
+        self._componentValuesSet = 0
+
+    def __repr__(self):
+        r = self.__class__.__name__ + '()'
+        for idx in range(len(self._componentValues)):
+            if self._componentValues[idx] is None:
+                continue
+            r = r + '.setComponentByPosition(%s, %r)' % (
+                idx, self._componentValues[idx]
+                )
+        return r
+
+    def __eq__(self, other):
+        return self is other and True or self._componentValues == other
+    def __ne__(self, other): return self._componentValues != other
+    def __lt__(self, other): return self._componentValues < other
+    def __le__(self, other): return self._componentValues <= other
+    def __gt__(self, other): return self._componentValues > other
+    def __ge__(self, other): return self._componentValues >= other
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self): return bool(self._componentValues)
+    else:
+        def __bool__(self): return bool(self._componentValues)
+
+    def getComponentTagMap(self):
+        raise error.PyAsn1Error('Method not implemented')
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag): pass
+
+    def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None, 
+              cloneValueFlag=None):
+        if tagSet is None:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        if sizeSpec is None:
+            sizeSpec = self._sizeSpec
+        r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
+        if cloneValueFlag:
+            self._cloneComponentValues(r, cloneValueFlag)
+        return r
+
+    def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None,
+                sizeSpec=None, cloneValueFlag=None):
+        if implicitTag is not None:
+            tagSet = self._tagSet.tagImplicitly(implicitTag)
+        elif explicitTag is not None:
+            tagSet = self._tagSet.tagExplicitly(explicitTag)
+        else:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        else:
+            subtypeSpec = subtypeSpec + self._subtypeSpec
+        if sizeSpec is None:
+            sizeSpec = self._sizeSpec
+        else:
+            sizeSpec = sizeSpec + self._sizeSpec
+        r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
+        if cloneValueFlag:
+            self._cloneComponentValues(r, cloneValueFlag)
+        return r
+
+    def _verifyComponent(self, idx, value): pass
+
+    def verifySizeSpec(self): self._sizeSpec(self)
+
+    def getComponentByPosition(self, idx):
+        raise error.PyAsn1Error('Method not implemented')
+    def setComponentByPosition(self, idx, value, verifyConstraints=True):
+        raise error.PyAsn1Error('Method not implemented')
+
+    def getComponentType(self): return self._componentType
+
+    def __getitem__(self, idx): return self.getComponentByPosition(idx)
+    def __setitem__(self, idx, value): self.setComponentByPosition(idx, value)
+
+    def __len__(self): return len(self._componentValues)
+    
+    def clear(self):
+        self._componentValues = []
+        self._componentValuesSet = 0
+
+    def setDefaultComponents(self): pass
diff --git a/lib/pyasn1/pyasn1/type/char.py b/lib/pyasn1/pyasn1/type/char.py
new file mode 100644
index 0000000..ae112f8
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/char.py
@@ -0,0 +1,61 @@
+# ASN.1 "character string" types
+from pyasn1.type import univ, tag
+
+class UTF8String(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+        )
+    encoding = "utf-8"
+
+class NumericString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
+        )
+
+class PrintableString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
+        )
+
+class TeletexString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
+        )
+    
+
+class VideotexString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
+        )
+
+class IA5String(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
+        )
+
+class GraphicString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
+        )
+
+class VisibleString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
+        )
+
+class GeneralString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
+        )
+
+class UniversalString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
+        )
+    encoding = "utf-32-be"
+
+class BMPString(univ.OctetString):
+    tagSet = univ.OctetString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
+        )
+    encoding = "utf-16-be"
diff --git a/lib/pyasn1/pyasn1/type/constraint.py b/lib/pyasn1/pyasn1/type/constraint.py
new file mode 100644
index 0000000..6687393
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/constraint.py
@@ -0,0 +1,200 @@
+#
+#   ASN.1 subtype constraints classes.
+#
+#   Constraints are relatively rare, but every ASN1 object
+#   is doing checks all the time for whether they have any
+#   constraints and whether they are applicable to the object.
+#
+#   What we're going to do is define objects/functions that
+#   can be called unconditionally if they are present, and that
+#   are simply not present if there are no constraints.
+#
+#   Original concept and code by Mike C. Fletcher.
+#
+import sys
+from pyasn1.type import error
+
+class AbstractConstraint:
+    """Abstract base-class for constraint objects
+
+       Constraints should be stored in a simple sequence in the
+       namespace of their client Asn1Item sub-classes.
+    """
+    def __init__(self, *values):
+        self._valueMap = {}
+        self._setValues(values)
+        self.__hashedValues = None
+    def __call__(self, value, idx=None):
+        try:
+            self._testValue(value, idx)
+        except error.ValueConstraintError:
+            raise error.ValueConstraintError(
+               '%s failed at: \"%s\"' % (self, sys.exc_info()[1])
+            )
+    def __repr__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__,
+            ', '.join([repr(x) for x in self._values])
+        )
+    def __eq__(self, other):
+        return self is other and True or self._values == other
+    def __ne__(self, other): return self._values != other
+    def __lt__(self, other): return self._values < other
+    def __le__(self, other): return self._values <= other
+    def __gt__(self, other): return self._values > other
+    def __ge__(self, other): return self._values >= other
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self): return bool(self._values)
+    else:
+        def __bool__(self): return bool(self._values)
+
+    def __hash__(self):
+        if self.__hashedValues is None:
+            self.__hashedValues = hash((self.__class__.__name__, self._values))
+        return self.__hashedValues
+
+    def _setValues(self, values): self._values = values
+    def _testValue(self, value, idx):
+        raise error.ValueConstraintError(value)
+
+    # Constraints derivation logic
+    def getValueMap(self): return self._valueMap
+    def isSuperTypeOf(self, otherConstraint):
+        return self in otherConstraint.getValueMap() or \
+               otherConstraint is self or otherConstraint == self
+    def isSubTypeOf(self, otherConstraint):
+        return otherConstraint in self._valueMap or \
+               otherConstraint is self or otherConstraint == self
+
+class SingleValueConstraint(AbstractConstraint):
+    """Value must be part of defined values constraint"""
+    def _testValue(self, value, idx):
+        # XXX index vals for performance?
+        if value not in self._values:
+            raise error.ValueConstraintError(value)
+
+class ContainedSubtypeConstraint(AbstractConstraint):
+    """Value must satisfy all of defined set of constraints"""
+    def _testValue(self, value, idx):
+        for c in self._values:
+            c(value, idx)
+
+class ValueRangeConstraint(AbstractConstraint):
+    """Value must be within start and stop values (inclusive)"""
+    def _testValue(self, value, idx):
+        if value < self.start or value > self.stop:
+            raise error.ValueConstraintError(value)
+
+    def _setValues(self, values):
+        if len(values) != 2:
+            raise error.PyAsn1Error(
+                '%s: bad constraint values' % (self.__class__.__name__,)
+                )
+        self.start, self.stop = values
+        if self.start > self.stop:
+            raise error.PyAsn1Error(
+                '%s: screwed constraint values (start > stop): %s > %s' % (
+                    self.__class__.__name__,
+                    self.start, self.stop
+                )
+            )
+        AbstractConstraint._setValues(self, values)
+        
+class ValueSizeConstraint(ValueRangeConstraint):
+    """len(value) must be within start and stop values (inclusive)"""
+    def _testValue(self, value, idx):
+        l = len(value)
+        if l < self.start or l > self.stop:
+            raise error.ValueConstraintError(value)
+
+class PermittedAlphabetConstraint(SingleValueConstraint):
+    def _setValues(self, values):
+        self._values = ()
+        for v in values:
+            self._values = self._values + tuple(v)
+
+    def _testValue(self, value, idx):
+        for v in value:
+            if v not in self._values:
+                raise error.ValueConstraintError(value)
+
+# This is a bit kludgy, meaning two op modes within a single constraing
+class InnerTypeConstraint(AbstractConstraint):
+    """Value must satisfy type and presense constraints"""
+    def _testValue(self, value, idx):
+        if self.__singleTypeConstraint:
+            self.__singleTypeConstraint(value)
+        elif self.__multipleTypeConstraint:
+            if idx not in self.__multipleTypeConstraint:
+                raise error.ValueConstraintError(value)
+            constraint, status = self.__multipleTypeConstraint[idx]
+            if status == 'ABSENT':   # XXX presense is not checked!
+                raise error.ValueConstraintError(value)
+            constraint(value)
+
+    def _setValues(self, values):
+        self.__multipleTypeConstraint = {}
+        self.__singleTypeConstraint = None
+        for v in values:
+            if isinstance(v, tuple):
+                self.__multipleTypeConstraint[v[0]] = v[1], v[2]
+            else:
+                self.__singleTypeConstraint = v
+        AbstractConstraint._setValues(self, values)
+
+# Boolean ops on constraints 
+
+class ConstraintsExclusion(AbstractConstraint):
+    """Value must not fit the single constraint"""
+    def _testValue(self, value, idx):
+        try:
+            self._values[0](value, idx)
+        except error.ValueConstraintError:
+            return
+        else:
+            raise error.ValueConstraintError(value)
+
+    def _setValues(self, values):
+        if len(values) != 1:
+            raise error.PyAsn1Error('Single constraint expected')
+        AbstractConstraint._setValues(self, values)
+
+class AbstractConstraintSet(AbstractConstraint):
+    """Value must not satisfy the single constraint"""
+    def __getitem__(self, idx): return self._values[idx]
+
+    def __add__(self, value): return self.__class__(self, value)
+    def __radd__(self, value): return self.__class__(self, value)
+
+    def __len__(self): return len(self._values)
+
+    # Constraints inclusion in sets
+    
+    def _setValues(self, values):
+        self._values = values
+        for v in values:
+            self._valueMap[v] = 1
+            self._valueMap.update(v.getValueMap())
+
+class ConstraintsIntersection(AbstractConstraintSet):
+    """Value must satisfy all constraints"""
+    def _testValue(self, value, idx):
+        for v in self._values:
+            v(value, idx)
+
+class ConstraintsUnion(AbstractConstraintSet):
+    """Value must satisfy at least one constraint"""
+    def _testValue(self, value, idx):
+        for v in self._values:
+            try:
+                v(value, idx)
+            except error.ValueConstraintError:
+                pass
+            else:
+                return
+        raise error.ValueConstraintError(
+            'all of %s failed for \"%s\"' % (self._values, value)
+            )
+
+# XXX
+# add tests for type check
diff --git a/lib/pyasn1/pyasn1/type/error.py b/lib/pyasn1/pyasn1/type/error.py
new file mode 100644
index 0000000..3e68484
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/error.py
@@ -0,0 +1,3 @@
+from pyasn1.error import PyAsn1Error
+
+class ValueConstraintError(PyAsn1Error): pass
diff --git a/lib/pyasn1/pyasn1/type/namedtype.py b/lib/pyasn1/pyasn1/type/namedtype.py
new file mode 100644
index 0000000..48967a5
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/namedtype.py
@@ -0,0 +1,132 @@
+# NamedType specification for constructed types
+import sys
+from pyasn1.type import tagmap
+from pyasn1 import error
+
+class NamedType:
+    isOptional = 0
+    isDefaulted = 0
+    def __init__(self, name, t):
+        self.__name = name; self.__type = t
+    def __repr__(self): return '%s(%s, %s)' % (
+        self.__class__.__name__, self.__name, self.__type
+        )
+    def getType(self): return self.__type
+    def getName(self): return self.__name
+    def __getitem__(self, idx):
+        if idx == 0: return self.__name
+        if idx == 1: return self.__type
+        raise IndexError()
+    
+class OptionalNamedType(NamedType):
+    isOptional = 1
+class DefaultedNamedType(NamedType):
+    isDefaulted = 1
+    
+class NamedTypes:
+    def __init__(self, *namedTypes):
+        self.__namedTypes = namedTypes
+        self.__namedTypesLen = len(self.__namedTypes)
+        self.__minTagSet = None
+        self.__tagToPosIdx = {}; self.__nameToPosIdx = {}
+        self.__tagMap = { False: None, True: None }
+        self.__ambigiousTypes = {}
+
+    def __repr__(self):
+        r = '%s(' % self.__class__.__name__
+        for n in self.__namedTypes:
+            r = r + '%r, ' % (n,)
+        return r + ')'
+    
+    def __getitem__(self, idx): return self.__namedTypes[idx]
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self): return bool(self.__namedTypesLen)
+    else:
+        def __bool__(self): return bool(self.__namedTypesLen)
+    def __len__(self): return self.__namedTypesLen
+    
+    def getTypeByPosition(self, idx):
+        if idx < 0 or idx >= self.__namedTypesLen:
+            raise error.PyAsn1Error('Type position out of range')
+        else:
+            return self.__namedTypes[idx].getType()
+
+    def getPositionByType(self, tagSet):
+        if not self.__tagToPosIdx:
+            idx = self.__namedTypesLen
+            while idx > 0:
+                idx = idx - 1
+                tagMap = self.__namedTypes[idx].getType().getTagMap()
+                for t in tagMap.getPosMap():
+                    if t in self.__tagToPosIdx:
+                        raise error.PyAsn1Error('Duplicate type %s' % (t,))
+                    self.__tagToPosIdx[t] = idx
+        try:
+            return self.__tagToPosIdx[tagSet]
+        except KeyError:
+            raise error.PyAsn1Error('Type %s not found' % (tagSet,))
+        
+    def getNameByPosition(self, idx):
+        try:
+            return self.__namedTypes[idx].getName()
+        except IndexError:
+            raise error.PyAsn1Error('Type position out of range')
+    def getPositionByName(self, name):
+        if not self.__nameToPosIdx:
+            idx = self.__namedTypesLen
+            while idx > 0:
+                idx = idx - 1
+                n = self.__namedTypes[idx].getName()
+                if n in self.__nameToPosIdx:
+                    raise error.PyAsn1Error('Duplicate name %s' % (n,))
+                self.__nameToPosIdx[n] = idx
+        try:
+            return self.__nameToPosIdx[name]
+        except KeyError:
+            raise error.PyAsn1Error('Name %s not found' % (name,))
+
+    def __buildAmbigiousTagMap(self):
+        ambigiousTypes = ()
+        idx = self.__namedTypesLen
+        while idx > 0:
+            idx = idx - 1
+            t = self.__namedTypes[idx]
+            if t.isOptional or t.isDefaulted:
+                ambigiousTypes = (t, ) + ambigiousTypes
+            else:
+                ambigiousTypes = (t, )
+            self.__ambigiousTypes[idx] = NamedTypes(*ambigiousTypes)
+        
+    def getTagMapNearPosition(self, idx):
+        if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
+        try:
+            return self.__ambigiousTypes[idx].getTagMap()
+        except KeyError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def getPositionNearType(self, tagSet, idx):
+        if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
+        try:
+            return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet)
+        except KeyError:
+            raise error.PyAsn1Error('Type position out of range')
+
+    def genMinTagSet(self):
+        if self.__minTagSet is None:
+            for t in self.__namedTypes:
+                __type = t.getType()
+                tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)()
+                if self.__minTagSet is None or tagSet < self.__minTagSet:
+                    self.__minTagSet = tagSet
+        return self.__minTagSet
+    
+    def getTagMap(self, uniq=False):
+        if self.__tagMap[uniq] is None:
+            tagMap = tagmap.TagMap()
+            for nt in self.__namedTypes:
+                tagMap = tagMap.clone(
+                    nt.getType(), nt.getType().getTagMap(), uniq
+                    )
+            self.__tagMap[uniq] = tagMap
+        return self.__tagMap[uniq]
diff --git a/lib/pyasn1/pyasn1/type/namedval.py b/lib/pyasn1/pyasn1/type/namedval.py
new file mode 100644
index 0000000..d0fea7c
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/namedval.py
@@ -0,0 +1,46 @@
+# ASN.1 named integers
+from pyasn1 import error
+
+__all__ = [ 'NamedValues' ]
+
+class NamedValues:
+    def __init__(self, *namedValues):
+        self.nameToValIdx = {}; self.valToNameIdx = {}
+        self.namedValues = ()        
+        automaticVal = 1
+        for namedValue in namedValues:
+            if isinstance(namedValue, tuple):
+                name, val = namedValue
+            else:
+                name = namedValue
+                val = automaticVal
+            if name in self.nameToValIdx:
+                raise error.PyAsn1Error('Duplicate name %s' % (name,))
+            self.nameToValIdx[name] = val
+            if val in self.valToNameIdx:
+                raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val))
+            self.valToNameIdx[val] = name
+            self.namedValues = self.namedValues + ((name, val),)
+            automaticVal = automaticVal + 1
+    def __str__(self): return str(self.namedValues)
+    
+    def getName(self, value):
+        if value in self.valToNameIdx:
+            return self.valToNameIdx[value]
+
+    def getValue(self, name):
+        if name in self.nameToValIdx:
+            return self.nameToValIdx[name]
+    
+    def __getitem__(self, i): return self.namedValues[i]
+    def __len__(self): return len(self.namedValues)
+
+    def __add__(self, namedValues):
+        return self.__class__(*self.namedValues + namedValues)
+    def __radd__(self, namedValues):
+        return self.__class__(*namedValues + tuple(self))
+        
+    def clone(self, *namedValues):
+        return self.__class__(*tuple(self) + namedValues)
+
+# XXX clone/subtype?
diff --git a/lib/pyasn1/pyasn1/type/tag.py b/lib/pyasn1/pyasn1/type/tag.py
new file mode 100644
index 0000000..1144907
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/tag.py
@@ -0,0 +1,122 @@
+# ASN.1 types tags
+from operator import getitem
+from pyasn1 import error
+
+tagClassUniversal = 0x00
+tagClassApplication = 0x40
+tagClassContext = 0x80
+tagClassPrivate = 0xC0
+
+tagFormatSimple = 0x00
+tagFormatConstructed = 0x20
+
+tagCategoryImplicit = 0x01
+tagCategoryExplicit = 0x02
+tagCategoryUntagged = 0x04
+
+class Tag:
+    def __init__(self, tagClass, tagFormat, tagId):
+        if tagId < 0:
+            raise error.PyAsn1Error(
+                'Negative tag ID (%s) not allowed' % (tagId,)
+                )
+        self.__tag = (tagClass, tagFormat, tagId)
+        self.uniq = (tagClass, tagId)
+        self.__hashedUniqTag = hash(self.uniq)
+
+    def __repr__(self):
+        return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % (
+            (self.__class__.__name__,) + self.__tag
+            )
+    # These is really a hotspot -- expose public "uniq" attribute to save on
+    # function calls
+    def __eq__(self, other): return self.uniq == other.uniq
+    def __ne__(self, other): return self.uniq != other.uniq
+    def __lt__(self, other): return self.uniq < other.uniq
+    def __le__(self, other): return self.uniq <= other.uniq
+    def __gt__(self, other): return self.uniq > other.uniq
+    def __ge__(self, other): return self.uniq >= other.uniq
+    def __hash__(self): return self.__hashedUniqTag
+    def __getitem__(self, idx): return self.__tag[idx]
+    def __and__(self, otherTag):
+        (tagClass, tagFormat, tagId) = otherTag
+        return self.__class__(
+            self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId
+            )
+    def __or__(self, otherTag):
+        (tagClass, tagFormat, tagId) = otherTag
+        return self.__class__(
+            self.__tag[0]|tagClass,
+            self.__tag[1]|tagFormat,
+            self.__tag[2]|tagId
+            )
+    def asTuple(self): return self.__tag  # __getitem__() is slow
+    
+class TagSet:
+    def __init__(self, baseTag=(), *superTags):
+        self.__baseTag = baseTag
+        self.__superTags = superTags
+        self.__hashedSuperTags = hash(superTags)
+        _uniq = ()
+        for t in superTags:
+            _uniq = _uniq + t.uniq
+        self.uniq = _uniq
+        self.__lenOfSuperTags = len(superTags)
+        
+    def __repr__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__,
+            ', '.join([repr(x) for x in self.__superTags])
+            )
+
+    def __add__(self, superTag):
+        return self.__class__(
+            self.__baseTag, *self.__superTags + (superTag,)
+            )
+    def __radd__(self, superTag):
+        return self.__class__(
+            self.__baseTag, *(superTag,) + self.__superTags
+            )
+
+    def tagExplicitly(self, superTag):
+        tagClass, tagFormat, tagId = superTag
+        if tagClass == tagClassUniversal:
+            raise error.PyAsn1Error(
+                'Can\'t tag with UNIVERSAL-class tag'
+                )
+        if tagFormat != tagFormatConstructed:
+            superTag = Tag(tagClass, tagFormatConstructed, tagId)
+        return self + superTag
+
+    def tagImplicitly(self, superTag):
+        tagClass, tagFormat, tagId = superTag
+        if self.__superTags:
+            superTag = Tag(tagClass, self.__superTags[-1][1], tagId)
+        return self[:-1] + superTag
+
+    def getBaseTag(self): return self.__baseTag
+    def __getitem__(self, idx):
+        if isinstance(idx, slice):
+            return self.__class__(
+               self.__baseTag, *getitem(self.__superTags, idx)
+            )
+        return self.__superTags[idx]
+    def __eq__(self, other): return self.uniq == other.uniq
+    def __ne__(self, other): return self.uniq != other.uniq
+    def __lt__(self, other): return self.uniq < other.uniq
+    def __le__(self, other): return self.uniq <= other.uniq
+    def __gt__(self, other): return self.uniq > other.uniq
+    def __ge__(self, other): return self.uniq >= other.uniq
+    def __hash__(self): return self.__hashedSuperTags
+    def __len__(self): return self.__lenOfSuperTags
+    def isSuperTagSetOf(self, tagSet):
+        if len(tagSet) < self.__lenOfSuperTags:
+            return
+        idx = self.__lenOfSuperTags - 1
+        while idx >= 0:
+            if self.__superTags[idx] != tagSet[idx]:
+                return
+            idx = idx - 1
+        return 1
+    
+def initTagSet(tag): return TagSet(tag, tag)
diff --git a/lib/pyasn1/pyasn1/type/tagmap.py b/lib/pyasn1/pyasn1/type/tagmap.py
new file mode 100644
index 0000000..7cec3a1
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/tagmap.py
@@ -0,0 +1,52 @@
+from pyasn1 import error
+
+class TagMap:
+    def __init__(self, posMap={}, negMap={}, defType=None):
+        self.__posMap = posMap.copy()
+        self.__negMap = negMap.copy()
+        self.__defType = defType
+        
+    def __contains__(self, tagSet):
+        return tagSet in self.__posMap or \
+               self.__defType is not None and tagSet not in self.__negMap
+
+    def __getitem__(self, tagSet):
+        if tagSet in self.__posMap:
+            return self.__posMap[tagSet]
+        elif tagSet in self.__negMap:
+            raise error.PyAsn1Error('Key in negative map')
+        elif self.__defType is not None:
+            return self.__defType
+        else:
+            raise KeyError()
+
+    def __repr__(self):
+        s = '%r/%r' % (self.__posMap, self.__negMap)
+        if self.__defType is not None:
+            s = s + '/%r' % (self.__defType,)
+        return s
+
+    def clone(self, parentType, tagMap, uniq=False):
+        if self.__defType is not None and tagMap.getDef() is not None:
+            raise error.PyAsn1Error('Duplicate default value at %s' % (self,))
+        if tagMap.getDef() is not None:
+            defType = tagMap.getDef()
+        else:
+            defType = self.__defType
+            
+        posMap = self.__posMap.copy()
+        for k in tagMap.getPosMap():
+            if uniq and k in posMap:
+                raise error.PyAsn1Error('Duplicate positive key %s' % (k,))
+            posMap[k] = parentType
+
+        negMap = self.__negMap.copy()
+        negMap.update(tagMap.getNegMap())
+        
+        return self.__class__(
+            posMap, negMap, defType,
+            )
+
+    def getPosMap(self): return self.__posMap.copy()
+    def getNegMap(self): return self.__negMap.copy()
+    def getDef(self): return self.__defType
diff --git a/lib/pyasn1/pyasn1/type/univ.py b/lib/pyasn1/pyasn1/type/univ.py
new file mode 100644
index 0000000..81a5637
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/univ.py
@@ -0,0 +1,1037 @@
+# ASN.1 "universal" data types
+import operator, sys
+from pyasn1.type import base, tag, constraint, namedtype, namedval, tagmap
+from pyasn1.codec.ber import eoo
+from pyasn1.compat import octets
+from pyasn1 import error
+
+# "Simple" ASN.1 types (yet incomplete)
+
+class Integer(base.AbstractSimpleAsn1Item):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
+        )
+    namedValues = namedval.NamedValues()
+    def __init__(self, value=None, tagSet=None, subtypeSpec=None,
+                 namedValues=None):
+        if namedValues is None:
+            self.__namedValues = self.namedValues
+        else:
+            self.__namedValues = namedValues
+        base.AbstractSimpleAsn1Item.__init__(
+            self, value, tagSet, subtypeSpec
+            )
+
+    def __and__(self, value): return self.clone(self._value & value)
+    def __rand__(self, value): return self.clone(value & self._value)
+    def __or__(self, value): return self.clone(self._value | value)
+    def __ror__(self, value): return self.clone(value | self._value)
+    def __xor__(self, value): return self.clone(self._value ^ value)
+    def __rxor__(self, value): return self.clone(value ^ self._value)
+    def __lshift__(self, value): return self.clone(self._value << value)
+    def __rshift__(self, value): return self.clone(self._value >> value)
+
+    def __add__(self, value): return self.clone(self._value + value)
+    def __radd__(self, value): return self.clone(value + self._value)
+    def __sub__(self, value): return self.clone(self._value - value)
+    def __rsub__(self, value): return self.clone(value - self._value)
+    def __mul__(self, value): return self.clone(self._value * value)
+    def __rmul__(self, value): return self.clone(value * self._value)
+    def __mod__(self, value): return self.clone(self._value % value)
+    def __rmod__(self, value): return self.clone(value % self._value)
+    def __pow__(self, value, modulo=None): return self.clone(pow(self._value, value, modulo))
+    def __rpow__(self, value): return self.clone(pow(value, self._value))
+
+    if sys.version_info[0] <= 2:
+        def __div__(self, value):  return self.clone(self._value // value)
+        def __rdiv__(self, value):  return self.clone(value // self._value)
+    else:
+        def __truediv__(self, value):  return self.clone(self._value / value)
+        def __rtruediv__(self, value):  return self.clone(value / self._value)
+        def __divmod__(self, value):  return self.clone(self._value // value)
+        def __rdivmod__(self, value):  return self.clone(value // self._value)
+
+        __hash__ = base.AbstractSimpleAsn1Item.__hash__
+
+    def __int__(self): return int(self._value)
+    if sys.version_info[0] <= 2:
+        def __long__(self): return long(self._value)
+    def __float__(self): return float(self._value)    
+    def __abs__(self): return abs(self._value)
+    def __index__(self): return int(self._value)
+
+    def __lt__(self, value): return self._value < value
+    def __le__(self, value): return self._value <= value
+    def __eq__(self, value): return self._value == value
+    def __ne__(self, value): return self._value != value
+    def __gt__(self, value): return self._value > value
+    def __ge__(self, value): return self._value >= value
+
+    def prettyIn(self, value):
+        if not isinstance(value, str):
+            return int(value)
+        r = self.__namedValues.getValue(value)
+        if r is not None:
+            return r
+        try:
+            return int(value)
+        except ValueError:
+            raise error.PyAsn1Error(
+                'Can\'t coerce %s into integer: %s' % (value, sys.exc_info()[1])
+                )
+
+    def prettyOut(self, value):
+        r = self.__namedValues.getName(value)
+        return r is None and str(value) or repr(r)
+
+    def getNamedValues(self): return self.__namedValues
+
+    def clone(self, value=None, tagSet=None, subtypeSpec=None,
+              namedValues=None):
+        if value is None and tagSet is None and subtypeSpec is None \
+               and namedValues is None:
+            return self
+        if value is None:
+            value = self._value
+        if tagSet is None:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        if namedValues is None:
+            namedValues = self.__namedValues
+        return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+    def subtype(self, value=None, implicitTag=None, explicitTag=None,
+                subtypeSpec=None, namedValues=None):
+        if value is None:
+            value = self._value
+        if implicitTag is not None:
+            tagSet = self._tagSet.tagImplicitly(implicitTag)
+        elif explicitTag is not None:
+            tagSet = self._tagSet.tagExplicitly(explicitTag)
+        else:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        else:
+            subtypeSpec = subtypeSpec + self._subtypeSpec
+        if namedValues is None:
+            namedValues = self.__namedValues
+        else:
+            namedValues = namedValues + self.__namedValues
+        return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+class Boolean(Integer):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01),
+        )
+    subtypeSpec = Integer.subtypeSpec+constraint.SingleValueConstraint(0,1)
+    namedValues = Integer.namedValues.clone(('False', 0), ('True', 1))
+
+class BitString(base.AbstractSimpleAsn1Item):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
+        )
+    namedValues = namedval.NamedValues()
+    def __init__(self, value=None, tagSet=None, subtypeSpec=None,
+                 namedValues=None):
+        if namedValues is None:
+            self.__namedValues = self.namedValues
+        else:
+            self.__namedValues = namedValues
+        base.AbstractSimpleAsn1Item.__init__(
+            self, value, tagSet, subtypeSpec
+            )
+
+    def clone(self, value=None, tagSet=None, subtypeSpec=None,
+              namedValues=None):
+        if value is None and tagSet is None and subtypeSpec is None \
+               and namedValues is None:
+            return self
+        if value is None:
+            value = self._value
+        if tagSet is None:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        if namedValues is None:
+            namedValues = self.__namedValues
+        return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+    def subtype(self, value=None, implicitTag=None, explicitTag=None,
+                subtypeSpec=None, namedValues=None):
+        if value is None:
+            value = self._value
+        if implicitTag is not None:
+            tagSet = self._tagSet.tagImplicitly(implicitTag)
+        elif explicitTag is not None:
+            tagSet = self._tagSet.tagExplicitly(explicitTag)
+        else:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        else:
+            subtypeSpec = subtypeSpec + self._subtypeSpec
+        if namedValues is None:
+            namedValues = self.__namedValues
+        else:
+            namedValues = namedValues + self.__namedValues
+        return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+    def __str__(self): return str(tuple(self))
+
+    # Immutable sequence object protocol
+
+    def __len__(self):
+        if self._len is None:
+            self._len = len(self._value)
+        return self._len
+    def __getitem__(self, i):
+        if isinstance(i, slice):
+            return self.clone(operator.getitem(self._value, i))
+        else:
+            return self._value[i]
+
+    def __add__(self, value): return self.clone(self._value + value)
+    def __radd__(self, value): return self.clone(value + self._value)
+    def __mul__(self, value): return self.clone(self._value * value)
+    def __rmul__(self, value): return self * value
+
+    def prettyIn(self, value):
+        r = []
+        if not value:
+            return ()
+        elif isinstance(value, str):
+            if value[0] == '\'':
+                if value[-2:] == '\'B':
+                    for v in value[1:-2]:
+                        if v == '0':
+                            r.append(0)
+                        elif v == '1':
+                            r.append(1)
+                        else:
+                            raise error.PyAsn1Error(
+                                'Non-binary BIT STRING initializer %s' % (v,)
+                                )
+                    return tuple(r)
+                elif value[-2:] == '\'H':
+                    for v in value[1:-2]:
+                        i = 4
+                        v = int(v, 16)
+                        while i:
+                            i = i - 1
+                            r.append((v>>i)&0x01)
+                    return tuple(r)
+                else:
+                    raise error.PyAsn1Error(
+                        'Bad BIT STRING value notation %s' % (value,)
+                        )                
+            else:
+                for i in value.split(','):
+                    j = self.__namedValues.getValue(i)
+                    if j is None:
+                        raise error.PyAsn1Error(
+                            'Unknown bit identifier \'%s\'' % (i,)
+                            )
+                    if j >= len(r):
+                        r.extend([0]*(j-len(r)+1))
+                    r[j] = 1
+                return tuple(r)
+        elif isinstance(value, (tuple, list)):
+            r = tuple(value)
+            for b in r:
+                if b and b != 1:
+                    raise error.PyAsn1Error(
+                        'Non-binary BitString initializer \'%s\'' % (r,)
+                        )
+            return r
+        elif isinstance(value, BitString):
+            return tuple(value)
+        else:
+            raise error.PyAsn1Error(
+                'Bad BitString initializer type \'%s\'' % (value,)
+                )
+
+    def prettyOut(self, value):
+        return '\"\'%s\'B\"' % ''.join([str(x) for x in value])
+
+class OctetString(base.AbstractSimpleAsn1Item):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
+        )
+    defaultBinValue = defaultHexValue = base.noValue
+    encoding = 'us-ascii'
+    def __init__(self, value=None, tagSet=None, subtypeSpec=None,
+                 encoding=None, binValue=None, hexValue=None):
+        if encoding is None:
+            self._encoding = self.encoding
+        else:
+            self._encoding = encoding
+        if binValue is not None:
+            value = self.fromBinaryString(binValue)
+        if hexValue is not None:
+            value = self.fromHexString(hexValue)
+        if value is None or value is base.noValue:
+            value = self.defaultHexValue
+        if value is None or value is base.noValue:
+            value = self.defaultBinValue
+        self.__intValue = None
+        base.AbstractSimpleAsn1Item.__init__(self, value, tagSet, subtypeSpec)
+
+    def clone(self, value=None, tagSet=None, subtypeSpec=None,
+              encoding=None, binValue=None, hexValue=None):
+        if value is None and tagSet is None and subtypeSpec is None and \
+               encoding is None and binValue is None and hexValue is None:
+            return self
+        if value is None and binValue is None and hexValue is None:
+            value = self._value
+        if tagSet is None:
+            tagSet = self._tagSet
+        if subtypeSpec is None:
+            subtypeSpec = self._subtypeSpec
+        if encoding is None:
+            encoding = self._encoding
+        return self.__class__(
+            value, tagSet, subtypeSpec, encoding, binValue, hexValue
+            )
+   
+    if sys.version_info[0] <= 2:
+        def prettyIn(self, value):
+            if isinstance(value, str):
+                return value
+            elif isinstance(value, (tuple, list)):
+                try:
+                    return ''.join([ chr(x) for x in value ])
+                except ValueError:
+                    raise error.PyAsn1Error(
+                        'Bad OctetString initializer \'%s\'' % (value,)
+                        )                
+            else:
+                return str(value)
+    else:
+        def prettyIn(self, value):
+            if isinstance(value, bytes):
+                return value
+            elif isinstance(value, OctetString):
+                return value.asOctets()
+            elif isinstance(value, (tuple, list, map)):
+                try:
+                    return bytes(value)
+                except ValueError:
+                    raise error.PyAsn1Error(
+                        'Bad OctetString initializer \'%s\'' % (value,)
+                        )
+            else:
+                try:
+                    return str(value).encode(self._encoding)
+                except UnicodeEncodeError:
+                    raise error.PyAsn1Error(
+                        'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+                        )
+                        
+
+    def fromBinaryString(self, value):
+        bitNo = 8; byte = 0; r = ()
+        for v in value:
+            if bitNo:
+                bitNo = bitNo - 1
+            else:
+                bitNo = 7
+                r = r + (byte,)
+                byte = 0
+            if v == '0':
+                v = 0
+            elif v == '1':
+                v = 1
+            else:
+                raise error.PyAsn1Error(
+                    'Non-binary OCTET STRING initializer %s' % (v,)
+                    )
+            byte = byte | (v << bitNo)
+        return octets.ints2octs(r + (byte,))
+        
+    def fromHexString(self, value):            
+        r = p = ()
+        for v in value:
+            if p:
+                r = r + (int(p+v, 16),)
+                p = ()
+            else:
+                p = v
+        if p:
+            r = r + (int(p+'0', 16),)
+        return octets.ints2octs(r)
+
+    def prettyOut(self, value):
+        if sys.version_info[0] <= 2:
+            numbers = tuple([ ord(x) for x in value ])
+        else:
+            numbers = tuple(value)
+        if [ x for x in numbers if x < 32 or x > 126 ]:
+            return '0x' + ''.join([ '%.2x' % x for x in numbers ])
+        else:
+            return str(value)
+
+    def __repr__(self):
+        if self._value is base.noValue:
+            return self.__class__.__name__ + '()'
+        if [ x for x in self.asNumbers() if x < 32 or x > 126 ]:
+            return self.__class__.__name__ + '(hexValue=\'' + ''.join([ '%.2x' % x for x in self.asNumbers() ])+'\')'
+        else:
+            return self.__class__.__name__ + '(\'' + self.prettyOut(self._value) + '\')'
+                                
+    if sys.version_info[0] <= 2:
+        def __str__(self): return str(self._value)
+        def __unicode__(self):
+            return self._value.decode(self._encoding, 'ignore')
+        def asOctets(self): return self._value
+        def asNumbers(self):
+            if self.__intValue is None:
+                self.__intValue = tuple([ ord(x) for x in self._value ])
+            return self.__intValue
+    else:
+        def __str__(self): return self._value.decode(self._encoding, 'ignore')
+        def __bytes__(self): return self._value
+        def asOctets(self): return self._value
+        def asNumbers(self):
+            if self.__intValue is None:
+                self.__intValue = tuple(self._value)
+            return self.__intValue
+ 
+    # Immutable sequence object protocol
+    
+    def __len__(self):
+        if self._len is None:
+            self._len = len(self._value)
+        return self._len
+    def __getitem__(self, i):
+        if isinstance(i, slice):
+            return self.clone(operator.getitem(self._value, i))
+        else:
+            return self._value[i]
+
+    def __add__(self, value): return self.clone(self._value + self.prettyIn(value))
+    def __radd__(self, value): return self.clone(self.prettyIn(value) + self._value)
+    def __mul__(self, value): return self.clone(self._value * value)
+    def __rmul__(self, value): return self * value
+
+class Null(OctetString):
+    defaultValue = ''.encode()  # This is tightly constrained
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05)
+        )
+    subtypeSpec = OctetString.subtypeSpec+constraint.SingleValueConstraint(''.encode())
+    
+if sys.version_info[0] <= 2:
+    intTypes = (int, long)
+else:
+    intTypes = int
+
+class ObjectIdentifier(base.AbstractSimpleAsn1Item):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
+        )
+    def __add__(self, other): return self.clone(self._value + other)
+    def __radd__(self, other): return self.clone(other + self._value)
+
+    def asTuple(self): return self._value
+    
+    # Sequence object protocol
+    
+    def __len__(self):
+        if self._len is None:
+            self._len = len(self._value)
+        return self._len
+    def __getitem__(self, i):
+        if isinstance(i, slice):
+            return self.clone(
+                operator.getitem(self._value, i)
+                )
+        else:
+            return self._value[i]
+
+    def __str__(self): return self.prettyPrint()
+    
+    def index(self, suboid): return self._value.index(suboid)
+
+    def isPrefixOf(self, value):
+        """Returns true if argument OID resides deeper in the OID tree"""
+        l = len(self)
+        if l <= len(value):
+            if self._value[:l] == value[:l]:
+                return 1
+        return 0
+
+    def prettyIn(self, value):
+        """Dotted -> tuple of numerics OID converter"""
+        if isinstance(value, tuple):
+            pass
+        elif isinstance(value, ObjectIdentifier):
+            return tuple(value)        
+        elif isinstance(value, str):
+            r = []
+            for element in [ x for x in value.split('.') if x != '' ]:
+                try:
+                    r.append(int(element, 0))
+                except ValueError:
+                    raise error.PyAsn1Error(
+                        'Malformed Object ID %s at %s: %s' %
+                        (str(value), self.__class__.__name__, sys.exc_info()[1])
+                        )
+            value = tuple(r)
+        else:
+            try:
+                value = tuple(value)
+            except TypeError:
+                raise error.PyAsn1Error(
+                        'Malformed Object ID %s at %s: %s' %
+                        (str(value), self.__class__.__name__,sys.exc_info()[1])
+                        )
+
+        for x in value:
+            if not isinstance(x, intTypes) or x < 0:
+                raise error.PyAsn1Error(
+                    'Invalid sub-ID in %s at %s' % (value, self.__class__.__name__)
+                    )
+    
+        return value
+
+    def prettyOut(self, value): return '.'.join([ str(x) for x in value ])
+    
+class Real(base.AbstractSimpleAsn1Item):
+    try:
+        _plusInf = float('inf')
+        _minusInf = float('-inf')
+        _inf = (_plusInf, _minusInf)
+    except ValueError:
+        # Infinity support is platform and Python dependent
+        _plusInf = _minusInf = None
+        _inf = ()
+
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
+        )
+
+    def __normalizeBase10(self, value):
+        m, b, e = value
+        while m and m % 10 == 0:
+            m = m / 10
+            e = e + 1
+        return m, b, e
+
+    def prettyIn(self, value):
+        if isinstance(value, tuple) and len(value) == 3:
+            for d in value:
+                if not isinstance(d, intTypes):
+                    raise error.PyAsn1Error(
+                        'Lame Real value syntax: %s' % (value,)
+                        )
+            if value[1] not in (2, 10):
+                raise error.PyAsn1Error(
+                    'Prohibited base for Real value: %s' % (value[1],)
+                    )
+            if value[1] == 10:
+                value = self.__normalizeBase10(value)
+            return value
+        elif isinstance(value, intTypes):
+            return self.__normalizeBase10((value, 10, 0))
+        elif isinstance(value, float):
+            if self._inf and value in self._inf:
+                return value
+            else:
+                e = 0
+                while int(value) != value:
+                    value = value * 10
+                    e = e - 1
+                return self.__normalizeBase10((int(value), 10, e))
+        elif isinstance(value, Real):
+            return tuple(value)
+        elif isinstance(value, str):  # handle infinite literal
+            try:
+                return float(value)
+            except ValueError:
+                pass
+        raise error.PyAsn1Error(
+            'Bad real value syntax: %s' % (value,)
+            )
+        
+    def prettyOut(self, value):
+        if value in self._inf:
+            return '\'%s\'' % value
+        else:
+            return str(value)
+
+    def isPlusInfinity(self): return self._value == self._plusInf
+    def isMinusInfinity(self): return self._value == self._minusInf
+    def isInfinity(self): return self._value in self._inf
+    
+    def __str__(self): return str(float(self))
+    
+    def __add__(self, value): return self.clone(float(self) + value)
+    def __radd__(self, value): return self + value
+    def __mul__(self, value): return self.clone(float(self) * value)
+    def __rmul__(self, value): return self * value
+    def __sub__(self, value): return self.clone(float(self) - value)
+    def __rsub__(self, value): return self.clone(value - float(self))
+    def __mod__(self, value): return self.clone(float(self) % value)
+    def __rmod__(self, value): return self.clone(value % float(self))
+    def __pow__(self, value, modulo=None): return self.clone(pow(float(self), value, modulo))
+    def __rpow__(self, value): return self.clone(pow(value, float(self)))
+
+    if sys.version_info[0] <= 2:
+        def __div__(self, value): return self.clone(float(self) / value)
+        def __rdiv__(self, value): return self.clone(value / float(self))
+    else:
+        def __truediv__(self, value): return self.clone(float(self) / value)
+        def __rtruediv__(self, value): return self.clone(value / float(self))
+        def __divmod__(self, value): return self.clone(float(self) // value)
+        def __rdivmod__(self, value): return self.clone(value // float(self))
+
+    def __int__(self): return int(float(self))
+    if sys.version_info[0] <= 2:
+        def __long__(self): return long(float(self))
+    def __float__(self):
+        if self._value in self._inf:
+            return self._value
+        else:
+            return float(
+                self._value[0] * pow(self._value[1], self._value[2])
+                )
+    def __abs__(self): return abs(float(self))
+
+    def __lt__(self, value): return float(self) < value
+    def __le__(self, value): return float(self) <= value
+    def __eq__(self, value): return float(self) == value
+    def __ne__(self, value): return float(self) != value
+    def __gt__(self, value): return float(self) > value
+    def __ge__(self, value): return float(self) >= value
+
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self): return bool(float(self))
+    else:
+        def __bool__(self): return bool(float(self))
+        __hash__ = base.AbstractSimpleAsn1Item.__hash__
+
+    def __getitem__(self, idx):
+        if self._value in self._inf:
+            raise error.PyAsn1Error('Invalid infinite value operation')
+        else:
+            return self._value[idx]
+    
+class Enumerated(Integer):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A)
+        )
+
+# "Structured" ASN.1 types
+
+class SetOf(base.AbstractConstructedAsn1Item):
+    componentType = None
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+        )
+    typeId = 1
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        idx = 0; l = len(self._componentValues)
+        while idx < l:
+            c = self._componentValues[idx]
+            if c is not None:
+                if isinstance(c, base.AbstractConstructedAsn1Item):
+                    myClone.setComponentByPosition(
+                        idx, c.clone(cloneValueFlag=cloneValueFlag)
+                        )
+                else:
+                    myClone.setComponentByPosition(idx, c.clone())
+            idx = idx + 1
+        
+    def _verifyComponent(self, idx, value):
+        if self._componentType is not None and \
+               not self._componentType.isSuperTypeOf(value):
+            raise error.PyAsn1Error('Component type error %s' % (value,))
+
+    def getComponentByPosition(self, idx): return self._componentValues[idx]
+    def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
+        l = len(self._componentValues)
+        if idx >= l:
+            self._componentValues = self._componentValues + (idx-l+1)*[None]
+        if value is None:
+            if self._componentValues[idx] is None:
+                if self._componentType is None:
+                    raise error.PyAsn1Error('Component type not defined')
+                self._componentValues[idx] = self._componentType.clone()
+                self._componentValuesSet = self._componentValuesSet + 1
+            return self
+        elif not isinstance(value, base.Asn1Item):
+            if self._componentType is None:
+                raise error.PyAsn1Error('Component type not defined')
+            if isinstance(self._componentType, base.AbstractSimpleAsn1Item):
+                value = self._componentType.clone(value=value)
+            else:
+                raise error.PyAsn1Error('Instance value required')
+        if verifyConstraints:
+            if self._componentType is not None:
+                self._verifyComponent(idx, value)
+            self._verifySubtypeSpec(value, idx)            
+        if self._componentValues[idx] is None:
+            self._componentValuesSet = self._componentValuesSet + 1
+        self._componentValues[idx] = value
+        return self
+
+    def getComponentTagMap(self):
+        if self._componentType is not None:
+            return self._componentType.getTagMap()
+
+    def prettyPrint(self, scope=0):
+        scope = scope + 1
+        r = self.__class__.__name__ + ':\n'        
+        for idx in range(len(self._componentValues)):
+            r = r + ' '*scope
+            if self._componentValues[idx] is None:
+                r = r + '<empty>'
+            else:
+                r = r + self._componentValues[idx].prettyPrint(scope)
+        return r
+
+class SequenceOf(SetOf):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+        )
+    typeId = 2
+
+class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
+    componentType = namedtype.NamedTypes()
+    def __init__(self, componentType=None, tagSet=None,
+                 subtypeSpec=None, sizeSpec=None):
+        base.AbstractConstructedAsn1Item.__init__(
+            self, componentType, tagSet, subtypeSpec, sizeSpec
+            )
+        if self._componentType is None:
+            self._componentTypeLen = 0
+        else:
+            self._componentTypeLen = len(self._componentType)
+
+    def __getitem__(self, idx):
+        if isinstance(idx, str):
+            return self.getComponentByName(idx)
+        else:
+            return base.AbstractConstructedAsn1Item.__getitem__(self, idx)
+
+    def __setitem__(self, idx, value):
+        if isinstance(idx, str):
+            self.setComponentByName(idx, value)
+        else:
+            base.AbstractConstructedAsn1Item.__setitem__(self, idx, value)
+        
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        idx = 0; l = len(self._componentValues)
+        while idx < l:
+            c = self._componentValues[idx]
+            if c is not None:
+                if isinstance(c, base.AbstractConstructedAsn1Item):
+                    myClone.setComponentByPosition(
+                        idx, c.clone(cloneValueFlag=cloneValueFlag)
+                        )
+                else:
+                    myClone.setComponentByPosition(idx, c.clone())
+            idx = idx + 1
+
+    def _verifyComponent(self, idx, value):
+        if idx >= self._componentTypeLen:
+            raise error.PyAsn1Error(
+                'Component type error out of range'
+                )
+        t = self._componentType[idx].getType()
+        if not t.isSuperTypeOf(value):
+            raise error.PyAsn1Error('Component type error %r vs %r' % (t, value))
+
+    def getComponentByName(self, name):
+        return self.getComponentByPosition(
+            self._componentType.getPositionByName(name)
+            )
+    def setComponentByName(self, name, value=None, verifyConstraints=True):
+        return self.setComponentByPosition(
+            self._componentType.getPositionByName(name), value,
+            verifyConstraints
+            )
+
+    def getComponentByPosition(self, idx):
+        try:
+            return self._componentValues[idx]
+        except IndexError:
+            if idx < self._componentTypeLen:
+                return
+            raise
+    def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
+        l = len(self._componentValues)
+        if idx >= l:
+            self._componentValues = self._componentValues + (idx-l+1)*[None]
+        if value is None:
+            if self._componentValues[idx] is None:
+                self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone()
+                self._componentValuesSet = self._componentValuesSet + 1
+            return self
+        elif not isinstance(value, base.Asn1Item):
+            t = self._componentType.getTypeByPosition(idx)
+            if isinstance(t, base.AbstractSimpleAsn1Item):
+                value = t.clone(value=value)
+            else:
+                raise error.PyAsn1Error('Instance value required')
+        if verifyConstraints:
+            if self._componentTypeLen:
+                self._verifyComponent(idx, value)
+            self._verifySubtypeSpec(value, idx)            
+        if self._componentValues[idx] is None:
+            self._componentValuesSet = self._componentValuesSet + 1
+        self._componentValues[idx] = value
+        return self
+
+    def getNameByPosition(self, idx):
+        if self._componentTypeLen:
+            return self._componentType.getNameByPosition(idx)
+
+    def getDefaultComponentByPosition(self, idx):
+        if self._componentTypeLen and self._componentType[idx].isDefaulted:
+            return self._componentType[idx].getType()
+
+    def getComponentType(self):
+        if self._componentTypeLen:
+            return self._componentType
+    
+    def setDefaultComponents(self):
+        if self._componentTypeLen == self._componentValuesSet:
+            return
+        idx = self._componentTypeLen
+        while idx:
+            idx = idx - 1
+            if self._componentType[idx].isDefaulted:
+                if self.getComponentByPosition(idx) is None:
+                    self.setComponentByPosition(idx)
+            elif not self._componentType[idx].isOptional:
+                if self.getComponentByPosition(idx) is None:
+                    raise error.PyAsn1Error(
+                        'Uninitialized component #%s at %r' % (idx, self)
+                        )
+
+    def prettyPrint(self, scope=0):
+        scope = scope + 1
+        r = self.__class__.__name__ + ':\n'
+        for idx in range(len(self._componentValues)):
+            if self._componentValues[idx] is not None:
+                r = r + ' '*scope
+                componentType = self.getComponentType()
+                if componentType is None:
+                    r = r + '<no-name>'
+                else:
+                    r = r + componentType.getNameByPosition(idx)
+                r = '%s=%s\n' % (
+                    r, self._componentValues[idx].prettyPrint(scope)
+                    )
+        return r
+
+class Sequence(SequenceAndSetBase):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+        )
+    typeId = 3
+
+    def getComponentTagMapNearPosition(self, idx):
+        if self._componentType:
+            return self._componentType.getTagMapNearPosition(idx)
+    
+    def getComponentPositionNearType(self, tagSet, idx):
+        if self._componentType:
+            return self._componentType.getPositionNearType(tagSet, idx)
+        else:
+            return idx
+    
+class Set(SequenceAndSetBase):
+    tagSet = baseTagSet = tag.initTagSet(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+        )
+    typeId = 4
+
+    def getComponent(self, innerFlag=0): return self
+    
+    def getComponentByType(self, tagSet, innerFlag=0):
+        c = self.getComponentByPosition(
+            self._componentType.getPositionByType(tagSet)
+            )
+        if innerFlag and isinstance(c, Set):
+            # get inner component by inner tagSet
+            return c.getComponent(1)
+        else:
+            # get outer component by inner tagSet
+            return c
+        
+    def setComponentByType(self, tagSet, value=None, innerFlag=0,
+                           verifyConstraints=True):
+        idx = self._componentType.getPositionByType(tagSet)
+        t = self._componentType.getTypeByPosition(idx)
+        if innerFlag:  # set inner component by inner tagSet
+            if t.getTagSet():
+                return self.setComponentByPosition(
+                    idx, value, verifyConstraints
+                    )
+            else:
+                t = self.setComponentByPosition(idx).getComponentByPosition(idx)
+                return t.setComponentByType(
+                    tagSet, value, innerFlag, verifyConstraints
+                    )
+        else:  # set outer component by inner tagSet
+            return self.setComponentByPosition(
+                idx, value, verifyConstraints
+                )
+            
+    def getComponentTagMap(self):
+        if self._componentType:
+            return self._componentType.getTagMap(True)
+
+    def getComponentPositionByType(self, tagSet):
+        if self._componentType:
+            return self._componentType.getPositionByType(tagSet)
+
+class Choice(Set):
+    tagSet = baseTagSet = tag.TagSet()  # untagged
+    sizeSpec = constraint.ConstraintsIntersection(
+        constraint.ValueSizeConstraint(1, 1)
+        )
+    typeId = 5
+    _currentIdx = None
+
+    def __eq__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] == other
+        return NotImplemented
+    def __ne__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] != other
+        return NotImplemented
+    def __lt__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] < other
+        return NotImplemented
+    def __le__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] <= other
+        return NotImplemented
+    def __gt__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] > other
+        return NotImplemented
+    def __ge__(self, other):
+        if self._componentValues:
+            return self._componentValues[self._currentIdx] >= other
+        return NotImplemented
+    if sys.version_info[0] <= 2:
+        def __nonzero__(self): return bool(self._componentValues)
+    else:
+        def __bool__(self): return bool(self._componentValues)
+
+    def __len__(self): return self._currentIdx is not None and 1 or 0
+    
+    def verifySizeSpec(self):
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+        else:
+            self._sizeSpec(' ')
+
+    def _cloneComponentValues(self, myClone, cloneValueFlag):
+        try:
+            c = self.getComponent()
+        except error.PyAsn1Error:
+            pass
+        else:
+            if isinstance(c, Choice):
+                tagSet = c.getEffectiveTagSet()
+            else:
+                tagSet = c.getTagSet()
+            if isinstance(c, base.AbstractConstructedAsn1Item):
+                myClone.setComponentByType(
+                    tagSet, c.clone(cloneValueFlag=cloneValueFlag)
+                    )
+            else:
+                myClone.setComponentByType(tagSet, c.clone())
+
+    def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
+        l = len(self._componentValues)
+        if idx >= l:
+            self._componentValues = self._componentValues + (idx-l+1)*[None]
+        if self._currentIdx is not None:
+            self._componentValues[self._currentIdx] = None
+        if value is None:
+            if self._componentValues[idx] is None:
+                self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone()
+                self._componentValuesSet = 1
+                self._currentIdx = idx
+            return self
+        elif not isinstance(value, base.Asn1Item):
+            value = self._componentType.getTypeByPosition(idx).clone(
+                value=value
+                )
+        if verifyConstraints:
+            if self._componentTypeLen:
+                self._verifyComponent(idx, value)
+            self._verifySubtypeSpec(value, idx)            
+        self._componentValues[idx] = value
+        self._currentIdx = idx
+        self._componentValuesSet = 1
+        return self
+
+    def getMinTagSet(self):
+        if self._tagSet:
+            return self._tagSet
+        else:
+            return self._componentType.genMinTagSet()
+
+    def getEffectiveTagSet(self):
+        if self._tagSet:
+            return self._tagSet
+        else:
+            c = self.getComponent()
+            if isinstance(c, Choice):
+                return c.getEffectiveTagSet()
+            else:
+                return c.getTagSet()
+
+    def getTagMap(self):
+        if self._tagSet:
+            return Set.getTagMap(self)
+        else:
+            return Set.getComponentTagMap(self)
+
+    def getComponent(self, innerFlag=0):
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+        else:
+            c = self._componentValues[self._currentIdx]
+            if innerFlag and isinstance(c, Choice):
+                return c.getComponent(innerFlag)
+            else:
+                return c
+
+    def getName(self, innerFlag=0):
+        if self._currentIdx is None:
+            raise error.PyAsn1Error('Component not chosen')
+        else:
+            if innerFlag:
+                c = self._componentValues[self._currentIdx]
+                if isinstance(c, Choice):
+                    return c.getName(innerFlag)
+            return self._componentType.getNameByPosition(self._currentIdx)
+
+    def setDefaultComponents(self): pass
+
+class Any(OctetString):
+    tagSet = baseTagSet = tag.TagSet()  # untagged
+    typeId = 6
+
+    def getTagMap(self):
+        return tagmap.TagMap(
+            { self.getTagSet(): self },
+            { eoo.endOfOctets.getTagSet(): eoo.endOfOctets },
+            self
+            )
+
+# XXX
+# coercion rules?
diff --git a/lib/pyasn1/pyasn1/type/useful.py b/lib/pyasn1/pyasn1/type/useful.py
new file mode 100644
index 0000000..a7139c2
--- /dev/null
+++ b/lib/pyasn1/pyasn1/type/useful.py
@@ -0,0 +1,12 @@
+# ASN.1 "useful" types
+from pyasn1.type import char, tag
+
+class GeneralizedTime(char.VisibleString):
+    tagSet = char.VisibleString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
+        )
+
+class UTCTime(char.VisibleString):
+    tagSet = char.VisibleString.tagSet.tagImplicitly(
+        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
+        )
diff --git a/lib/pyasn1_modules/pyasn1_modules/LICENSE b/lib/pyasn1_modules/pyasn1_modules/LICENSE
new file mode 100644
index 0000000..b87ef84
--- /dev/null
+++ b/lib/pyasn1_modules/pyasn1_modules/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2011-2012 Ilya Etingof <ilya@glas.net>, all rights reserved.
+
+THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION
+ENDANGERING HUMAN LIFE OR PROPERTY.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, 
+    this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+  * The name of the authors may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
diff --git a/lib/pyasn1_modules/pyasn1_modules/rfc2459.py b/lib/pyasn1_modules/pyasn1_modules/rfc2459.py
new file mode 100644
index 0000000..c5021e0
--- /dev/null
+++ b/lib/pyasn1_modules/pyasn1_modules/rfc2459.py
@@ -0,0 +1,903 @@
+#
+# X.509 message syntax
+#
+# ASN.1 source from:
+# http://www.trl.ibm.com/projects/xml/xss4j/data/asn1/grammars/x509.asn
+# http://www.ietf.org/rfc/rfc2459.txt
+#
+# Sample captures from:
+# http://wiki.wireshark.org/SampleCaptures/
+#
+from pyasn1.type import tag,namedtype,namedval,univ,constraint,char,useful
+
+MAX = 64  # XXX ?
+
+#
+# PKIX1Explicit88
+#
+
+# Upper Bounds
+ub_name = univ.Integer(32768)
+ub_common_name = univ.Integer(64)
+ub_locality_name = univ.Integer(128)
+ub_state_name = univ.Integer(128)
+ub_organization_name = univ.Integer(64)
+ub_organizational_unit_name = univ.Integer(64)
+ub_title = univ.Integer(64)
+ub_match = univ.Integer(128)
+ub_emailaddress_length = univ.Integer(128)
+ub_common_name_length = univ.Integer(64)
+ub_country_name_alpha_length = univ.Integer(2)
+ub_country_name_numeric_length = univ.Integer(3)
+ub_domain_defined_attributes = univ.Integer(4)
+ub_domain_defined_attribute_type_length = univ.Integer(8)
+ub_domain_defined_attribute_value_length = univ.Integer(128)
+ub_domain_name_length = univ.Integer(16)
+ub_extension_attributes = univ.Integer(256)
+ub_e163_4_number_length = univ.Integer(15)
+ub_e163_4_sub_address_length = univ.Integer(40)
+ub_generation_qualifier_length = univ.Integer(3)
+ub_given_name_length = univ.Integer(16)
+ub_initials_length = univ.Integer(5)
+ub_integer_options = univ.Integer(256)
+ub_numeric_user_id_length = univ.Integer(32)
+ub_organization_name_length = univ.Integer(64)
+ub_organizational_unit_name_length = univ.Integer(32)
+ub_organizational_units = univ.Integer(4)
+ub_pds_name_length = univ.Integer(16)
+ub_pds_parameter_length = univ.Integer(30)
+ub_pds_physical_address_lines = univ.Integer(6)
+ub_postal_code_length = univ.Integer(16)
+ub_surname_length = univ.Integer(40)
+ub_terminal_id_length = univ.Integer(24)
+ub_unformatted_address_length = univ.Integer(180)
+ub_x121_address_length = univ.Integer(16)
+
+class UniversalString(char.UniversalString): pass
+class BMPString(char.BMPString): pass
+class UTF8String(char.UTF8String): pass
+
+id_pkix = univ.ObjectIdentifier('1.3.6.1.5.5.7')
+id_pe = univ.ObjectIdentifier('1.3.6.1.5.5.7.1')
+id_qt = univ.ObjectIdentifier('1.3.6.1.5.5.7.2')
+id_kp = univ.ObjectIdentifier('1.3.6.1.5.5.7.3')
+id_ad = univ.ObjectIdentifier('1.3.6.1.5.5.7.48')
+
+id_qt_cps = univ.ObjectIdentifier('1.3.6.1.5.5.7.2.1')
+id_qt_unotice = univ.ObjectIdentifier('1.3.6.1.5.5.7.2.2')
+
+id_ad_ocsp = univ.ObjectIdentifier('1.3.6.1.5.5.7.48.1')
+id_ad_caIssuers = univ.ObjectIdentifier('1.3.6.1.5.5.7.48.2')
+
+class AttributeValue(univ.Any): pass
+
+class AttributeType(univ.ObjectIdentifier): pass
+
+class AttributeTypeAndValue(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('type', AttributeType()),
+        namedtype.NamedType('value', AttributeValue())
+        )
+
+class Attribute(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('type', AttributeType()),
+        namedtype.NamedType('vals', univ.SetOf(componentType=AttributeValue()))
+        )
+
+id_at = univ.ObjectIdentifier('2.5.4')
+id_at_name = univ.ObjectIdentifier('2.5.4.41')
+id_at_sutname = univ.ObjectIdentifier('2.5.4.4')
+id_at_givenName = univ.ObjectIdentifier('2.5.4.42')
+id_at_initials = univ.ObjectIdentifier('2.5.4.43')
+id_at_generationQualifier = univ.ObjectIdentifier('2.5.4.44')
+
+class X520name(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name)))
+    )
+
+id_at_commonName = univ.ObjectIdentifier('2.5.4.3')
+
+class X520CommonName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name)))
+    )
+
+id_at_localityName = univ.ObjectIdentifier('2.5.4.7')
+
+class X520LocalityName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name)))
+    )
+
+id_at_stateOrProvinceName = univ.ObjectIdentifier('2.5.4.8')
+
+class X520StateOrProvinceName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name)))
+    )
+
+id_at_organizationName = univ.ObjectIdentifier('2.5.4.10')
+
+class X520OrganizationName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name)))
+    )
+
+id_at_organizationalUnitName = univ.ObjectIdentifier('2.5.4.11')
+
+class X520OrganizationalUnitName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name)))
+    )
+
+id_at_title = univ.ObjectIdentifier('2.5.4.12')
+
+class X520Title(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title)))
+    )
+
+id_at_dnQualifier = univ.ObjectIdentifier('2.5.4.46')
+
+class X520dnQualifier(char.PrintableString): pass
+
+id_at_countryName = univ.ObjectIdentifier('2.5.4.6')
+
+class X520countryName(char.PrintableString):
+    subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(2, 2)
+
+pkcs_9 = univ.ObjectIdentifier('1.2.840.113549.1.9')
+
+emailAddress = univ.ObjectIdentifier('1.2.840.113549.1.9.1')
+
+class Pkcs9email(char.IA5String):
+    subtypeSpec = char.IA5String.subtypeSpec + constraint.ValueSizeConstraint(1, ub_emailaddress_length)
+
+# ----
+
+class DSAPrivateKey(univ.Sequence):
+    """PKIX compliant DSA private key structure"""
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('version', univ.Integer(namedValues=namedval.NamedValues(('v1', 0)))),
+        namedtype.NamedType('p', univ.Integer()),
+        namedtype.NamedType('q', univ.Integer()),
+        namedtype.NamedType('g', univ.Integer()),
+        namedtype.NamedType('public', univ.Integer()),
+        namedtype.NamedType('private', univ.Integer())
+        )
+
+# ----
+
+class RelativeDistinguishedName(univ.SetOf):
+    componentType = AttributeTypeAndValue()
+
+class RDNSequence(univ.SequenceOf):
+    componentType = RelativeDistinguishedName()
+
+class Name(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('', RDNSequence())
+        )
+
+class DirectoryString(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
+        namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
+        namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
+        namedtype.NamedType('ia5String', char.IA5String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) # hm, this should not be here!? XXX
+        )
+
+# certificate and CRL specific structures begin here
+                          
+class AlgorithmIdentifier(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('algorithm', univ.ObjectIdentifier()),
+        namedtype.OptionalNamedType('parameters', univ.Any())
+        )
+
+class Extension(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('extnID', univ.ObjectIdentifier()),
+        namedtype.DefaultedNamedType('critical', univ.Boolean('False')),
+        namedtype.NamedType('extnValue', univ.Any())
+        )
+
+class Extensions(univ.SequenceOf):
+    componentType = Extension()
+    sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+class SubjectPublicKeyInfo(univ.Sequence):
+     componentType = namedtype.NamedTypes(
+         namedtype.NamedType('algorithm', AlgorithmIdentifier()),
+         namedtype.NamedType('subjectPublicKey', univ.BitString())
+         )
+
+class UniqueIdentifier(univ.BitString): pass
+
+class Time(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('utcTime', useful.UTCTime()),
+        namedtype.NamedType('generalTime', useful.GeneralizedTime())
+        )
+    
+class Validity(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('notBefore', Time()),
+        namedtype.NamedType('notAfter', Time())
+        )
+
+class CertificateSerialNumber(univ.Integer): pass
+
+class Version(univ.Integer):
+    namedValues = namedval.NamedValues(
+        ('v1', 0), ('v2', 1), ('v3', 2)
+        )
+
+class TBSCertificate(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.DefaultedNamedType('version', Version('v1').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.NamedType('serialNumber', CertificateSerialNumber()),
+        namedtype.NamedType('signature', AlgorithmIdentifier()),
+        namedtype.NamedType('issuer', Name()),
+        namedtype.NamedType('validity', Validity()),
+        namedtype.NamedType('subject', Name()),
+        namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo()),
+        namedtype.OptionalNamedType('issuerUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.OptionalNamedType('subjectUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
+        namedtype.OptionalNamedType('extensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)))
+        )
+
+class Certificate(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('tbsCertificate', TBSCertificate()),
+        namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()),
+        namedtype.NamedType('signatureValue', univ.BitString())
+        )
+
+# CRL structures
+
+class RevokedCertificate(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('userCertificate', CertificateSerialNumber()),
+        namedtype.NamedType('revocationDate', Time()),
+        namedtype.OptionalNamedType('crlEntryExtensions', Extensions())
+    )
+ 
+class TBSCertList(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('version', Version()),
+        namedtype.NamedType('signature', AlgorithmIdentifier()),
+        namedtype.NamedType('issuer', Name()),
+        namedtype.NamedType('thisUpdate', Time()),
+        namedtype.OptionalNamedType('nextUpdate', Time()),
+        namedtype.OptionalNamedType('revokedCertificates', univ.SequenceOf(componentType=RevokedCertificate())),
+        namedtype.OptionalNamedType('crlExtensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
+    )
+
+class CertificateList(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('tbsCertList', TBSCertList()),
+        namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()),
+        namedtype.NamedType('signature', univ.BitString())
+    )
+
+# Algorithm OIDs and parameter structures
+
+pkcs_1 = univ.ObjectIdentifier('1.2.840.113549.1.1')
+rsaEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.1')
+md2WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.2')
+md5WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.4')
+sha1WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.5')
+id_dsa_with_sha1 = univ.ObjectIdentifier('1.2.840.10040.4.3')
+
+class Dss_Sig_Value(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('r', univ.Integer()),
+        namedtype.NamedType('s', univ.Integer())
+    )
+
+dhpublicnumber = univ.ObjectIdentifier('1.2.840.10046.2.1')
+
+class ValidationParms(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('seed', univ.BitString()),
+        namedtype.NamedType('pgenCounter', univ.Integer())
+    )
+
+class DomainParameters(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('p', univ.Integer()),
+        namedtype.NamedType('g', univ.Integer()),
+        namedtype.NamedType('q', univ.Integer()),
+        namedtype.NamedType('j', univ.Integer()),
+        namedtype.OptionalNamedType('validationParms', ValidationParms())
+    )
+
+id_dsa = univ.ObjectIdentifier('1.2.840.10040.4.1')
+
+class Dss_Parms(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('p', univ.Integer()),
+        namedtype.NamedType('q', univ.Integer()),
+        namedtype.NamedType('g', univ.Integer())
+    )
+
+# x400 address syntax starts here
+
+teletex_domain_defined_attributes = univ.Integer(6)
+
+class TeletexDomainDefinedAttribute(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('type', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_defined_attribute_type_length))),
+        namedtype.NamedType('value', char.TeletexString())
+    )
+
+class TeletexDomainDefinedAttributes(univ.SequenceOf):
+    componentType = TeletexDomainDefinedAttribute()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_domain_defined_attributes)
+
+terminal_type = univ.Integer(23)
+
+class TerminalType(univ.Integer):
+    subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueSizeConstraint(0, ub_integer_options)
+    namedValues = namedval.NamedValues(
+        ('telex', 3),
+        ('teletelex', 4),
+        ('g3-facsimile', 5),
+        ('g4-facsimile', 6),
+        ('ia5-terminal', 7),
+        ('videotex', 8)
+    )
+
+class PresentationAddress(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('pSelector', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.OptionalNamedType('sSelector', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.OptionalNamedType('tSelector', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
+        namedtype.OptionalNamedType('nAddresses', univ.SetOf(componentType=univ.OctetString()).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3), subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
+    )
+
+extended_network_address = univ.Integer(22)
+
+class E163_4_address(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('number', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_e163_4_number_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.OptionalNamedType('sub-address', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_e163_4_sub_address_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
+    )
+
+class ExtendedNetworkAddress(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('e163-4-address', E163_4_address()),
+        namedtype.NamedType('psap-address', PresentationAddress().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
+    )
+
+class PDSParameter(univ.Set):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('printable-string', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_parameter_length))),
+        namedtype.OptionalNamedType('teletex-string', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_parameter_length)))
+    )
+
+local_postal_attributes = univ.Integer(21)
+
+class LocalPostalAttributes(PDSParameter): pass
+
+class UniquePostalName(PDSParameter): pass
+
+unique_postal_name = univ.Integer(20)
+
+poste_restante_address = univ.Integer(19)
+
+class PosteRestanteAddress(PDSParameter): pass
+
+post_office_box_address = univ.Integer(18)
+
+class PostOfficeBoxAddress(PDSParameter): pass
+
+street_address = univ.Integer(17)
+
+class StreetAddress(PDSParameter): pass
+
+class UnformattedPostalAddress(univ.Set):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('printable-address', univ.SequenceOf(componentType=char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_parameter_length)).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_physical_address_lines)))),
+        namedtype.OptionalNamedType('teletex-string', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_unformatted_address_length)))
+    )
+
+physical_delivery_office_name = univ.Integer(10)
+
+class PhysicalDeliveryOfficeName(PDSParameter): pass
+
+physical_delivery_office_number = univ.Integer(11)
+
+class PhysicalDeliveryOfficeNumber(PDSParameter): pass
+
+extension_OR_address_components = univ.Integer(12)
+
+class ExtensionORAddressComponents(PDSParameter): pass
+
+physical_delivery_personal_name = univ.Integer(13)
+
+class PhysicalDeliveryPersonalName(PDSParameter): pass
+
+physical_delivery_organization_name = univ.Integer(14)
+
+class PhysicalDeliveryOrganizationName(PDSParameter): pass
+
+extension_physical_delivery_address_components = univ.Integer(15)
+
+class ExtensionPhysicalDeliveryAddressComponents(PDSParameter): pass
+
+unformatted_postal_address = univ.Integer(16)
+
+postal_code = univ.Integer(9)
+
+class PostalCode(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('numeric-code', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_postal_code_length))),
+        namedtype.NamedType('printable-code', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_postal_code_length)))
+    )
+
+class PhysicalDeliveryCountryName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('x121-dcc-code', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_numeric_length, ub_country_name_numeric_length))),
+        namedtype.NamedType('iso-3166-alpha2-code', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_alpha_length, ub_country_name_alpha_length)))
+    )
+
+class PDSName(char.PrintableString):
+    subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_pds_name_length)
+
+physical_delivery_country_name = univ.Integer(8)
+
+class TeletexOrganizationalUnitName(char.TeletexString):
+    subtypeSpec = char.TeletexString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_unit_name_length)
+
+pds_name = univ.Integer(7)
+
+teletex_organizational_unit_names = univ.Integer(5)
+
+class TeletexOrganizationalUnitNames(univ.SequenceOf):
+    componentType = TeletexOrganizationalUnitName()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_units)
+
+teletex_personal_name = univ.Integer(4)
+
+class TeletexPersonalName(univ.Set):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('surname', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_surname_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.OptionalNamedType('given-name', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_given_name_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.OptionalNamedType('initials', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_initials_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
+        namedtype.OptionalNamedType('generation-qualifier', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_generation_qualifier_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)))
+    )
+
+teletex_organization_name = univ.Integer(3)
+
+class TeletexOrganizationName(char.TeletexString):
+    subtypeSpec = char.TeletexString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organization_name_length)
+
+teletex_common_name = univ.Integer(2)
+
+class TeletexCommonName(char.TeletexString):
+    subtypeSpec = char.TeletexString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_common_name_length)
+
+class CommonName(char.PrintableString):
+    subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_common_name_length)
+
+common_name = univ.Integer(1)
+
+class ExtensionAttribute(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('extension-attribute-type', univ.Integer().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, ub_extension_attributes), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.NamedType('extension-attribute-value', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
+    )
+
+class ExtensionAttributes(univ.SetOf):
+    componentType = ExtensionAttribute()
+    subtypeSpec = univ.SetOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_extension_attributes)
+
+class BuiltInDomainDefinedAttribute(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('type', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_defined_attribute_type_length))),
+        namedtype.NamedType('value', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_defined_attribute_value_length)))
+    )
+
+class BuiltInDomainDefinedAttributes(univ.SequenceOf):
+    componentType = BuiltInDomainDefinedAttribute()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_domain_defined_attributes)
+
+class OrganizationalUnitName(char.PrintableString):
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_unit_name_length)
+
+class OrganizationalUnitNames(univ.SequenceOf):
+    componentType = OrganizationalUnitName()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_units)
+
+class PersonalName(univ.Set):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('surname', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_surname_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.OptionalNamedType('given-name', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_given_name_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.OptionalNamedType('initials', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_initials_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
+        namedtype.OptionalNamedType('generation-qualifier', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_generation_qualifier_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)))
+    )
+
+class NumericUserIdentifier(char.NumericString):
+    subtypeSpec = char.NumericString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_numeric_user_id_length)
+
+class OrganizationName(char.PrintableString):
+    subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organization_name_length)
+
+class PrivateDomainName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('numeric', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_name_length))),
+        namedtype.NamedType('printable', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_name_length)))
+    )
+
+class TerminalIdentifier(char.PrintableString):
+    subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_terminal_id_length)
+
+class X121Address(char.NumericString):
+    subtypeSpec = char.NumericString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_x121_address_length)
+
+class NetworkAddress(X121Address): pass
+
+class AdministrationDomainName(univ.Choice):
+    tagSet = univ.Choice.tagSet.tagExplicitly(
+        tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 2)
+        )
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('numeric', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, ub_domain_name_length))),
+        namedtype.NamedType('printable', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, ub_domain_name_length)))
+    )
+
+class CountryName(univ.Choice):
+    tagSet = univ.Choice.tagSet.tagExplicitly(
+        tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 1)
+        )
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('x121-dcc-code', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_numeric_length, ub_country_name_numeric_length))),
+        namedtype.NamedType('iso-3166-alpha2-code', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_alpha_length, ub_country_name_alpha_length)))
+    )
+
+class BuiltInStandardAttributes(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('country-name', CountryName()),
+        namedtype.OptionalNamedType('administration-domain-name', AdministrationDomainName()),
+        namedtype.OptionalNamedType('network-address', NetworkAddress().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.OptionalNamedType('terminal-identifier', TerminalIdentifier().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.OptionalNamedType('private-domain-name', PrivateDomainName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
+        namedtype.OptionalNamedType('organization-name', OrganizationName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))),
+        namedtype.OptionalNamedType('numeric-user-identifier', NumericUserIdentifier().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))),
+        namedtype.OptionalNamedType('personal-name', PersonalName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))),
+        namedtype.OptionalNamedType('organizational-unit-names', OrganizationalUnitNames().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6)))
+    )
+
+class ORAddress(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('built-in-standard-attributes', BuiltInStandardAttributes()),
+        namedtype.OptionalNamedType('built-in-domain-defined-attributes', BuiltInDomainDefinedAttributes()),
+        namedtype.OptionalNamedType('extension-attributes', ExtensionAttributes())
+    )
+
+#
+# PKIX1Implicit88
+#
+
+id_ce_invalidityDate = univ.ObjectIdentifier('2.5.29.24')
+
+class InvalidityDate(useful.GeneralizedTime): pass
+
+id_holdinstruction_none = univ.ObjectIdentifier('2.2.840.10040.2.1')
+id_holdinstruction_callissuer = univ.ObjectIdentifier('2.2.840.10040.2.2')
+id_holdinstruction_reject = univ.ObjectIdentifier('2.2.840.10040.2.3')
+
+holdInstruction = univ.ObjectIdentifier('2.2.840.10040.2')
+
+id_ce_holdInstructionCode = univ.ObjectIdentifier('2.5.29.23')
+
+class HoldInstructionCode(univ.ObjectIdentifier): pass
+
+id_ce_cRLReasons = univ.ObjectIdentifier('2.5.29.21')
+
+class CRLReason(univ.Enumerated):
+    namedValues = namedval.NamedValues(
+        ('unspecified', 0),
+        ('keyCompromise', 1),
+        ('cACompromise', 2),
+        ('affiliationChanged', 3),
+        ('superseded', 4),
+        ('cessationOfOperation', 5),
+        ('certificateHold', 6),
+        ('removeFromCRL', 8)
+    )
+
+id_ce_cRLNumber = univ.ObjectIdentifier('2.5.29.20')
+
+class CRLNumber(univ.Integer):
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(0, MAX)
+
+class BaseCRLNumber(CRLNumber): pass
+
+id_kp_serverAuth = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.1.1')
+id_kp_clientAuth = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.2')
+id_kp_codeSigning = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.3')
+id_kp_emailProtection = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.4')
+id_kp_ipsecEndSystem = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.5')
+id_kp_ipsecTunnel = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.6')
+id_kp_ipsecUser = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.7')
+id_kp_timeStamping = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.8')
+id_pe_authorityInfoAccess = univ.ObjectIdentifier('1.3.6.1.5.5.7.1.1')
+id_ce_extKeyUsage = univ.ObjectIdentifier('2.5.29.37')
+
+class KeyPurposeId(univ.ObjectIdentifier): pass
+
+class ExtKeyUsageSyntax(univ.SequenceOf):
+    componentType = KeyPurposeId()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+class ReasonFlags(univ.BitString):
+    namedValues = namedval.NamedValues(
+        ('unused', 0),
+        ('keyCompromise', 1),
+        ('cACompromise', 2),
+        ('affiliationChanged', 3),
+        ('superseded', 4),
+        ('cessationOfOperation', 5),
+        ('certificateHold', 6)
+    )
+
+
+class SkipCerts(univ.Integer):
+    subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueSizeConstraint(0, MAX)
+
+id_ce_policyConstraints = univ.ObjectIdentifier('2.5.29.36')
+
+class PolicyConstraints(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('requireExplicitPolicy', SkipCerts().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
+        namedtype.OptionalNamedType('inhibitPolicyMapping', SkipCerts().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
+    )
+
+id_ce_basicConstraints = univ.ObjectIdentifier('2.5.29.19')
+
+class BasicConstraints(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('cA', univ.Boolean(False)),
+        namedtype.OptionalNamedType('pathLenConstraint', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, MAX)))
+    )
+
+id_ce_subjectDirectoryAttributes = univ.ObjectIdentifier('2.5.29.9')
+
+class SubjectDirectoryAttributes(univ.SequenceOf):
+    componentType = Attribute()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+class EDIPartyName(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('nameAssigner', DirectoryString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.NamedType('partyName', DirectoryString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
+    )
+
+class AnotherName(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('type-id', univ.ObjectIdentifier()),
+        namedtype.NamedType('value', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
+    )
+
+class GeneralName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('otherName', AnotherName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.NamedType('rfc822Name', char.IA5String().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.NamedType('dNSName', char.IA5String().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
+        namedtype.NamedType('x400Address', ORAddress().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))),
+        namedtype.NamedType('directoryName', Name().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))),
+        namedtype.NamedType('ediPartyName', EDIPartyName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))),
+        namedtype.NamedType('uniformResourceIdentifier', char.IA5String().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6))),
+        namedtype.NamedType('iPAddress', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))),
+        namedtype.NamedType('registeredID', univ.ObjectIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8)))
+    )
+
+class GeneralNames(univ.SequenceOf):
+    componentType = GeneralName()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+class AccessDescription(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('accessMethod', univ.ObjectIdentifier()),
+        namedtype.NamedType('accessLocation', GeneralName())
+    )
+
+class AuthorityInfoAccessSyntax(univ.SequenceOf):
+    componentType = AccessDescription()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+id_ce_deltaCRLIndicator = univ.ObjectIdentifier('2.5.29.27')
+
+class DistributionPointName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('fullName', GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
+        namedtype.NamedType('nameRelativeToCRLIssuer', RelativeDistinguishedName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
+    )
+
+class DistributionPoint(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('distributionPoint', DistributionPointName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
+        namedtype.OptionalNamedType('reasons', ReasonFlags().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.OptionalNamedType('cRLIssuer', GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
+    )
+class BaseDistance(univ.Integer):
+    subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint(0, MAX)
+
+id_ce_cRLDistributionPoints = univ.ObjectIdentifier('2.5.29.31')
+
+class CRLDistPointsSyntax(univ.SequenceOf):
+    componentType = DistributionPoint
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+id_ce_issuingDistributionPoint = univ.ObjectIdentifier('2.5.29.28')
+
+class IssuingDistributionPoint(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('distributionPoint', DistributionPointName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
+        namedtype.NamedType('onlyContainsUserCerts', univ.Boolean(False).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.NamedType('onlyContainsCACerts', univ.Boolean(False).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))),
+        namedtype.OptionalNamedType('onlySomeReasons', ReasonFlags().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))),
+        namedtype.NamedType('indirectCRL', univ.Boolean(False).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)))
+    )
+
+class GeneralSubtree(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('base', GeneralName()),
+        namedtype.NamedType('minimum', BaseDistance(0).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
+        namedtype.OptionalNamedType('maximum', BaseDistance().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
+    )
+
+class GeneralSubtrees(univ.SequenceOf):
+    componentType = GeneralSubtree()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+id_ce_nameConstraints = univ.ObjectIdentifier('2.5.29.30')
+
+class NameConstraints(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('permittedSubtrees', GeneralSubtrees().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
+        namedtype.OptionalNamedType('excludedSubtrees', GeneralSubtrees().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
+    )
+
+
+class DisplayText(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('visibleString', char.VisibleString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 200))),
+        namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 200))),
+        namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 200)))
+    )
+
+class NoticeReference(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('organization', DisplayText()),
+        namedtype.NamedType('noticeNumbers', univ.SequenceOf(componentType=univ.Integer()))
+    )
+
+class UserNotice(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('noticeRef', NoticeReference()),
+        namedtype.OptionalNamedType('explicitText', DisplayText())
+    )
+
+class CPSuri(char.IA5String): pass
+
+class PolicyQualifierId(univ.ObjectIdentifier):
+    subtypeSpec = univ.ObjectIdentifier.subtypeSpec + constraint.SingleValueConstraint(id_qt_cps, id_qt_unotice)
+
+class CertPolicyId(univ.ObjectIdentifier): pass
+
+class PolicyQualifierInfo(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('policyQualifierId', PolicyQualifierId()),
+        namedtype.NamedType('qualifier', univ.Any())
+    )
+
+id_ce_certificatePolicies = univ.ObjectIdentifier('2.5.29.32')
+
+class PolicyInformation(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('policyIdentifier', CertPolicyId()),
+        namedtype.OptionalNamedType('policyQualifiers', univ.SequenceOf(componentType=PolicyQualifierInfo()).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX)))
+    )
+
+class CertificatePolicies(univ.SequenceOf):
+    componentType = PolicyInformation()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+id_ce_policyMappings = univ.ObjectIdentifier('2.5.29.33')
+
+class PolicyMapping(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('issuerDomainPolicy', CertPolicyId()),
+        namedtype.NamedType('subjectDomainPolicy', CertPolicyId())
+    )
+
+class PolicyMappings(univ.SequenceOf):
+    componentType = PolicyMapping()
+    subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX)
+
+id_ce_privateKeyUsagePeriod = univ.ObjectIdentifier('2.5.29.16')
+
+class PrivateKeyUsagePeriod(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('notBefore', useful.GeneralizedTime().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.OptionalNamedType('notAfter', useful.GeneralizedTime().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)))
+    )
+
+id_ce_keyUsage = univ.ObjectIdentifier('2.5.29.15')
+
+class KeyUsage(univ.BitString):
+    namedValues = namedval.NamedValues(
+        ('digitalSignature', 0),
+        ('nonRepudiation', 1),
+        ('keyEncipherment', 2),
+        ('dataEncipherment', 3),
+        ('keyAgreement', 4),
+        ('keyCertSign', 5),
+        ('cRLSign', 6),
+        ('encipherOnly', 7),
+        ('decipherOnly', 8)
+    )
+
+id_ce = univ.ObjectIdentifier('2.5.29')
+
+id_ce_authorityKeyIdentifier = univ.ObjectIdentifier('2.5.29.35')
+
+class KeyIdentifier(univ.OctetString): pass
+
+id_ce_subjectKeyIdentifier = univ.ObjectIdentifier('2.5.29.14')
+
+class SubjectKeyIdentifier(KeyIdentifier): pass
+
+class AuthorityKeyIdentifier(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.OptionalNamedType('keyIdentifier', KeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
+        namedtype.OptionalNamedType('authorityCertIssuer', GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+        namedtype.OptionalNamedType('authorityCertSerialNumber', CertificateSerialNumber().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2)))
+    )
+
+id_ce_certificateIssuer = univ.ObjectIdentifier('2.5.29.29')
+
+class CertificateIssuer(GeneralNames): pass
+
+id_ce_subjectAltName = univ.ObjectIdentifier('2.5.29.17')
+
+class SubjectAltName(GeneralNames): pass
+
+id_ce_issuerAltName = univ.ObjectIdentifier('2.5.29.18')
+
+class IssuerAltName(GeneralNames): pass
diff --git a/lib/rsa/rsa/LICENSE b/lib/rsa/rsa/LICENSE
new file mode 100644
index 0000000..770191b
--- /dev/null
+++ b/lib/rsa/rsa/LICENSE
@@ -0,0 +1,67 @@
+Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+
+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 (pasted below)
+
+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.
+
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 
+
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/lib/rsa/rsa/__init__.py b/lib/rsa/rsa/__init__.py
new file mode 100644
index 0000000..e06011e
--- /dev/null
+++ b/lib/rsa/rsa/__init__.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+"""RSA module
+
+Module for calculating large primes, and RSA encryption, decryption, signing
+and verification. Includes generating public and private keys.
+
+WARNING: this implementation does not use random padding, compression of the
+cleartext input to prevent repetitions, or other common security improvements.
+Use with care.
+
+If you want to have a more secure implementation, use the functions from the
+``rsa.pkcs1`` module.
+
+"""
+
+__author__ = "Sybren Stuvel, Barry Mead and Yesudeep Mangalapilly"
+__date__ = "2013-09-15"
+__version__ = '3.1.2'
+
+from rsa.key import PrivateKey, PublicKey
+from rsa.pkcs1 import sign, verify, \
+    VerificationError
+
+# Do doctest if we're run directly
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+
+__all__ = ["sign", "verify", 'PublicKey',
+    'PrivateKey', 'VerificationError']
diff --git a/lib/rsa/rsa/_compat.py b/lib/rsa/rsa/_compat.py
new file mode 100644
index 0000000..3c4eb81
--- /dev/null
+++ b/lib/rsa/rsa/_compat.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+"""Python compatibility wrappers."""
+
+
+from __future__ import absolute_import
+
+import sys
+from struct import pack
+
+try:
+    MAX_INT = sys.maxsize
+except AttributeError:
+    MAX_INT = sys.maxint
+
+MAX_INT64 = (1 << 63) - 1
+MAX_INT32 = (1 << 31) - 1
+MAX_INT16 = (1 << 15) - 1
+
+# Determine the word size of the processor.
+if MAX_INT == MAX_INT64:
+    # 64-bit processor.
+    MACHINE_WORD_SIZE = 64
+elif MAX_INT == MAX_INT32:
+    # 32-bit processor.
+    MACHINE_WORD_SIZE = 32
+else:
+    # Else we just assume 64-bit processor keeping up with modern times.
+    MACHINE_WORD_SIZE = 64
+
+
+try:
+    # < Python3
+    unicode_type = unicode
+    have_python3 = False
+except NameError:
+    # Python3.
+    unicode_type = str
+    have_python3 = True
+
+# Fake byte literals.
+if str is unicode_type:
+    def byte_literal(s):
+        return s.encode('latin1')
+else:
+    def byte_literal(s):
+        return s
+
+# ``long`` is no more. Do type detection using this instead.
+try:
+    integer_types = (int, long)
+except NameError:
+    integer_types = (int,)
+
+b = byte_literal
+
+try:
+    # Python 2.6 or higher.
+    bytes_type = bytes
+except NameError:
+    # Python 2.5
+    bytes_type = str
+
+
+# To avoid calling b() multiple times in tight loops.
+ZERO_BYTE = b('\x00')
+EMPTY_BYTE = b('')
+
+
+def is_bytes(obj):
+    """
+    Determines whether the given value is a byte string.
+
+    :param obj:
+        The value to test.
+    :returns:
+        ``True`` if ``value`` is a byte string; ``False`` otherwise.
+    """
+    return isinstance(obj, bytes_type)
+
+
+def is_integer(obj):
+    """
+    Determines whether the given value is an integer.
+
+    :param obj:
+        The value to test.
+    :returns:
+        ``True`` if ``value`` is an integer; ``False`` otherwise.
+    """
+    return isinstance(obj, integer_types)
+
+
+def byte(num):
+    """
+    Converts a number between 0 and 255 (both inclusive) to a base-256 (byte)
+    representation.
+
+    Use it as a replacement for ``chr`` where you are expecting a byte
+    because this will work on all current versions of Python::
+
+    :param num:
+        An unsigned integer between 0 and 255 (both inclusive).
+    :returns:
+        A single byte.
+    """
+    return pack("B", num)
+
+
+def get_word_alignment(num, force_arch=64,
+                       _machine_word_size=MACHINE_WORD_SIZE):
+    """
+    Returns alignment details for the given number based on the platform
+    Python is running on.
+
+    :param num:
+        Unsigned integral number.
+    :param force_arch:
+        If you don't want to use 64-bit unsigned chunks, set this to
+        anything other than 64. 32-bit chunks will be preferred then.
+        Default 64 will be used when on a 64-bit machine.
+    :param _machine_word_size:
+        (Internal) The machine word size used for alignment.
+    :returns:
+        4-tuple::
+
+            (word_bits, word_bytes,
+             max_uint, packing_format_type)
+    """
+    max_uint64 = 0xffffffffffffffff
+    max_uint32 = 0xffffffff
+    max_uint16 = 0xffff
+    max_uint8 = 0xff
+
+    if force_arch == 64 and _machine_word_size >= 64 and num > max_uint32:
+        # 64-bit unsigned integer.
+        return 64, 8, max_uint64, "Q"
+    elif num > max_uint16:
+        # 32-bit unsigned integer
+        return 32, 4, max_uint32, "L"
+    elif num > max_uint8:
+        # 16-bit unsigned integer.
+        return 16, 2, max_uint16, "H"
+    else:
+        # 8-bit unsigned integer.
+        return 8, 1, max_uint8, "B"
diff --git a/lib/rsa/rsa/asn1.py b/lib/rsa/rsa/asn1.py
new file mode 100644
index 0000000..706e6cf
--- /dev/null
+++ b/lib/rsa/rsa/asn1.py
@@ -0,0 +1,35 @@
+'''ASN.1 definitions.
+
+Not all ASN.1-handling code use these definitions, but when it does, they should be here.
+'''
+
+from pyasn1.type import univ, namedtype, tag
+
+class PubKeyHeader(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('oid', univ.ObjectIdentifier()),
+        namedtype.NamedType('parameters', univ.Null()),
+    )
+
+class OpenSSLPubKey(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('header', PubKeyHeader()),
+        
+        # This little hack (the implicit tag) allows us to get a Bit String as Octet String
+        namedtype.NamedType('key', univ.OctetString().subtype(
+                                          implicitTag=tag.Tag(tagClass=0, tagFormat=0, tagId=3))),
+    )
+
+
+class AsnPubKey(univ.Sequence):
+    '''ASN.1 contents of DER encoded public key:
+    
+    RSAPublicKey ::= SEQUENCE {
+         modulus           INTEGER,  -- n
+         publicExponent    INTEGER,  -- e
+    '''
+
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('modulus', univ.Integer()),
+        namedtype.NamedType('publicExponent', univ.Integer()),
+    )
diff --git a/lib/rsa/rsa/common.py b/lib/rsa/rsa/common.py
new file mode 100644
index 0000000..39feb8c
--- /dev/null
+++ b/lib/rsa/rsa/common.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+'''Common functionality shared by several modules.'''
+
+
+def bit_size(num):
+    '''
+    Number of bits needed to represent a integer excluding any prefix
+    0 bits.
+
+    As per definition from http://wiki.python.org/moin/BitManipulation and
+    to match the behavior of the Python 3 API.
+
+    Usage::
+    
+        >>> bit_size(1023)
+        10
+        >>> bit_size(1024)
+        11
+        >>> bit_size(1025)
+        11
+
+    :param num:
+        Integer value. If num is 0, returns 0. Only the absolute value of the
+        number is considered. Therefore, signed integers will be abs(num)
+        before the number's bit length is determined.
+    :returns:
+        Returns the number of bits in the integer.
+    '''
+    if num == 0:
+        return 0
+    if num < 0:
+        num = -num
+
+    # Make sure this is an int and not a float.
+    num & 1
+
+    hex_num = "%x" % num
+    return ((len(hex_num) - 1) * 4) + {
+        '0':0, '1':1, '2':2, '3':2,
+        '4':3, '5':3, '6':3, '7':3,
+        '8':4, '9':4, 'a':4, 'b':4,
+        'c':4, 'd':4, 'e':4, 'f':4,
+     }[hex_num[0]]
+
+
+def _bit_size(number):
+    '''
+    Returns the number of bits required to hold a specific long number.
+    '''
+    if number < 0:
+        raise ValueError('Only nonnegative numbers possible: %s' % number)
+
+    if number == 0:
+        return 0
+    
+    # This works, even with very large numbers. When using math.log(number, 2),
+    # you'll get rounding errors and it'll fail.
+    bits = 0
+    while number:
+        bits += 1
+        number >>= 1
+
+    return bits
+
+
+def byte_size(number):
+    '''
+    Returns the number of bytes required to hold a specific long number.
+    
+    The number of bytes is rounded up.
+
+    Usage::
+
+        >>> byte_size(1 << 1023)
+        128
+        >>> byte_size((1 << 1024) - 1)
+        128
+        >>> byte_size(1 << 1024)
+        129
+
+    :param number:
+        An unsigned integer
+    :returns:
+        The number of bytes required to hold a specific long number.
+    '''
+    quanta, mod = divmod(bit_size(number), 8)
+    if mod or number == 0:
+        quanta += 1
+    return quanta
+    #return int(math.ceil(bit_size(number) / 8.0))
+
+
+def extended_gcd(a, b):
+    '''Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb
+    '''
+    # r = gcd(a,b) i = multiplicitive inverse of a mod b
+    #      or      j = multiplicitive inverse of b mod a
+    # Neg return values for i or j are made positive mod b or a respectively
+    # Iterateive Version is faster and uses much less stack space
+    x = 0
+    y = 1
+    lx = 1
+    ly = 0
+    oa = a                             #Remember original a/b to remove 
+    ob = b                             #negative values from return results
+    while b != 0:
+        q = a // b
+        (a, b)  = (b, a % b)
+        (x, lx) = ((lx - (q * x)),x)
+        (y, ly) = ((ly - (q * y)),y)
+    if (lx < 0): lx += ob              #If neg wrap modulo orignal b
+    if (ly < 0): ly += oa              #If neg wrap modulo orignal a
+    return (a, lx, ly)                 #Return only positive values
+
+
+def inverse(x, n):
+    '''Returns x^-1 (mod n)
+
+    >>> inverse(7, 4)
+    3
+    >>> (inverse(143, 4) * 143) % 4
+    1
+    '''
+
+    (divider, inv, _) = extended_gcd(x, n)
+
+    if divider != 1:
+        raise ValueError("x (%d) and n (%d) are not relatively prime" % (x, n))
+
+    return inv
+
+
+def crt(a_values, modulo_values):
+    '''Chinese Remainder Theorem.
+
+    Calculates x such that x = a[i] (mod m[i]) for each i.
+
+    :param a_values: the a-values of the above equation
+    :param modulo_values: the m-values of the above equation
+    :returns: x such that x = a[i] (mod m[i]) for each i
+    
+
+    >>> crt([2, 3], [3, 5])
+    8
+
+    >>> crt([2, 3, 2], [3, 5, 7])
+    23
+
+    >>> crt([2, 3, 0], [7, 11, 15])
+    135
+    '''
+
+    m = 1
+    x = 0 
+
+    for modulo in modulo_values:
+        m *= modulo
+
+    for (m_i, a_i) in zip(modulo_values, a_values):
+        M_i = m // m_i
+        inv = inverse(M_i, m_i)
+
+        x = (x + a_i * M_i * inv) % m
+
+    return x
+
+if __name__ == '__main__':
+    import doctest
+    doctest.testmod()
+
diff --git a/lib/rsa/rsa/core.py b/lib/rsa/rsa/core.py
new file mode 100644
index 0000000..90dfee8
--- /dev/null
+++ b/lib/rsa/rsa/core.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+'''Core mathematical operations.
+
+This is the actual core RSA implementation, which is only defined
+mathematically on integers.
+'''
+
+
+from rsa._compat import is_integer
+
+def assert_int(var, name):
+
+    if is_integer(var):
+        return
+
+    raise TypeError('%s should be an integer, not %s' % (name, var.__class__))
+
+def encrypt_int(message, ekey, n):
+    '''Encrypts a message using encryption key 'ekey', working modulo n'''
+
+    assert_int(message, 'message')
+    assert_int(ekey, 'ekey')
+    assert_int(n, 'n')
+
+    if message < 0:
+        raise ValueError('Only non-negative numbers are supported')
+         
+    if message > n:
+        raise OverflowError("The message %i is too long for n=%i" % (message, n))
+
+    return pow(message, ekey, n)
+
+def decrypt_int(cyphertext, dkey, n):
+    '''Decrypts a cypher text using the decryption key 'dkey', working
+    modulo n'''
+
+    assert_int(cyphertext, 'cyphertext')
+    assert_int(dkey, 'dkey')
+    assert_int(n, 'n')
+
+    message = pow(cyphertext, dkey, n)
+    return message
+
diff --git a/lib/rsa/rsa/key.py b/lib/rsa/rsa/key.py
new file mode 100644
index 0000000..62af903
--- /dev/null
+++ b/lib/rsa/rsa/key.py
@@ -0,0 +1,449 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+'''RSA key generation code.
+
+Create new keys with the newkeys() function. It will give you a PublicKey and a
+PrivateKey object.
+
+Loading and saving keys requires the pyasn1 module. This module is imported as
+late as possible, such that other functionality will remain working in absence
+of pyasn1.
+
+'''
+
+import logging
+from rsa._compat import b, bytes_type
+
+import rsa.pem
+import rsa.common
+
+log = logging.getLogger(__name__)
+
+
+
+class AbstractKey(object):
+    '''Abstract superclass for private and public keys.'''
+
+    @classmethod
+    def load_pkcs1(cls, keyfile, format='PEM'):
+        r'''Loads a key in PKCS#1 DER or PEM format.
+
+        :param keyfile: contents of a DER- or PEM-encoded file that contains
+            the public key.
+        :param format: the format of the file to load; 'PEM' or 'DER'
+
+        :return: a PublicKey object
+
+        '''
+
+        methods = {
+            'PEM': cls._load_pkcs1_pem,
+            'DER': cls._load_pkcs1_der,
+        }
+
+        if format not in methods:
+            formats = ', '.join(sorted(methods.keys()))
+            raise ValueError('Unsupported format: %r, try one of %s' % (format,
+                formats))
+
+        method = methods[format]
+        return method(keyfile)
+
+    def save_pkcs1(self, format='PEM'):
+        '''Saves the public key in PKCS#1 DER or PEM format.
+
+        :param format: the format to save; 'PEM' or 'DER'
+        :returns: the DER- or PEM-encoded public key.
+
+        '''
+
+        methods = {
+            'PEM': self._save_pkcs1_pem,
+            'DER': self._save_pkcs1_der,
+        }
+
+        if format not in methods:
+            formats = ', '.join(sorted(methods.keys()))
+            raise ValueError('Unsupported format: %r, try one of %s' % (format,
+                formats))
+
+        method = methods[format]
+        return method()
+
+class PublicKey(AbstractKey):
+    '''Represents a public RSA key.
+
+    This key is also known as the 'encryption key'. It contains the 'n' and 'e'
+    values.
+
+    Supports attributes as well as dictionary-like access. Attribute accesss is
+    faster, though.
+
+    >>> PublicKey(5, 3)
+    PublicKey(5, 3)
+
+    >>> key = PublicKey(5, 3)
+    >>> key.n
+    5
+    >>> key['n']
+    5
+    >>> key.e
+    3
+    >>> key['e']
+    3
+
+    '''
+
+    __slots__ = ('n', 'e')
+
+    def __init__(self, n, e):
+        self.n = n
+        self.e = e
+
+    def __getitem__(self, key):
+        return getattr(self, key)
+
+    def __repr__(self):
+        return 'PublicKey(%i, %i)' % (self.n, self.e)
+
+    def __eq__(self, other):
+        if other is None:
+            return False
+
+        if not isinstance(other, PublicKey):
+            return False
+
+        return self.n == other.n and self.e == other.e
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    @classmethod
+    def _load_pkcs1_der(cls, keyfile):
+        r'''Loads a key in PKCS#1 DER format.
+
+        @param keyfile: contents of a DER-encoded file that contains the public
+            key.
+        @return: a PublicKey object
+
+        First let's construct a DER encoded key:
+
+        >>> import base64
+        >>> b64der = 'MAwCBQCNGmYtAgMBAAE='
+        >>> der = base64.decodestring(b64der)
+
+        This loads the file:
+
+        >>> PublicKey._load_pkcs1_der(der)
+        PublicKey(2367317549, 65537)
+
+        '''
+
+        from pyasn1.codec.der import decoder
+        from rsa.asn1 import AsnPubKey
+        
+        (priv, _) = decoder.decode(keyfile, asn1Spec=AsnPubKey())
+        return cls(n=int(priv['modulus']), e=int(priv['publicExponent']))
+
+    def _save_pkcs1_der(self):
+        '''Saves the public key in PKCS#1 DER format.
+
+        @returns: the DER-encoded public key.
+        '''
+
+        from pyasn1.codec.der import encoder
+        from rsa.asn1 import AsnPubKey
+
+        # Create the ASN object
+        asn_key = AsnPubKey()
+        asn_key.setComponentByName('modulus', self.n)
+        asn_key.setComponentByName('publicExponent', self.e)
+
+        return encoder.encode(asn_key)
+
+    @classmethod
+    def _load_pkcs1_pem(cls, keyfile):
+        '''Loads a PKCS#1 PEM-encoded public key file.
+
+        The contents of the file before the "-----BEGIN RSA PUBLIC KEY-----" and
+        after the "-----END RSA PUBLIC KEY-----" lines is ignored.
+
+        @param keyfile: contents of a PEM-encoded file that contains the public
+            key.
+        @return: a PublicKey object
+        '''
+
+        der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY')
+        return cls._load_pkcs1_der(der)
+
+    def _save_pkcs1_pem(self):
+        '''Saves a PKCS#1 PEM-encoded public key file.
+
+        @return: contents of a PEM-encoded file that contains the public key.
+        '''
+
+        der = self._save_pkcs1_der()
+        return rsa.pem.save_pem(der, 'RSA PUBLIC KEY')
+
+    @classmethod
+    def load_pkcs1_openssl_pem(cls, keyfile):
+        '''Loads a PKCS#1.5 PEM-encoded public key file from OpenSSL.
+        
+        These files can be recognised in that they start with BEGIN PUBLIC KEY
+        rather than BEGIN RSA PUBLIC KEY.
+        
+        The contents of the file before the "-----BEGIN PUBLIC KEY-----" and
+        after the "-----END PUBLIC KEY-----" lines is ignored.
+
+        @param keyfile: contents of a PEM-encoded file that contains the public
+            key, from OpenSSL.
+        @return: a PublicKey object
+        '''
+
+        der = rsa.pem.load_pem(keyfile, 'PUBLIC KEY')
+        return cls.load_pkcs1_openssl_der(der)
+
+    @classmethod
+    def load_pkcs1_openssl_der(cls, keyfile):
+        '''Loads a PKCS#1 DER-encoded public key file from OpenSSL.
+
+        @param keyfile: contents of a DER-encoded file that contains the public
+            key, from OpenSSL.
+        @return: a PublicKey object
+        '''
+    
+        from rsa.asn1 import OpenSSLPubKey
+        from pyasn1.codec.der import decoder
+        from pyasn1.type import univ
+        
+        (keyinfo, _) = decoder.decode(keyfile, asn1Spec=OpenSSLPubKey())
+        
+        if keyinfo['header']['oid'] != univ.ObjectIdentifier('1.2.840.113549.1.1.1'):
+            raise TypeError("This is not a DER-encoded OpenSSL-compatible public key")
+                
+        return cls._load_pkcs1_der(keyinfo['key'][1:])
+        
+        
+
+
+class PrivateKey(AbstractKey):
+    '''Represents a private RSA key.
+
+    This key is also known as the 'decryption key'. It contains the 'n', 'e',
+    'd', 'p', 'q' and other values.
+
+    Supports attributes as well as dictionary-like access. Attribute accesss is
+    faster, though.
+
+    >>> PrivateKey(3247, 65537, 833, 191, 17)
+    PrivateKey(3247, 65537, 833, 191, 17)
+
+    exp1, exp2 and coef don't have to be given, they will be calculated:
+
+    >>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
+    >>> pk.exp1
+    55063
+    >>> pk.exp2
+    10095
+    >>> pk.coef
+    50797
+
+    If you give exp1, exp2 or coef, they will be used as-is:
+
+    >>> pk = PrivateKey(1, 2, 3, 4, 5, 6, 7, 8)
+    >>> pk.exp1
+    6
+    >>> pk.exp2
+    7
+    >>> pk.coef
+    8
+
+    '''
+
+    __slots__ = ('n', 'e', 'd', 'p', 'q', 'exp1', 'exp2', 'coef')
+
+    def __init__(self, n, e, d, p, q, exp1=None, exp2=None, coef=None):
+        self.n = n
+        self.e = e
+        self.d = d
+        self.p = p
+        self.q = q
+
+        # Calculate the other values if they aren't supplied
+        if exp1 is None:
+            self.exp1 = int(d % (p - 1))
+        else:
+            self.exp1 = exp1
+
+        if exp1 is None:
+            self.exp2 = int(d % (q - 1))
+        else:
+            self.exp2 = exp2
+
+        if coef is None:
+            self.coef = rsa.common.inverse(q, p)
+        else:
+            self.coef = coef
+
+    def __getitem__(self, key):
+        return getattr(self, key)
+
+    def __repr__(self):
+        return 'PrivateKey(%(n)i, %(e)i, %(d)i, %(p)i, %(q)i)' % self
+
+    def __eq__(self, other):
+        if other is None:
+            return False
+
+        if not isinstance(other, PrivateKey):
+            return False
+
+        return (self.n == other.n and
+            self.e == other.e and
+            self.d == other.d and
+            self.p == other.p and
+            self.q == other.q and
+            self.exp1 == other.exp1 and
+            self.exp2 == other.exp2 and
+            self.coef == other.coef)
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    @classmethod
+    def _load_pkcs1_der(cls, keyfile):
+        r'''Loads a key in PKCS#1 DER format.
+
+        @param keyfile: contents of a DER-encoded file that contains the private
+            key.
+        @return: a PrivateKey object
+
+        First let's construct a DER encoded key:
+
+        >>> import base64
+        >>> b64der = 'MC4CAQACBQDeKYlRAgMBAAECBQDHn4npAgMA/icCAwDfxwIDANcXAgInbwIDAMZt'
+        >>> der = base64.decodestring(b64der)
+
+        This loads the file:
+
+        >>> PrivateKey._load_pkcs1_der(der)
+        PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
+
+        '''
+
+        from pyasn1.codec.der import decoder
+        (priv, _) = decoder.decode(keyfile)
+
+        # ASN.1 contents of DER encoded private key:
+        #
+        # RSAPrivateKey ::= SEQUENCE {
+        #     version           Version, 
+        #     modulus           INTEGER,  -- n
+        #     publicExponent    INTEGER,  -- e
+        #     privateExponent   INTEGER,  -- d
+        #     prime1            INTEGER,  -- p
+        #     prime2            INTEGER,  -- q
+        #     exponent1         INTEGER,  -- d mod (p-1)
+        #     exponent2         INTEGER,  -- d mod (q-1) 
+        #     coefficient       INTEGER,  -- (inverse of q) mod p
+        #     otherPrimeInfos   OtherPrimeInfos OPTIONAL 
+        # }
+
+        if priv[0] != 0:
+            raise ValueError('Unable to read this file, version %s != 0' % priv[0])
+
+        as_ints = tuple(int(x) for x in priv[1:9])
+        return cls(*as_ints)
+
+    def _save_pkcs1_der(self):
+        '''Saves the private key in PKCS#1 DER format.
+
+        @returns: the DER-encoded private key.
+        '''
+
+        from pyasn1.type import univ, namedtype
+        from pyasn1.codec.der import encoder
+
+        class AsnPrivKey(univ.Sequence):
+            componentType = namedtype.NamedTypes(
+                namedtype.NamedType('version', univ.Integer()),
+                namedtype.NamedType('modulus', univ.Integer()),
+                namedtype.NamedType('publicExponent', univ.Integer()),
+                namedtype.NamedType('privateExponent', univ.Integer()),
+                namedtype.NamedType('prime1', univ.Integer()),
+                namedtype.NamedType('prime2', univ.Integer()),
+                namedtype.NamedType('exponent1', univ.Integer()),
+                namedtype.NamedType('exponent2', univ.Integer()),
+                namedtype.NamedType('coefficient', univ.Integer()),
+            )
+
+        # Create the ASN object
+        asn_key = AsnPrivKey()
+        asn_key.setComponentByName('version', 0)
+        asn_key.setComponentByName('modulus', self.n)
+        asn_key.setComponentByName('publicExponent', self.e)
+        asn_key.setComponentByName('privateExponent', self.d)
+        asn_key.setComponentByName('prime1', self.p)
+        asn_key.setComponentByName('prime2', self.q)
+        asn_key.setComponentByName('exponent1', self.exp1)
+        asn_key.setComponentByName('exponent2', self.exp2)
+        asn_key.setComponentByName('coefficient', self.coef)
+
+        return encoder.encode(asn_key)
+
+    @classmethod
+    def _load_pkcs1_pem(cls, keyfile):
+        '''Loads a PKCS#1 PEM-encoded private key file.
+
+        The contents of the file before the "-----BEGIN RSA PRIVATE KEY-----" and
+        after the "-----END RSA PRIVATE KEY-----" lines is ignored.
+
+        @param keyfile: contents of a PEM-encoded file that contains the private
+            key.
+        @return: a PrivateKey object
+        '''
+
+        der = rsa.pem.load_pem(keyfile, b('RSA PRIVATE KEY'))
+        return cls._load_pkcs1_der(der)
+
+    def _save_pkcs1_pem(self):
+        '''Saves a PKCS#1 PEM-encoded private key file.
+
+        @return: contents of a PEM-encoded file that contains the private key.
+        '''
+
+        der = self._save_pkcs1_der()
+        return rsa.pem.save_pem(der, b('RSA PRIVATE KEY'))
+
+
+__all__ = ['PublicKey', 'PrivateKey']
+
+if __name__ == '__main__':
+    import doctest
+    
+    try:
+        for count in range(100):
+            (failures, tests) = doctest.testmod()
+            if failures:
+                break
+
+            if (count and count % 10 == 0) or count == 1:
+                print('%i times' % count)
+    except KeyboardInterrupt:
+        print('Aborted')
+    else:
+        print('Doctests done')
diff --git a/lib/rsa/rsa/pem.py b/lib/rsa/rsa/pem.py
new file mode 100644
index 0000000..b1c3a0e
--- /dev/null
+++ b/lib/rsa/rsa/pem.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+'''Functions that load and write PEM-encoded files.'''
+
+import base64
+from rsa._compat import b, is_bytes
+
+def _markers(pem_marker):
+    '''
+    Returns the start and end PEM markers
+    '''
+
+    if is_bytes(pem_marker):
+        pem_marker = pem_marker.decode('utf-8')
+
+    return (b('-----BEGIN %s-----' % pem_marker),
+            b('-----END %s-----' % pem_marker))
+
+def load_pem(contents, pem_marker):
+    '''Loads a PEM file.
+
+    @param contents: the contents of the file to interpret
+    @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY'
+        when your file has '-----BEGIN RSA PRIVATE KEY-----' and
+        '-----END RSA PRIVATE KEY-----' markers.
+
+    @return the base64-decoded content between the start and end markers.
+
+    @raise ValueError: when the content is invalid, for example when the start
+        marker cannot be found.
+
+    '''
+
+    (pem_start, pem_end) = _markers(pem_marker)
+
+    pem_lines = []
+    in_pem_part = False
+
+    for line in contents.splitlines():
+        line = line.strip()
+
+        # Skip empty lines
+        if not line:
+            continue
+
+        # Handle start marker
+        if line == pem_start:
+            if in_pem_part:
+                raise ValueError('Seen start marker "%s" twice' % pem_start)
+
+            in_pem_part = True
+            continue
+
+        # Skip stuff before first marker
+        if not in_pem_part:
+            continue
+
+        # Handle end marker
+        if in_pem_part and line == pem_end:
+            in_pem_part = False
+            break
+
+        # Load fields
+        if b(':') in line:
+            continue
+
+        pem_lines.append(line)
+
+    # Do some sanity checks
+    if not pem_lines:
+        raise ValueError('No PEM start marker "%s" found' % pem_start)
+
+    if in_pem_part:
+        raise ValueError('No PEM end marker "%s" found' % pem_end)
+
+    # Base64-decode the contents
+    pem = b('').join(pem_lines)
+    return base64.decodestring(pem)
+
+
+def save_pem(contents, pem_marker):
+    '''Saves a PEM file.
+
+    @param contents: the contents to encode in PEM format
+    @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY'
+        when your file has '-----BEGIN RSA PRIVATE KEY-----' and
+        '-----END RSA PRIVATE KEY-----' markers.
+
+    @return the base64-encoded content between the start and end markers.
+
+    '''
+
+    (pem_start, pem_end) = _markers(pem_marker)
+
+    b64 = base64.encodestring(contents).replace(b('\n'), b(''))
+    pem_lines = [pem_start]
+    
+    for block_start in range(0, len(b64), 64):
+        block = b64[block_start:block_start + 64]
+        pem_lines.append(block)
+
+    pem_lines.append(pem_end)
+    pem_lines.append(b(''))
+
+    return b('\n').join(pem_lines)
+    
diff --git a/lib/rsa/rsa/pkcs1.py b/lib/rsa/rsa/pkcs1.py
new file mode 100644
index 0000000..355e0b5
--- /dev/null
+++ b/lib/rsa/rsa/pkcs1.py
@@ -0,0 +1,245 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+'''Functions for PKCS#1 version 1.5 encryption and signing
+
+This module implements certain functionality from PKCS#1 version 1.5. For a
+very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes
+
+At least 8 bytes of random padding is used when encrypting a message. This makes
+these methods much more secure than the ones in the ``rsa`` module.
+
+WARNING: this module leaks information when decryption or verification fails.
+The exceptions that are raised contain the Python traceback information, which
+can be used to deduce where in the process the failure occurred. DO NOT PASS
+SUCH INFORMATION to your users.
+'''
+
+import hashlib
+import os
+
+from rsa._compat import b
+from rsa import common, transform, core, varblock
+
+# ASN.1 codes that describe the hash algorithm used.
+HASH_ASN1 = {
+    'MD5': b('\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
+    'SHA-1': b('\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
+    'SHA-256': b('\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'),
+    'SHA-384': b('\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'),
+    'SHA-512': b('\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'),
+}
+
+HASH_METHODS = {
+    'MD5': hashlib.md5,
+    'SHA-1': hashlib.sha1,
+    'SHA-256': hashlib.sha256,
+    'SHA-384': hashlib.sha384,
+    'SHA-512': hashlib.sha512,
+}
+
+class CryptoError(Exception):
+    '''Base class for all exceptions in this module.'''
+
+
+class VerificationError(CryptoError):
+    '''Raised when verification fails.'''
+     
+
+def _pad_for_signing(message, target_length):
+    r'''Pads the message for signing, returning the padded message.
+    
+    The padding is always a repetition of FF bytes.
+    
+    :return: 00 01 PADDING 00 MESSAGE
+    
+    >>> block = _pad_for_signing('hello', 16)
+    >>> len(block)
+    16
+    >>> block[0:2]
+    '\x00\x01'
+    >>> block[-6:]
+    '\x00hello'
+    >>> block[2:-6]
+    '\xff\xff\xff\xff\xff\xff\xff\xff'
+    
+    '''
+
+    max_msglength = target_length - 11
+    msglength = len(message)
+    
+    if msglength > max_msglength:
+        raise OverflowError('%i bytes needed for message, but there is only'
+            ' space for %i' % (msglength, max_msglength))
+    
+    padding_length = target_length - msglength - 3
+    
+    return b('').join([b('\x00\x01'),
+                    padding_length * b('\xff'),
+                    b('\x00'),
+                    message])
+    
+    
+def sign(message, priv_key, hash):
+    '''Signs the message with the private key.
+
+    Hashes the message, then signs the hash with the given key. This is known
+    as a "detached signature", because the message itself isn't altered.
+    
+    :param message: the message to sign. Can be an 8-bit string or a file-like
+        object. If ``message`` has a ``read()`` method, it is assumed to be a
+        file-like object.
+    :param priv_key: the :py:class:`rsa.PrivateKey` to sign with
+    :param hash: the hash method used on the message. Use 'MD5', 'SHA-1',
+        'SHA-256', 'SHA-384' or 'SHA-512'.
+    :return: a message signature block.
+    :raise OverflowError: if the private key is too small to contain the
+        requested hash.
+
+    '''
+
+    # Get the ASN1 code for this hash method
+    if hash not in HASH_ASN1:
+        raise ValueError('Invalid hash method: %s' % hash)
+    asn1code = HASH_ASN1[hash]
+    
+    # Calculate the hash
+    hash = _hash(message, hash)
+
+    # Encrypt the hash with the private key
+    cleartext = asn1code + hash
+    keylength = common.byte_size(priv_key.n)
+    padded = _pad_for_signing(cleartext, keylength)
+    
+    payload = transform.bytes2int(padded)
+    encrypted = core.encrypt_int(payload, priv_key.d, priv_key.n)
+    block = transform.int2bytes(encrypted, keylength)
+    
+    return block
+
+def verify(message, signature, pub_key):
+    '''Verifies that the signature matches the message.
+    
+    The hash method is detected automatically from the signature.
+    
+    :param message: the signed message. Can be an 8-bit string or a file-like
+        object. If ``message`` has a ``read()`` method, it is assumed to be a
+        file-like object.
+    :param signature: the signature block, as created with :py:func:`rsa.sign`.
+    :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message.
+    :raise VerificationError: when the signature doesn't match the message.
+
+    .. warning::
+
+        Never display the stack trace of a
+        :py:class:`rsa.pkcs1.VerificationError` exception. It shows where in
+        the code the exception occurred, and thus leaks information about the
+        key. It's only a tiny bit of information, but every bit makes cracking
+        the keys easier.
+
+    '''
+    
+    blocksize = common.byte_size(pub_key.n)
+    encrypted = transform.bytes2int(signature)
+    decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n)
+    clearsig = transform.int2bytes(decrypted, blocksize)
+
+    # If we can't find the signature  marker, verification failed.
+    if clearsig[0:2] != b('\x00\x01'):
+        raise VerificationError('Verification failed')
+    
+    # Find the 00 separator between the padding and the payload
+    try:
+        sep_idx = clearsig.index(b('\x00'), 2)
+    except ValueError:
+        raise VerificationError('Verification failed')
+    
+    # Get the hash and the hash method
+    (method_name, signature_hash) = _find_method_hash(clearsig[sep_idx+1:])
+    message_hash = _hash(message, method_name)
+
+    # Compare the real hash to the hash in the signature
+    if message_hash != signature_hash:
+        raise VerificationError('Verification failed')
+
+    return True
+
+def _hash(message, method_name):
+    '''Returns the message digest.
+    
+    :param message: the signed message. Can be an 8-bit string or a file-like
+        object. If ``message`` has a ``read()`` method, it is assumed to be a
+        file-like object.
+    :param method_name: the hash method, must be a key of
+        :py:const:`HASH_METHODS`.
+    
+    '''
+
+    if method_name not in HASH_METHODS:
+        raise ValueError('Invalid hash method: %s' % method_name)
+    
+    method = HASH_METHODS[method_name]
+    hasher = method()
+
+    if hasattr(message, 'read') and hasattr(message.read, '__call__'):
+        # read as 1K blocks
+        for block in varblock.yield_fixedblocks(message, 1024):
+            hasher.update(block)
+    else:
+        # hash the message object itself.
+        hasher.update(message)
+
+    return hasher.digest()
+
+
+def _find_method_hash(method_hash):
+    '''Finds the hash method and the hash itself.
+    
+    :param method_hash: ASN1 code for the hash method concatenated with the
+        hash itself.
+    
+    :return: tuple (method, hash) where ``method`` is the used hash method, and
+        ``hash`` is the hash itself.
+    
+    :raise VerificationFailed: when the hash method cannot be found
+
+    '''
+
+    for (hashname, asn1code) in HASH_ASN1.items():
+        if not method_hash.startswith(asn1code):
+            continue
+        
+        return (hashname, method_hash[len(asn1code):])
+    
+    raise VerificationError('Verification failed')
+
+
+__all__ = ['sign', 'verify',
+           'VerificationError', 'CryptoError']
+
+if __name__ == '__main__':
+    print('Running doctests 1000x or until failure')
+    import doctest
+    
+    for count in range(1000):
+        (failures, tests) = doctest.testmod()
+        if failures:
+            break
+        
+        if count and count % 100 == 0:
+            print('%i times' % count)
+    
+    print('Doctests done')
diff --git a/lib/rsa/rsa/transform.py b/lib/rsa/rsa/transform.py
new file mode 100644
index 0000000..c740b2d
--- /dev/null
+++ b/lib/rsa/rsa/transform.py
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+'''Data transformation functions.
+
+From bytes to a number, number to bytes, etc.
+'''
+
+from __future__ import absolute_import
+
+try:
+    # We'll use psyco if available on 32-bit architectures to speed up code.
+    # Using psyco (if available) cuts down the execution time on Python 2.5
+    # at least by half.
+    import psyco
+    psyco.full()
+except ImportError:
+    pass
+
+import binascii
+from struct import pack
+from rsa import common
+from rsa._compat import is_integer, b, byte, get_word_alignment, ZERO_BYTE, EMPTY_BYTE
+
+
+def bytes2int(raw_bytes):
+    r'''Converts a list of bytes or an 8-bit string to an integer.
+
+    When using unicode strings, encode it to some encoding like UTF8 first.
+
+    >>> (((128 * 256) + 64) * 256) + 15
+    8405007
+    >>> bytes2int('\x80@\x0f')
+    8405007
+
+    '''
+
+    return int(binascii.hexlify(raw_bytes), 16)
+
+
+def _int2bytes(number, block_size=None):
+    r'''Converts a number to a string of bytes.
+
+    Usage::
+
+        >>> _int2bytes(123456789)
+        '\x07[\xcd\x15'
+        >>> bytes2int(_int2bytes(123456789))
+        123456789
+
+        >>> _int2bytes(123456789, 6)
+        '\x00\x00\x07[\xcd\x15'
+        >>> bytes2int(_int2bytes(123456789, 128))
+        123456789
+
+        >>> _int2bytes(123456789, 3)
+        Traceback (most recent call last):
+        ...
+        OverflowError: Needed 4 bytes for number, but block size is 3
+
+    @param number: the number to convert
+    @param block_size: the number of bytes to output. If the number encoded to
+        bytes is less than this, the block will be zero-padded. When not given,
+        the returned block is not padded.
+
+    @throws OverflowError when block_size is given and the number takes up more
+        bytes than fit into the block.
+    '''
+    # Type checking
+    if not is_integer(number):
+        raise TypeError("You must pass an integer for 'number', not %s" %
+            number.__class__)
+
+    if number < 0:
+        raise ValueError('Negative numbers cannot be used: %i' % number)
+
+    # Do some bounds checking
+    if number == 0:
+        needed_bytes = 1
+        raw_bytes = [ZERO_BYTE]
+    else:
+        needed_bytes = common.byte_size(number)
+        raw_bytes = []
+
+    # You cannot compare None > 0 in Python 3x. It will fail with a TypeError.
+    if block_size and block_size > 0:
+        if needed_bytes > block_size:
+            raise OverflowError('Needed %i bytes for number, but block size '
+                'is %i' % (needed_bytes, block_size))
+
+    # Convert the number to bytes.
+    while number > 0:
+        raw_bytes.insert(0, byte(number & 0xFF))
+        number >>= 8
+
+    # Pad with zeroes to fill the block
+    if block_size and block_size > 0:
+        padding = (block_size - needed_bytes) * ZERO_BYTE
+    else:
+        padding = EMPTY_BYTE
+
+    return padding + EMPTY_BYTE.join(raw_bytes)
+
+
+def bytes_leading(raw_bytes, needle=ZERO_BYTE):
+    '''
+    Finds the number of prefixed byte occurrences in the haystack.
+
+    Useful when you want to deal with padding.
+
+    :param raw_bytes:
+        Raw bytes.
+    :param needle:
+        The byte to count. Default \000.
+    :returns:
+        The number of leading needle bytes.
+    '''
+    leading = 0
+    # Indexing keeps compatibility between Python 2.x and Python 3.x
+    _byte = needle[0]
+    for x in raw_bytes:
+        if x == _byte:
+            leading += 1
+        else:
+            break
+    return leading
+
+
+def int2bytes(number, fill_size=None, chunk_size=None, overflow=False):
+    '''
+    Convert an unsigned integer to bytes (base-256 representation)::
+
+    Does not preserve leading zeros if you don't specify a chunk size or
+    fill size.
+
+    .. NOTE:
+        You must not specify both fill_size and chunk_size. Only one
+        of them is allowed.
+
+    :param number:
+        Integer value
+    :param fill_size:
+        If the optional fill size is given the length of the resulting
+        byte string is expected to be the fill size and will be padded
+        with prefix zero bytes to satisfy that length.
+    :param chunk_size:
+        If optional chunk size is given and greater than zero, pad the front of
+        the byte string with binary zeros so that the length is a multiple of
+        ``chunk_size``.
+    :param overflow:
+        ``False`` (default). If this is ``True``, no ``OverflowError``
+        will be raised when the fill_size is shorter than the length
+        of the generated byte sequence. Instead the byte sequence will
+        be returned as is.
+    :returns:
+        Raw bytes (base-256 representation).
+    :raises:
+        ``OverflowError`` when fill_size is given and the number takes up more
+        bytes than fit into the block. This requires the ``overflow``
+        argument to this function to be set to ``False`` otherwise, no
+        error will be raised.
+    '''
+    if number < 0:
+        raise ValueError("Number must be an unsigned integer: %d" % number)
+
+    if fill_size and chunk_size:
+        raise ValueError("You can either fill or pad chunks, but not both")
+
+    # Ensure these are integers.
+    number & 1
+
+    raw_bytes = b('')
+
+    # Pack the integer one machine word at a time into bytes.
+    num = number
+    word_bits, _, max_uint, pack_type = get_word_alignment(num)
+    pack_format = ">%s" % pack_type
+    while num > 0:
+        raw_bytes = pack(pack_format, num & max_uint) + raw_bytes
+        num >>= word_bits
+    # Obtain the index of the first non-zero byte.
+    zero_leading = bytes_leading(raw_bytes)
+    if number == 0:
+        raw_bytes = ZERO_BYTE
+    # De-padding.
+    raw_bytes = raw_bytes[zero_leading:]
+
+    length = len(raw_bytes)
+    if fill_size and fill_size > 0:
+        if not overflow and length > fill_size:
+            raise OverflowError(
+                "Need %d bytes for number, but fill size is %d" %
+                (length, fill_size)
+            )
+        raw_bytes = raw_bytes.rjust(fill_size, ZERO_BYTE)
+    elif chunk_size and chunk_size > 0:
+        remainder = length % chunk_size
+        if remainder:
+            padding_size = chunk_size - remainder
+            raw_bytes = raw_bytes.rjust(length + padding_size, ZERO_BYTE)
+    return raw_bytes
+
+
+if __name__ == '__main__':
+    import doctest
+    doctest.testmod()
+
diff --git a/lib/rsa/rsa/varblock.py b/lib/rsa/rsa/varblock.py
new file mode 100644
index 0000000..c7d96ae
--- /dev/null
+++ b/lib/rsa/rsa/varblock.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
+#
+#  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.
+
+'''VARBLOCK file support
+
+The VARBLOCK file format is as follows, where || denotes byte concatenation:
+
+    FILE := VERSION || BLOCK || BLOCK ...
+
+    BLOCK := LENGTH || DATA
+
+    LENGTH := varint-encoded length of the subsequent data. Varint comes from
+    Google Protobuf, and encodes an integer into a variable number of bytes.
+    Each byte uses the 7 lowest bits to encode the value. The highest bit set
+    to 1 indicates the next byte is also part of the varint. The last byte will
+    have this bit set to 0.
+
+This file format is called the VARBLOCK format, in line with the varint format
+used to denote the block sizes.
+
+'''
+
+from rsa._compat import byte, b
+
+
+ZERO_BYTE = b('\x00')
+VARBLOCK_VERSION = 1
+
+def read_varint(infile):
+    '''Reads a varint from the file.
+
+    When the first byte to be read indicates EOF, (0, 0) is returned. When an
+    EOF occurs when at least one byte has been read, an EOFError exception is
+    raised.
+
+    @param infile: the file-like object to read from. It should have a read()
+        method.
+    @returns (varint, length), the read varint and the number of read bytes.
+    '''
+
+    varint = 0
+    read_bytes = 0
+
+    while True:
+        char = infile.read(1)
+        if len(char) == 0:
+            if read_bytes == 0:
+                return (0, 0)
+            raise EOFError('EOF while reading varint, value is %i so far' %
+                           varint)
+
+        byte = ord(char)
+        varint += (byte & 0x7F) << (7 * read_bytes)
+
+        read_bytes += 1
+
+        if not byte & 0x80:
+            return (varint, read_bytes)
+
+
+def write_varint(outfile, value):
+    '''Writes a varint to a file.
+
+    @param outfile: the file-like object to write to. It should have a write()
+        method.
+    @returns the number of written bytes.
+    '''
+
+    # there is a big difference between 'write the value 0' (this case) and
+    # 'there is nothing left to write' (the false-case of the while loop)
+
+    if value == 0:
+        outfile.write(ZERO_BYTE)
+        return 1
+
+    written_bytes = 0
+    while value > 0:
+        to_write = value & 0x7f
+        value = value >> 7
+
+        if value > 0:
+            to_write |= 0x80
+
+        outfile.write(byte(to_write))
+        written_bytes += 1
+
+    return written_bytes
+
+
+def yield_varblocks(infile):
+    '''Generator, yields each block in the input file.
+
+    @param infile: file to read, is expected to have the VARBLOCK format as
+        described in the module's docstring.
+    @yields the contents of each block.
+    '''
+
+    # Check the version number
+    first_char = infile.read(1)
+    if len(first_char) == 0:
+        raise EOFError('Unable to read VARBLOCK version number')
+
+    version = ord(first_char)
+    if version != VARBLOCK_VERSION:
+        raise ValueError('VARBLOCK version %i not supported' % version)
+
+    while True:
+        (block_size, read_bytes) = read_varint(infile)
+
+        # EOF at block boundary, that's fine.
+        if read_bytes == 0 and block_size == 0:
+            break
+
+        block = infile.read(block_size)
+
+        read_size = len(block)
+        if read_size != block_size:
+            raise EOFError('Block size is %i, but could read only %i bytes' %
+                           (block_size, read_size))
+
+        yield block
+
+
+def yield_fixedblocks(infile, blocksize):
+    '''Generator, yields each block of ``blocksize`` bytes in the input file.
+
+    :param infile: file to read and separate in blocks.
+    :returns: a generator that yields the contents of each block
+    '''
+
+    while True:
+        block = infile.read(blocksize)
+
+        read_bytes = len(block)
+        if read_bytes == 0:
+            break
+
+        yield block
+
+        if read_bytes < blocksize:
+            break
+
diff --git a/old_dev_appserver.py b/old_dev_appserver.py
index b15365d..2af168b 100644
--- a/old_dev_appserver.py
+++ b/old_dev_appserver.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [
diff --git a/php/sdk/google/appengine/api/blobstore/blobstore_service_pb.php b/php/sdk/google/appengine/api/blobstore/blobstore_service_pb.php
index effca68..b4a312b 100644
--- a/php/sdk/google/appengine/api/blobstore/blobstore_service_pb.php
+++ b/php/sdk/google/appengine/api/blobstore/blobstore_service_pb.php
@@ -372,8 +372,26 @@
     public function clearBlobKey() {
       $this->blob_key = array();
     }
+    public function getToken() {
+      if (!isset($this->token)) {
+        return '';
+      }
+      return $this->token;
+    }
+    public function setToken($val) {
+      $this->token = $val;
+      return $this;
+    }
+    public function clearToken() {
+      unset($this->token);
+      return $this;
+    }
+    public function hasToken() {
+      return isset($this->token);
+    }
     public function clear() {
       $this->clearBlobKey();
+      $this->clearToken();
     }
     public function byteSizePartial() {
       $res = 0;
@@ -382,6 +400,10 @@
       foreach ($this->blob_key as $value) {
         $res += $this->lengthString(strlen($value));
       }
+      if (isset($this->token)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->token));
+      }
       return $res;
     }
     public function outputPartial($out) {
@@ -390,6 +412,10 @@
         $out->putVarInt32(10);
         $out->putPrefixedString($value);
       }
+      if (isset($this->token)) {
+        $out->putVarInt32(18);
+        $out->putPrefixedString($this->token);
+      }
     }
     public function tryMerge($d) {
       while($d->avail() > 0) {
@@ -400,6 +426,11 @@
             $this->addBlobKey(substr($d->buffer(), $d->pos(), $length));
             $d->skip($length);
             break;
+          case 18:
+            $length = $d->getVarInt32();
+            $this->setToken(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
           case 0:
             throw new \google\net\ProtocolBufferDecodeError();
             break;
@@ -416,6 +447,9 @@
       foreach ($x->getBlobKeyList() as $v) {
         $this->addBlobKey($v);
       }
+      if ($x->hasToken()) {
+        $this->setToken($x->getToken());
+      }
     }
     public function equals($x) {
       if ($x === $this) { return true; }
@@ -423,6 +457,8 @@
       foreach (array_map(null, $this->blob_key, $x->blob_key) as $v) {
         if ($v[0] !== $v[1]) return false;
       }
+      if (isset($this->token) !== isset($x->token)) return false;
+      if (isset($this->token) && $this->token !== $x->token) return false;
       return true;
     }
     public function shortDebugString($prefix = "") {
@@ -430,6 +466,9 @@
       foreach ($this->blob_key as $value) {
         $res .= $prefix . "blob_key: " . $this->debugFormatString($value) . "\n";
       }
+      if (isset($this->token)) {
+        $res .= $prefix . "token: " . $this->debugFormatString($this->token) . "\n";
+      }
       return $res;
     }
   }
diff --git a/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php b/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php
index 97db6fe..456f35f 100644
--- a/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php
+++ b/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php
@@ -33,16 +33,8 @@
 use google\appengine\files\GetDefaultGsBucketNameResponse;
 use google\appengine\runtime\ApiProxy;
 use google\appengine\runtime\ApplicationError;
-use google\appengine\util as util;
-
-require_once 'google/appengine/api/blobstore/blobstore_service_pb.php';
-require_once 'google/appengine/api/cloud_storage/CloudStorageException.php';
-require_once 'google/appengine/api/files/file_service_pb.php';
-require_once 'google/appengine/api/images/images_service_pb.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-require_once 'google/appengine/runtime/ApiProxy.php';
-require_once 'google/appengine/runtime/ApplicationError.php';
-require_once 'google/appengine/util/array_util.php';
+use google\appengine\util\ArrayUtil;
+use google\appengine\util\StringUtil;
 
 /**
  * CloudStorageTools allows the user to create and serve data with
@@ -58,8 +50,14 @@
   // The GCS endpoint path when running in the dev appserver.
   const LOCAL_ENDPOINT = "/_ah/gcs";
 
-  // The storage host when running in production.
-  const PRODUCTION_HOST = "storage.googleapis.com";
+  // The storage host when running in production
+  // - The subdomain format is more secure but does not work for HTTPS if the
+  //   bucket name contains ".". This is becuase the wildcard SSL certificate
+  //   used by GCS can only validate one level of subdomain.
+  // - The path format is less secure and should only be used for the specific
+  //   case when the subdomain format fails.
+  const PRODUCTION_HOST_SUBDOMAIN_FORMAT = "%s.storage.googleapis.com";
+  const PRODUCTION_HOST_PATH_FORMAT = "storage.googleapis.com/%s";
 
   // The GCS filename format (bucket, object).
   const GS_FILENAME_FORMAT = "gs://%s/%s";
@@ -80,9 +78,9 @@
       'use_range'];
 
   private static $get_image_serving_url_default_options = [
-    'crop'       => false,
-    'secure_url' => false,
-    'size'       => null,
+      'crop'       => false,
+      'secure_url' => false,
+      'size'       => null,
   ];
 
   /**
@@ -91,7 +89,20 @@
    * expected to be a closure that accepts a key, value pair where key is the
    * header name, and value is the header value.
    */
-  static private $send_header = null;
+  private static $send_header = null;
+
+  /**
+   * Object names may contain characters that need to be percent-encoded when
+   * building the URL. All characters allowed for bucket name are URL-safe. See
+   * https://developers.google.com/storage/docs/bucketnaming#requirements for
+   * more details.
+   */
+  private static $url_path_translation_map = [
+      ' ' => '%20',
+      '#' => '%23',
+      '%' => '%25',
+      '?' => '%3F',
+  ];
 
   /**
    * Create an absolute URL that can be used by a user to asynchronously upload
@@ -323,26 +334,39 @@
    * format or $use_https is not a boolean.
    */
   public static function getPublicUrl($gs_filename, $use_https) {
-    $path = self::stripGsPrefix($gs_filename);
-
     if (!is_bool($use_https)) {
       throw new \InvalidArgumentException(
           'Parameter $use_https must be boolean but was ' .
           typeOrClass($use_https));
     }
 
-    if (self::isDevelServer()) {
-      $scheme = "http";
-      $url_base = sprintf("%s%s", getenv('HTTP_HOST'), self::LOCAL_ENDPOINT);
-    } else {
-      $scheme = $use_https ? "https" : "http";
-      $url_base = self::PRODUCTION_HOST;
+    if (!self::parseFilename($gs_filename, $bucket, $object)) {
+      throw new \InvalidArgumentException(
+          sprintf('Invalid Google Cloud Storage filename: %s', $gs_filename));
     }
 
-    return sprintf("%s://%s/%s",
+    if (self::isDevelServer()) {
+      $scheme = 'http';
+      $host = getenv('HTTP_HOST');
+      $path = sprintf('%s/%s%s', self::LOCAL_ENDPOINT, $bucket, $object);
+    } else {
+      // Use path format for HTTPS URL when the bucket name contains "." to
+      // avoid SSL certificate validation issue.
+      if ($use_https && strpos($bucket, '.') !== false) {
+        $format = self::PRODUCTION_HOST_PATH_FORMAT;
+      } else {
+        $format = self::PRODUCTION_HOST_SUBDOMAIN_FORMAT;
+      }
+
+      $scheme = $use_https ? 'https' : 'http';
+      $host = sprintf($format, $bucket);
+      $path = $object;
+    }
+
+    return sprintf('%s://%s%s',
                    $scheme,
-                   $url_base,
-                   str_replace('%', urlencode('%'), $path));
+                   $host,
+                   strtr($path, self::$url_path_translation_map));
   }
 
   /**
@@ -358,18 +382,73 @@
   public static function getFilename($bucket, $object) {
     if (self::validateBucketName($bucket) === false) {
       throw new \InvalidArgumentException(
-          sprintf('Invalid cloud storage bucket name \'%s\' ', $bucket));
+          sprintf('Invalid cloud storage bucket name \'%s\'', $bucket));
     }
 
     if (self::validateObjectName($object) === false) {
       throw new \InvalidArgumentException(
-          sprintf('Invalid cloud storage object name \'%s\' ', $object));
+          sprintf('Invalid cloud storage object name \'%s\'', $object));
     }
 
     return sprintf(self::GS_FILENAME_FORMAT, $bucket, $object);
   }
 
   /**
+   * Parse and extract the bucket and object names from the supplied filename.
+   *
+   * @param string $filename The filename in the format gs://bucket_name or
+   * gs://bucket_name/object_name.
+   * @param string &$bucket The extracted bucket.
+   * @param string &$object The extracted bucket. Can be null if the filename
+   * contains only bucket name.
+   *
+   * @return bool true if the filename is successfully parsed, false otherwise.
+   */
+  public static function parseFilename($filename, &$bucket, &$object) {
+    $bucket = null;
+    $object = null;
+
+    // $filename may contain nasty characters like # and ? that can throw off
+    // parse_url(). It is best to do a manual parse here.
+    $gs_prefix_len = strlen(self::GS_PREFIX);
+    if (!StringUtil::startsWith($filename, self::GS_PREFIX)) {
+      return false;
+    }
+
+    $first_slash_pos = strpos($filename, '/', $gs_prefix_len);
+    if ($first_slash_pos === false) {
+      $bucket = substr($filename, $gs_prefix_len);
+    } else {
+      $bucket = substr($filename, $gs_prefix_len,
+          $first_slash_pos - $gs_prefix_len);
+      // gs://bucket_name/ is treated the same as gs://bucket_name where
+      // $object should be set to null.
+      if ($first_slash_pos != strlen($filename) - 1) {
+        $object = substr($filename, $first_slash_pos);
+      }
+    }
+
+    if (strlen($bucket) == 0) {
+      return false;
+    }
+
+    // Validate bucket & object names.
+    if (self::validateBucketName($bucket) === false) {
+      trigger_error(sprintf('Invalid cloud storage bucket name \'%s\'',
+          $bucket), E_USER_ERROR);
+      return false;
+    }
+
+    if (isset($object) && self::validateObjectName($object) === false) {
+      trigger_error(sprintf('Invalid cloud storage object name \'%s\'',
+          $object), E_USER_ERROR);
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
    * Validate the bucket name according to the rules stated at
    * https://developers.google.com/storage/docs/bucketnaming.
    */
@@ -472,17 +551,17 @@
     }
 
     // Determine the range to send
-    $start = util\findByKeyOrNull($options, "start");
-    $end = util\findByKeyOrNull($options, "end");
-    $use_range = util\findByKeyOrNull($options, "use_range");
-    $request_range_header = util\findByKeyOrNull($_SERVER, "HTTP_RANGE");
+    $start = ArrayUtil::findByKeyOrNull($options, "start");
+    $end = ArrayUtil::findByKeyOrNull($options, "end");
+    $use_range = ArrayUtil::findByKeyOrNull($options, "use_range");
+    $request_range_header = ArrayUtil::findByKeyOrNull($_SERVER, "HTTP_RANGE");
 
     $range_header = self::checkRanges($start,
                                       $end,
                                       $use_range,
                                       $request_range_header);
 
-    $save_as = util\findByKeyOrNull($options, "save_as");
+    $save_as = ArrayUtil::findByKeyOrNull($options, "save_as");
     if (isset($save_as) && !is_string($save_as)) {
       throw new \InvalidArgumentException("Unexpected value for save_as.");
     }
@@ -494,7 +573,7 @@
       self::sendHeader(self::BLOB_RANGE_HEADER, $range_header);
     }
 
-    $content_type = util\findByKeyOrNull($options, "content_type");
+    $content_type = ArrayUtil::findByKeyOrNull($options, "content_type");
     if (isset($content_type)) {
       self::sendHeader("Content-Type", $content_type);
     }
diff --git a/php/sdk/google/appengine/api/cloud_storage/CloudStorageToolsTest.php b/php/sdk/google/appengine/api/cloud_storage/CloudStorageToolsTest.php
index d731aac..46778fd 100644
--- a/php/sdk/google/appengine/api/cloud_storage/CloudStorageToolsTest.php
+++ b/php/sdk/google/appengine/api/cloud_storage/CloudStorageToolsTest.php
@@ -61,6 +61,11 @@
 
   public function tearDown() {
     $_SERVER = $this->_SERVER;
+
+    // Reset environmental variables.
+    putenv("SERVER_SOFTWARE=");
+    putenv("HTTP_HOST=");
+
     parent::tearDown();
   }
 
@@ -656,19 +661,33 @@
   }
 
   public function testGetPublicUrlInProduction() {
-    $bucket = "bucket";
     $object = "object";
-    $gs_filename = sprintf("gs://%s/%s", $bucket, $object);
-    $host = "storage.googleapis.com";
     putenv("SERVER_SOFTWARE=Google App Engine/1.8.6");
 
-    // Get HTTPS URL
-    $expected = "https://storage.googleapis.com/bucket/object";
+    $bucket_with_a_dot = "bucket.name";
+    $gs_filename = sprintf("gs://%s/%s", $bucket_with_a_dot, $object);
+
+    // Get HTTPS URL for bucket containing "." - should use the path format to
+    // avoid SSL certificate validation issue.
+    $expected = "https://storage.googleapis.com/bucket.name/object";
     $actual = CloudStorageTools::getPublicUrl($gs_filename, true);
     $this->assertEquals($expected, $actual);
 
-    // Get HTTP URL
-    $expected = "http://storage.googleapis.com/bucket/object";
+    // Get HTTP URL for bucket contain "." - should use the subdomain format.
+    $expected = "http://bucket.name.storage.googleapis.com/object";
+    $actual = CloudStorageTools::getPublicUrl($gs_filename, false);
+    $this->assertEquals($expected, $actual);
+
+    $bucket_without_dot = "bucket";
+    $gs_filename = sprintf("gs://%s/%s", $bucket_without_dot, $object);
+
+    // Get HTTPS URL for bucket without "." - should use the subdomain format.
+    $expected = "https://bucket.storage.googleapis.com/object";
+    $actual = CloudStorageTools::getPublicUrl($gs_filename, true);
+    $this->assertEquals($expected, $actual);
+
+    // Get HTTP URL for bucket without "." - should use the subdomain format.
+    $expected = "http://bucket.storage.googleapis.com/object";
     $actual = CloudStorageTools::getPublicUrl($gs_filename, false);
     $this->assertEquals($expected, $actual);
   }
@@ -677,9 +696,8 @@
     $bucket = "bucket";
     $object = "object";
     $gs_filename = sprintf("gs://%s/%s", $bucket, $object);
-    $host = "localhost:8080";
     putenv("SERVER_SOFTWARE=Development/2.0");
-    putenv("HTTP_HOST=" . $host);
+    putenv("HTTP_HOST=localhost:8080");
 
     // Get HTTPS URL
     $expected = "http://localhost:8080/_ah/gcs/bucket/object";
@@ -692,6 +710,16 @@
     $this->assertEquals($expected, $actual);
   }
 
+  public function testGetPublicUrlEncoding() {
+    $bucket = "bucket";
+    $object = " %#?";
+    $gs_filename = sprintf("gs://%s/%s", $bucket, $object);
+
+    $expected = "https://bucket.storage.googleapis.com/%20%25%23%3F";
+    $actual = CloudStorageTools::getPublicUrl($gs_filename, true);
+    $this->assertEquals($expected, $actual);
+  }
+
   public function testGetFilenameFromValidBucketAndObject() {
     $bucket = "bucket";
     $object = "object";
@@ -730,5 +758,23 @@
       CloudStorageTools::getFilename('foo', $object);
     }
   }
+
+  public function testParseFilenameWithBucketAndObject() {
+    $gs_filename = 'gs://bucket/object';
+
+    $this->assertEquals(true,
+        CloudStorageTools::parseFilename($gs_filename, $bucket, $object));
+    $this->assertEquals('bucket', $bucket);
+    $this->assertEquals('/object', $object);
+  }
+
+  public function testParseFilenameWithBucketOnly() {
+    $gs_filename = 'gs://bucket';
+
+    $this->assertEquals(true,
+        CloudStorageTools::parseFilename($gs_filename, $bucket, $object));
+    $this->assertEquals('bucket', $bucket);
+    $this->assertEquals(null, $object);
+  }
 }
 
diff --git a/php/sdk/google/appengine/api/log/LogService.php b/php/sdk/google/appengine/api/log/LogService.php
index bcbb6af..367edec 100644
--- a/php/sdk/google/appengine/api/log/LogService.php
+++ b/php/sdk/google/appengine/api/log/LogService.php
@@ -19,18 +19,12 @@
 
 namespace google\appengine\api\log;
 
-require_once 'google/appengine/api/logservice/log_service_pb.php';
-require_once 'google/appengine/api/log/RequestLogIterator.php';
-require_once 'google/appengine/api/log/LogException.php';
-require_once 'google/appengine/runtime/ApiProxy.php';
-require_once 'google/appengine/util/string_util.php';
-
 use google\appengine\LogReadRequest;
 use google\appengine\LogReadResponse;
 use google\appengine\LogServiceError\ErrorCode;
 use google\appengine\runtime\ApiProxy;
 use google\appengine\runtime\ApplicationError;
-use google\appengine\util as util;
+use google\appengine\util\StringUtil;
 
 /**
  * The LogService allows an application to query for request and application
@@ -161,7 +155,7 @@
           if (!is_string($value)) {
             self::optionTypeException($key, $value, 'string');
           }
-          $decoded = util\base64UrlDecode($value);
+          $decoded = StringUtil::base64UrlDecode($value);
           $request->mutableOffset()->parseFromString($decoded);
           break;
         case 'minimum_log_level':
diff --git a/php/sdk/google/appengine/api/log/RequestLog.php b/php/sdk/google/appengine/api/log/RequestLog.php
index 9080cc8..f990e6f 100644
--- a/php/sdk/google/appengine/api/log/RequestLog.php
+++ b/php/sdk/google/appengine/api/log/RequestLog.php
@@ -19,10 +19,7 @@
 
 namespace google\appengine\api\log;
 
-require_once 'google/appengine/api/log/AppLogLine.php';
-require_once 'google/appengine/util/string_util.php';
-
-use google\appengine\util as util;
+use google\appengine\util\StringUtil;
 
 /**
  * Represents the details of a single request and may optionally contain
@@ -175,7 +172,7 @@
   public function getOffset() {
     if ($this->pb->hasOffset()) {
       $offset = $this->pb->getOffset()->serializeToString();
-      return util\base64UrlEncode($offset);
+      return StringUtil::base64UrlEncode($offset);
     }
     return false;
   }
diff --git a/php/sdk/google/appengine/api/modules/ModulesService.php b/php/sdk/google/appengine/api/modules/ModulesService.php
index 2588937..85532b4 100644
--- a/php/sdk/google/appengine/api/modules/ModulesService.php
+++ b/php/sdk/google/appengine/api/modules/ModulesService.php
@@ -296,12 +296,12 @@
    * @throws \InvalidArgumentException If $module or $version is not a string.
    * @throws ModulesException if the given combination of $module and $version
    * is invalid.
-   * @throws InvalidModuleStateException if the given $module is already
+   * @throws InvalidModuleStateException if the given $version is already
    * started or cannot be started.
    * @throws TransientModulesException if there is an issue starting the module
    * version.
    */
-  public static function startModule($module, $version) {
+  public static function startVersion($module, $version) {
     $req = new StartModuleRequest();
     $resp = new StartModuleResponse();
 
@@ -336,12 +336,12 @@
    * @throws \InvalidArgumentException If $module or $version is not a string.
    * @throws ModulesException if the given combination of $module and $version
    * instance is invalid.
-   * @throws InvalidModuleStateException if the given $module is already
+   * @throws InvalidModuleStateException if the given $version is already
    * stopped or cannot be stopped.
    * @throws TransientModulesException if there is an issue stopping the module
    * version.
    */
-  public static function stopModule($module = null, $version = null) {
+  public static function stopVersion($module = null, $version = null) {
     $req = new StopModuleRequest();
     $resp = new StopModuleResponse();
 
diff --git a/php/sdk/google/appengine/api/modules/ModulesServiceTest.php b/php/sdk/google/appengine/api/modules/ModulesServiceTest.php
index f0765ca..daf055b 100644
--- a/php/sdk/google/appengine/api/modules/ModulesServiceTest.php
+++ b/php/sdk/google/appengine/api/modules/ModulesServiceTest.php
@@ -241,20 +241,20 @@
 
     $this->apiProxyMock->expectCall('modules', 'StartModule', $req, $resp);
 
-    ModulesService::startModule('module1', 'v1');
+    ModulesService::startVersion('module1', 'v1');
     $this->apiProxyMock->verify();
   }
 
   public function testStartModuleWithIntegerModule() {
     $this->setExpectedException('\InvalidArgumentException',
       '$module must be a string. Actual type: integer');
-    ModulesService::startModule(5, 'v1');
+    ModulesService::startVersion(5, 'v1');
   }
 
   public function testStartModuleWithIntegerVersion() {
     $this->setExpectedException('\InvalidArgumentException',
       '$version must be a string. Actual type: integer');
-    ModulesService::startModule('module1', 5);
+    ModulesService::startVersion('module1', 5);
   }
 
   public function testStartModuleWithTransientError() {
@@ -269,7 +269,7 @@
         '\google\appengine\api\modules\TransientModulesException');
     $this->apiProxyMock->expectCall('modules', 'StartModule', $req, $resp);
 
-    ModulesService::startModule('module1', 'v1');
+    ModulesService::startVersion('module1', 'v1');
     $this->apiProxyMock->verify();
   }
 
@@ -279,7 +279,7 @@
 
     $this->apiProxyMock->expectCall('modules', 'StopModule', $req, $resp);
 
-    ModulesService::stopModule();
+    ModulesService::stopVersion();
     $this->apiProxyMock->verify();
   }
 
@@ -292,20 +292,20 @@
 
     $this->apiProxyMock->expectCall('modules', 'StopModule', $req, $resp);
 
-    ModulesService::stopModule('module1', 'v1');
+    ModulesService::stopVersion('module1', 'v1');
     $this->apiProxyMock->verify();
   }
 
   public function testStopModuleWithIntegerModule() {
     $this->setExpectedException('\InvalidArgumentException',
       '$module must be a string. Actual type: integer');
-    ModulesService::stopModule(5, 'v1');
+    ModulesService::stopVersion(5, 'v1');
   }
 
   public function testStopModuleWithIntegerVersion() {
     $this->setExpectedException('\InvalidArgumentException',
       '$version must be a string. Actual type: integer');
-    ModulesService::stopModule('module1', 5);
+    ModulesService::stopVersion('module1', 5);
   }
 
   public function testStopModuleWithTransientError() {
@@ -320,7 +320,7 @@
         '\google\appengine\api\modules\TransientModulesException');
     $this->apiProxyMock->expectCall('modules', 'StopModule', $req, $resp);
 
-    ModulesService::stopModule('module1', 'v1');
+    ModulesService::stopVersion('module1', 'v1');
     $this->apiProxyMock->verify();
   }
 
diff --git a/php/sdk/google/appengine/api/users/User.php b/php/sdk/google/appengine/api/users/User.php
index 5ac7d25..31f8a26 100644
--- a/php/sdk/google/appengine/api/users/User.php
+++ b/php/sdk/google/appengine/api/users/User.php
@@ -19,9 +19,7 @@
 
 namespace google\appengine\api\users;
 
-require_once 'google/appengine/util/string_util.php';
-
-use google\appengine\util as util;
+use google\appengine\util\StringUtil;
 
 /**
  * A user.
@@ -86,7 +84,7 @@
    */
   public function getNickname() {
     if ($this->email != null && $this->auth_domain != null &&
-        util\endsWith($this->email, '@' . $this->auth_domain)) {
+        StringUtil::endsWith($this->email, '@' . $this->auth_domain)) {
       $suffixLen = strlen($this->auth_domain) + 1;
       return substr($this->email, 0, -$suffixLen);
     } else if ($this->federated_identity) {
diff --git a/php/sdk/google/appengine/datastore/DatastoreV4ProtoTest.php b/php/sdk/google/appengine/datastore/DatastoreV4ProtoTest.php
new file mode 100644
index 0000000..5e28e6a
--- /dev/null
+++ b/php/sdk/google/appengine/datastore/DatastoreV4ProtoTest.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * 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.
+ */
+namespace google\appengine\datastore;
+
+require_once 'google/appengine/datastore/datastore_v4_pb.php';
+
+use \google\appengine\datastore\v4\Error\ErrorCode;
+
+class DatastoreV4ProtoTest extends\PHPUnit_Framework_TestCase {
+  public function testTrivial() {
+    $this->assertEquals(1, ErrorCode::BAD_REQUEST);
+  }
+}
diff --git a/php/sdk/google/appengine/datastore/datastore_v3_pb.php b/php/sdk/google/appengine/datastore/datastore_v3_pb.php
index 7c955b2..5ed31c5 100644
--- a/php/sdk/google/appengine/datastore/datastore_v3_pb.php
+++ b/php/sdk/google/appengine/datastore/datastore_v3_pb.php
@@ -3600,6 +3600,23 @@
     public function hasApproximateStorageDelta() {
       return isset($this->approximate_storage_delta);
     }
+    public function getIdSequenceUpdates() {
+      if (!isset($this->id_sequence_updates)) {
+        return 0;
+      }
+      return $this->id_sequence_updates;
+    }
+    public function setIdSequenceUpdates($val) {
+      $this->id_sequence_updates = $val;
+      return $this;
+    }
+    public function clearIdSequenceUpdates() {
+      unset($this->id_sequence_updates);
+      return $this;
+    }
+    public function hasIdSequenceUpdates() {
+      return isset($this->id_sequence_updates);
+    }
     public function clear() {
       $this->clearIndexWrites();
       $this->clearIndexWriteBytes();
@@ -3607,6 +3624,7 @@
       $this->clearEntityWriteBytes();
       $this->clearCommitCost();
       $this->clearApproximateStorageDelta();
+      $this->clearIdSequenceUpdates();
     }
     public function byteSizePartial() {
       $res = 0;
@@ -3634,6 +3652,10 @@
         $res += 1;
         $res += $this->lengthVarInt64($this->approximate_storage_delta);
       }
+      if (isset($this->id_sequence_updates)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->id_sequence_updates);
+      }
       return $res;
     }
     public function outputPartial($out) {
@@ -3662,6 +3684,10 @@
         $out->putVarInt32(64);
         $out->putVarInt32($this->approximate_storage_delta);
       }
+      if (isset($this->id_sequence_updates)) {
+        $out->putVarInt32(72);
+        $out->putVarInt32($this->id_sequence_updates);
+      }
     }
     public function tryMerge($d) {
       while($d->avail() > 0) {
@@ -3685,6 +3711,9 @@
           case 64:
             $this->setApproximateStorageDelta($d->getVarInt32());
             break;
+          case 72:
+            $this->setIdSequenceUpdates($d->getVarInt32());
+            break;
           case 0:
             throw new \google\net\ProtocolBufferDecodeError();
             break;
@@ -3717,6 +3746,9 @@
       if ($x->hasApproximateStorageDelta()) {
         $this->setApproximateStorageDelta($x->getApproximateStorageDelta());
       }
+      if ($x->hasIdSequenceUpdates()) {
+        $this->setIdSequenceUpdates($x->getIdSequenceUpdates());
+      }
     }
     public function equals($x) {
       if ($x === $this) { return true; }
@@ -3732,6 +3764,8 @@
       if (isset($this->commitcost) && !$this->commitcost->equals($x->commitcost)) return false;
       if (isset($this->approximate_storage_delta) !== isset($x->approximate_storage_delta)) return false;
       if (isset($this->approximate_storage_delta) && !$this->integerEquals($this->approximate_storage_delta, $x->approximate_storage_delta)) return false;
+      if (isset($this->id_sequence_updates) !== isset($x->id_sequence_updates)) return false;
+      if (isset($this->id_sequence_updates) && !$this->integerEquals($this->id_sequence_updates, $x->id_sequence_updates)) return false;
       return true;
     }
     public function shortDebugString($prefix = "") {
@@ -3754,6 +3788,9 @@
       if (isset($this->approximate_storage_delta)) {
         $res .= $prefix . "approximate_storage_delta: " . $this->debugFormatInt32($this->approximate_storage_delta) . "\n";
       }
+      if (isset($this->id_sequence_updates)) {
+        $res .= $prefix . "id_sequence_updates: " . $this->debugFormatInt32($this->id_sequence_updates) . "\n";
+      }
       return $res;
     }
   }
@@ -6767,9 +6804,32 @@
     public function hasEnd() {
       return isset($this->end);
     }
+    public function getCost() {
+      if (!isset($this->cost)) {
+        return new \google\appengine_datastore_v3\Cost();
+      }
+      return $this->cost;
+    }
+    public function mutableCost() {
+      if (!isset($this->cost)) {
+        $res = new \google\appengine_datastore_v3\Cost();
+        $this->cost = $res;
+        return $res;
+      }
+      return $this->cost;
+    }
+    public function clearCost() {
+      if (isset($this->cost)) {
+        unset($this->cost);
+      }
+    }
+    public function hasCost() {
+      return isset($this->cost);
+    }
     public function clear() {
       $this->clearStart();
       $this->clearEnd();
+      $this->clearCost();
     }
     public function byteSizePartial() {
       $res = 0;
@@ -6781,6 +6841,10 @@
         $res += 1;
         $res += $this->lengthVarInt64($this->end);
       }
+      if (isset($this->cost)) {
+        $res += 1;
+        $res += $this->lengthString($this->cost->byteSizePartial());
+      }
       return $res;
     }
     public function outputPartial($out) {
@@ -6792,6 +6856,11 @@
         $out->putVarInt32(16);
         $out->putVarInt64($this->end);
       }
+      if (isset($this->cost)) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($this->cost->byteSizePartial());
+        $this->cost->outputPartial($out);
+      }
     }
     public function tryMerge($d) {
       while($d->avail() > 0) {
@@ -6803,6 +6872,12 @@
           case 16:
             $this->setEnd($d->getVarInt64());
             break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableCost()->tryMerge($tmp);
+            break;
           case 0:
             throw new \google\net\ProtocolBufferDecodeError();
             break;
@@ -6814,6 +6889,7 @@
     public function checkInitialized() {
       if (!isset($this->start)) return 'start';
       if (!isset($this->end)) return 'end';
+      if (isset($this->cost) && (!$this->cost->isInitialized())) return 'cost';
       return null;
     }
     public function mergeFrom($x) {
@@ -6824,6 +6900,9 @@
       if ($x->hasEnd()) {
         $this->setEnd($x->getEnd());
       }
+      if ($x->hasCost()) {
+        $this->mutableCost()->mergeFrom($x->getCost());
+      }
     }
     public function equals($x) {
       if ($x === $this) { return true; }
@@ -6831,6 +6910,8 @@
       if (isset($this->start) && !$this->integerEquals($this->start, $x->start)) return false;
       if (isset($this->end) !== isset($x->end)) return false;
       if (isset($this->end) && !$this->integerEquals($this->end, $x->end)) return false;
+      if (isset($this->cost) !== isset($x->cost)) return false;
+      if (isset($this->cost) && !$this->cost->equals($x->cost)) return false;
       return true;
     }
     public function shortDebugString($prefix = "") {
@@ -6841,6 +6922,9 @@
       if (isset($this->end)) {
         $res .= $prefix . "end: " . $this->debugFormatInt64($this->end) . "\n";
       }
+      if (isset($this->cost)) {
+        $res .= $prefix . "cost <\n" . $this->cost->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
       return $res;
     }
   }
diff --git a/php/sdk/google/appengine/datastore/datastore_v4_pb.php b/php/sdk/google/appengine/datastore/datastore_v4_pb.php
new file mode 100644
index 0000000..d5c7ab2
--- /dev/null
+++ b/php/sdk/google/appengine/datastore/datastore_v4_pb.php
@@ -0,0 +1,5014 @@
+<?php
+/**
+ * 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.
+ */
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: google/appengine/datastore/datastore_v4.proto
+
+namespace dummy {
+  require_once 'google/appengine/runtime/proto/ProtocolMessage.php';
+  require_once 'google/appengine/datastore/entity_v4_pb.php';
+}
+namespace google\appengine\datastore\v4\Error {
+  class ErrorCode {
+    const BAD_REQUEST = 1;
+    const CONCURRENT_TRANSACTION = 2;
+    const INTERNAL_ERROR = 3;
+    const NEED_INDEX = 4;
+    const TIMEOUT = 5;
+    const PERMISSION_DENIED = 6;
+    const BIGTABLE_ERROR = 7;
+    const COMMITTED_BUT_STILL_APPLYING = 8;
+    const CAPABILITY_DISABLED = 9;
+    const TRY_ALTERNATE_BACKEND = 10;
+    const SAFE_TIME_TOO_OLD = 11;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Error extends \google\net\ProtocolMessage {
+    public function clear() {
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      return $res;
+    }
+    public function outputPartial($out) {
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\EntityResult {
+  class ResultType {
+    const FULL = 1;
+    const PROJECTION = 2;
+    const KEY_ONLY = 3;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class EntityResult extends \google\net\ProtocolMessage {
+    public function getEntity() {
+      if (!isset($this->entity)) {
+        return new \google\appengine\datastore\v4\Entity();
+      }
+      return $this->entity;
+    }
+    public function mutableEntity() {
+      if (!isset($this->entity)) {
+        $res = new \google\appengine\datastore\v4\Entity();
+        $this->entity = $res;
+        return $res;
+      }
+      return $this->entity;
+    }
+    public function clearEntity() {
+      if (isset($this->entity)) {
+        unset($this->entity);
+      }
+    }
+    public function hasEntity() {
+      return isset($this->entity);
+    }
+    public function getVersion() {
+      if (!isset($this->version)) {
+        return "0";
+      }
+      return $this->version;
+    }
+    public function setVersion($val) {
+      if (is_double($val)) {
+        $this->version = sprintf('%0.0F', $val);
+      } else {
+        $this->version = $val;
+      }
+      return $this;
+    }
+    public function clearVersion() {
+      unset($this->version);
+      return $this;
+    }
+    public function hasVersion() {
+      return isset($this->version);
+    }
+    public function clear() {
+      $this->clearEntity();
+      $this->clearVersion();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->entity)) {
+        $res += 1;
+        $res += $this->lengthString($this->entity->byteSizePartial());
+      }
+      if (isset($this->version)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->version);
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->entity)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->entity->byteSizePartial());
+        $this->entity->outputPartial($out);
+      }
+      if (isset($this->version)) {
+        $out->putVarInt32(16);
+        $out->putVarInt64($this->version);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableEntity()->tryMerge($tmp);
+            break;
+          case 16:
+            $this->setVersion($d->getVarInt64());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if ((!isset($this->entity)) || (!$this->entity->isInitialized())) return 'entity';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasEntity()) {
+        $this->mutableEntity()->mergeFrom($x->getEntity());
+      }
+      if ($x->hasVersion()) {
+        $this->setVersion($x->getVersion());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->entity) !== isset($x->entity)) return false;
+      if (isset($this->entity) && !$this->entity->equals($x->entity)) return false;
+      if (isset($this->version) !== isset($x->version)) return false;
+      if (isset($this->version) && !$this->integerEquals($this->version, $x->version)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->entity)) {
+        $res .= $prefix . "entity <\n" . $this->entity->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->version)) {
+        $res .= $prefix . "version: " . $this->debugFormatInt64($this->version) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Query extends \google\net\ProtocolMessage {
+    private $projection = array();
+    private $kind = array();
+    private $order = array();
+    private $group_by = array();
+    public function getProjectionSize() {
+      return sizeof($this->projection);
+    }
+    public function getProjectionList() {
+      return $this->projection;
+    }
+    public function mutableProjection($idx) {
+      if (!isset($this->projection[$idx])) {
+        $val = new \google\appengine\datastore\v4\PropertyExpression();
+        $this->projection[$idx] = $val;
+        return $val;
+      }
+      return $this->projection[$idx];
+    }
+    public function getProjection($idx) {
+      if (isset($this->projection[$idx])) {
+        return $this->projection[$idx];
+      }
+      if ($idx >= end(array_keys($this->projection))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\PropertyExpression();
+    }
+    public function addProjection() {
+      $val = new \google\appengine\datastore\v4\PropertyExpression();
+      $this->projection[] = $val;
+      return $val;
+    }
+    public function clearProjection() {
+      $this->projection = array();
+    }
+    public function getKindSize() {
+      return sizeof($this->kind);
+    }
+    public function getKindList() {
+      return $this->kind;
+    }
+    public function mutableKind($idx) {
+      if (!isset($this->kind[$idx])) {
+        $val = new \google\appengine\datastore\v4\KindExpression();
+        $this->kind[$idx] = $val;
+        return $val;
+      }
+      return $this->kind[$idx];
+    }
+    public function getKind($idx) {
+      if (isset($this->kind[$idx])) {
+        return $this->kind[$idx];
+      }
+      if ($idx >= end(array_keys($this->kind))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\KindExpression();
+    }
+    public function addKind() {
+      $val = new \google\appengine\datastore\v4\KindExpression();
+      $this->kind[] = $val;
+      return $val;
+    }
+    public function clearKind() {
+      $this->kind = array();
+    }
+    public function getFilter() {
+      if (!isset($this->filter)) {
+        return new \google\appengine\datastore\v4\Filter();
+      }
+      return $this->filter;
+    }
+    public function mutableFilter() {
+      if (!isset($this->filter)) {
+        $res = new \google\appengine\datastore\v4\Filter();
+        $this->filter = $res;
+        return $res;
+      }
+      return $this->filter;
+    }
+    public function clearFilter() {
+      if (isset($this->filter)) {
+        unset($this->filter);
+      }
+    }
+    public function hasFilter() {
+      return isset($this->filter);
+    }
+    public function getOrderSize() {
+      return sizeof($this->order);
+    }
+    public function getOrderList() {
+      return $this->order;
+    }
+    public function mutableOrder($idx) {
+      if (!isset($this->order[$idx])) {
+        $val = new \google\appengine\datastore\v4\PropertyOrder();
+        $this->order[$idx] = $val;
+        return $val;
+      }
+      return $this->order[$idx];
+    }
+    public function getOrder($idx) {
+      if (isset($this->order[$idx])) {
+        return $this->order[$idx];
+      }
+      if ($idx >= end(array_keys($this->order))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\PropertyOrder();
+    }
+    public function addOrder() {
+      $val = new \google\appengine\datastore\v4\PropertyOrder();
+      $this->order[] = $val;
+      return $val;
+    }
+    public function clearOrder() {
+      $this->order = array();
+    }
+    public function getGroupBySize() {
+      return sizeof($this->group_by);
+    }
+    public function getGroupByList() {
+      return $this->group_by;
+    }
+    public function mutableGroupBy($idx) {
+      if (!isset($this->group_by[$idx])) {
+        $val = new \google\appengine\datastore\v4\PropertyReference();
+        $this->group_by[$idx] = $val;
+        return $val;
+      }
+      return $this->group_by[$idx];
+    }
+    public function getGroupBy($idx) {
+      if (isset($this->group_by[$idx])) {
+        return $this->group_by[$idx];
+      }
+      if ($idx >= end(array_keys($this->group_by))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\PropertyReference();
+    }
+    public function addGroupBy() {
+      $val = new \google\appengine\datastore\v4\PropertyReference();
+      $this->group_by[] = $val;
+      return $val;
+    }
+    public function clearGroupBy() {
+      $this->group_by = array();
+    }
+    public function getStartCursor() {
+      if (!isset($this->start_cursor)) {
+        return '';
+      }
+      return $this->start_cursor;
+    }
+    public function setStartCursor($val) {
+      $this->start_cursor = $val;
+      return $this;
+    }
+    public function clearStartCursor() {
+      unset($this->start_cursor);
+      return $this;
+    }
+    public function hasStartCursor() {
+      return isset($this->start_cursor);
+    }
+    public function getEndCursor() {
+      if (!isset($this->end_cursor)) {
+        return '';
+      }
+      return $this->end_cursor;
+    }
+    public function setEndCursor($val) {
+      $this->end_cursor = $val;
+      return $this;
+    }
+    public function clearEndCursor() {
+      unset($this->end_cursor);
+      return $this;
+    }
+    public function hasEndCursor() {
+      return isset($this->end_cursor);
+    }
+    public function getOffset() {
+      if (!isset($this->offset)) {
+        return 0;
+      }
+      return $this->offset;
+    }
+    public function setOffset($val) {
+      $this->offset = $val;
+      return $this;
+    }
+    public function clearOffset() {
+      unset($this->offset);
+      return $this;
+    }
+    public function hasOffset() {
+      return isset($this->offset);
+    }
+    public function getLimit() {
+      if (!isset($this->limit)) {
+        return 0;
+      }
+      return $this->limit;
+    }
+    public function setLimit($val) {
+      $this->limit = $val;
+      return $this;
+    }
+    public function clearLimit() {
+      unset($this->limit);
+      return $this;
+    }
+    public function hasLimit() {
+      return isset($this->limit);
+    }
+    public function clear() {
+      $this->clearProjection();
+      $this->clearKind();
+      $this->clearFilter();
+      $this->clearOrder();
+      $this->clearGroupBy();
+      $this->clearStartCursor();
+      $this->clearEndCursor();
+      $this->clearOffset();
+      $this->clearLimit();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      $this->checkProtoArray($this->projection);
+      $res += 1 * sizeof($this->projection);
+      foreach ($this->projection as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->kind);
+      $res += 1 * sizeof($this->kind);
+      foreach ($this->kind as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      if (isset($this->filter)) {
+        $res += 1;
+        $res += $this->lengthString($this->filter->byteSizePartial());
+      }
+      $this->checkProtoArray($this->order);
+      $res += 1 * sizeof($this->order);
+      foreach ($this->order as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->group_by);
+      $res += 1 * sizeof($this->group_by);
+      foreach ($this->group_by as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      if (isset($this->start_cursor)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->start_cursor));
+      }
+      if (isset($this->end_cursor)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->end_cursor));
+      }
+      if (isset($this->offset)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->offset);
+      }
+      if (isset($this->limit)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->limit);
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      $this->checkProtoArray($this->projection);
+      foreach ($this->projection as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->kind);
+      foreach ($this->kind as $value) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      if (isset($this->filter)) {
+        $out->putVarInt32(34);
+        $out->putVarInt32($this->filter->byteSizePartial());
+        $this->filter->outputPartial($out);
+      }
+      $this->checkProtoArray($this->order);
+      foreach ($this->order as $value) {
+        $out->putVarInt32(42);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->group_by);
+      foreach ($this->group_by as $value) {
+        $out->putVarInt32(50);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      if (isset($this->start_cursor)) {
+        $out->putVarInt32(58);
+        $out->putPrefixedString($this->start_cursor);
+      }
+      if (isset($this->end_cursor)) {
+        $out->putVarInt32(66);
+        $out->putPrefixedString($this->end_cursor);
+      }
+      if (isset($this->offset)) {
+        $out->putVarInt32(80);
+        $out->putVarInt32($this->offset);
+      }
+      if (isset($this->limit)) {
+        $out->putVarInt32(88);
+        $out->putVarInt32($this->limit);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addProjection()->tryMerge($tmp);
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addKind()->tryMerge($tmp);
+            break;
+          case 34:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableFilter()->tryMerge($tmp);
+            break;
+          case 42:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addOrder()->tryMerge($tmp);
+            break;
+          case 50:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addGroupBy()->tryMerge($tmp);
+            break;
+          case 58:
+            $length = $d->getVarInt32();
+            $this->setStartCursor(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 66:
+            $length = $d->getVarInt32();
+            $this->setEndCursor(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 80:
+            $this->setOffset($d->getVarInt32());
+            break;
+          case 88:
+            $this->setLimit($d->getVarInt32());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      foreach ($this->projection as $value) {
+        if (!$value->isInitialized()) return 'projection';
+      }
+      foreach ($this->kind as $value) {
+        if (!$value->isInitialized()) return 'kind';
+      }
+      if (isset($this->filter) && (!$this->filter->isInitialized())) return 'filter';
+      foreach ($this->order as $value) {
+        if (!$value->isInitialized()) return 'order';
+      }
+      foreach ($this->group_by as $value) {
+        if (!$value->isInitialized()) return 'group_by';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      foreach ($x->getProjectionList() as $v) {
+        $this->addProjection()->copyFrom($v);
+      }
+      foreach ($x->getKindList() as $v) {
+        $this->addKind()->copyFrom($v);
+      }
+      if ($x->hasFilter()) {
+        $this->mutableFilter()->mergeFrom($x->getFilter());
+      }
+      foreach ($x->getOrderList() as $v) {
+        $this->addOrder()->copyFrom($v);
+      }
+      foreach ($x->getGroupByList() as $v) {
+        $this->addGroupBy()->copyFrom($v);
+      }
+      if ($x->hasStartCursor()) {
+        $this->setStartCursor($x->getStartCursor());
+      }
+      if ($x->hasEndCursor()) {
+        $this->setEndCursor($x->getEndCursor());
+      }
+      if ($x->hasOffset()) {
+        $this->setOffset($x->getOffset());
+      }
+      if ($x->hasLimit()) {
+        $this->setLimit($x->getLimit());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (sizeof($this->projection) !== sizeof($x->projection)) return false;
+      foreach (array_map(null, $this->projection, $x->projection) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->kind) !== sizeof($x->kind)) return false;
+      foreach (array_map(null, $this->kind, $x->kind) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (isset($this->filter) !== isset($x->filter)) return false;
+      if (isset($this->filter) && !$this->filter->equals($x->filter)) return false;
+      if (sizeof($this->order) !== sizeof($x->order)) return false;
+      foreach (array_map(null, $this->order, $x->order) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->group_by) !== sizeof($x->group_by)) return false;
+      foreach (array_map(null, $this->group_by, $x->group_by) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (isset($this->start_cursor) !== isset($x->start_cursor)) return false;
+      if (isset($this->start_cursor) && $this->start_cursor !== $x->start_cursor) return false;
+      if (isset($this->end_cursor) !== isset($x->end_cursor)) return false;
+      if (isset($this->end_cursor) && $this->end_cursor !== $x->end_cursor) return false;
+      if (isset($this->offset) !== isset($x->offset)) return false;
+      if (isset($this->offset) && !$this->integerEquals($this->offset, $x->offset)) return false;
+      if (isset($this->limit) !== isset($x->limit)) return false;
+      if (isset($this->limit) && !$this->integerEquals($this->limit, $x->limit)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      foreach ($this->projection as $value) {
+        $res .= $prefix . "projection <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->kind as $value) {
+        $res .= $prefix . "kind <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->filter)) {
+        $res .= $prefix . "filter <\n" . $this->filter->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->order as $value) {
+        $res .= $prefix . "order <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->group_by as $value) {
+        $res .= $prefix . "group_by <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->start_cursor)) {
+        $res .= $prefix . "start_cursor: " . $this->debugFormatString($this->start_cursor) . "\n";
+      }
+      if (isset($this->end_cursor)) {
+        $res .= $prefix . "end_cursor: " . $this->debugFormatString($this->end_cursor) . "\n";
+      }
+      if (isset($this->offset)) {
+        $res .= $prefix . "offset: " . $this->debugFormatInt32($this->offset) . "\n";
+      }
+      if (isset($this->limit)) {
+        $res .= $prefix . "limit: " . $this->debugFormatInt32($this->limit) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class KindExpression extends \google\net\ProtocolMessage {
+    public function getName() {
+      if (!isset($this->name)) {
+        return '';
+      }
+      return $this->name;
+    }
+    public function setName($val) {
+      $this->name = $val;
+      return $this;
+    }
+    public function clearName() {
+      unset($this->name);
+      return $this;
+    }
+    public function hasName() {
+      return isset($this->name);
+    }
+    public function clear() {
+      $this->clearName();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->name)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->name));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->name)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->name);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setName(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->name)) return 'name';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasName()) {
+        $this->setName($x->getName());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->name) !== isset($x->name)) return false;
+      if (isset($this->name) && $this->name !== $x->name) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->name)) {
+        $res .= $prefix . "name: " . $this->debugFormatString($this->name) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class PropertyReference extends \google\net\ProtocolMessage {
+    public function getName() {
+      if (!isset($this->name)) {
+        return '';
+      }
+      return $this->name;
+    }
+    public function setName($val) {
+      $this->name = $val;
+      return $this;
+    }
+    public function clearName() {
+      unset($this->name);
+      return $this;
+    }
+    public function hasName() {
+      return isset($this->name);
+    }
+    public function clear() {
+      $this->clearName();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->name)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->name));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->name)) {
+        $out->putVarInt32(18);
+        $out->putPrefixedString($this->name);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 18:
+            $length = $d->getVarInt32();
+            $this->setName(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->name)) return 'name';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasName()) {
+        $this->setName($x->getName());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->name) !== isset($x->name)) return false;
+      if (isset($this->name) && $this->name !== $x->name) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->name)) {
+        $res .= $prefix . "name: " . $this->debugFormatString($this->name) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\PropertyExpression {
+  class AggregationFunction {
+    const FIRST = 1;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class PropertyExpression extends \google\net\ProtocolMessage {
+    public function getProperty() {
+      if (!isset($this->property)) {
+        return new \google\appengine\datastore\v4\PropertyReference();
+      }
+      return $this->property;
+    }
+    public function mutableProperty() {
+      if (!isset($this->property)) {
+        $res = new \google\appengine\datastore\v4\PropertyReference();
+        $this->property = $res;
+        return $res;
+      }
+      return $this->property;
+    }
+    public function clearProperty() {
+      if (isset($this->property)) {
+        unset($this->property);
+      }
+    }
+    public function hasProperty() {
+      return isset($this->property);
+    }
+    public function getAggregationFunction() {
+      if (!isset($this->aggregation_function)) {
+        return 1;
+      }
+      return $this->aggregation_function;
+    }
+    public function setAggregationFunction($val) {
+      $this->aggregation_function = $val;
+      return $this;
+    }
+    public function clearAggregationFunction() {
+      unset($this->aggregation_function);
+      return $this;
+    }
+    public function hasAggregationFunction() {
+      return isset($this->aggregation_function);
+    }
+    public function clear() {
+      $this->clearProperty();
+      $this->clearAggregationFunction();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->property)) {
+        $res += 1;
+        $res += $this->lengthString($this->property->byteSizePartial());
+      }
+      if (isset($this->aggregation_function)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->aggregation_function);
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->property)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->property->byteSizePartial());
+        $this->property->outputPartial($out);
+      }
+      if (isset($this->aggregation_function)) {
+        $out->putVarInt32(16);
+        $out->putVarInt32($this->aggregation_function);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableProperty()->tryMerge($tmp);
+            break;
+          case 16:
+            $this->setAggregationFunction($d->getVarInt32());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if ((!isset($this->property)) || (!$this->property->isInitialized())) return 'property';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasProperty()) {
+        $this->mutableProperty()->mergeFrom($x->getProperty());
+      }
+      if ($x->hasAggregationFunction()) {
+        $this->setAggregationFunction($x->getAggregationFunction());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->property) !== isset($x->property)) return false;
+      if (isset($this->property) && !$this->property->equals($x->property)) return false;
+      if (isset($this->aggregation_function) !== isset($x->aggregation_function)) return false;
+      if (isset($this->aggregation_function) && $this->aggregation_function !== $x->aggregation_function) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->property)) {
+        $res .= $prefix . "property <\n" . $this->property->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->aggregation_function)) {
+        $res .= $prefix . "aggregation_function: " . ($this->aggregation_function) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\PropertyOrder {
+  class Direction {
+    const ASCENDING = 1;
+    const DESCENDING = 2;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class PropertyOrder extends \google\net\ProtocolMessage {
+    public function getProperty() {
+      if (!isset($this->property)) {
+        return new \google\appengine\datastore\v4\PropertyReference();
+      }
+      return $this->property;
+    }
+    public function mutableProperty() {
+      if (!isset($this->property)) {
+        $res = new \google\appengine\datastore\v4\PropertyReference();
+        $this->property = $res;
+        return $res;
+      }
+      return $this->property;
+    }
+    public function clearProperty() {
+      if (isset($this->property)) {
+        unset($this->property);
+      }
+    }
+    public function hasProperty() {
+      return isset($this->property);
+    }
+    public function getDirection() {
+      if (!isset($this->direction)) {
+        return 1;
+      }
+      return $this->direction;
+    }
+    public function setDirection($val) {
+      $this->direction = $val;
+      return $this;
+    }
+    public function clearDirection() {
+      unset($this->direction);
+      return $this;
+    }
+    public function hasDirection() {
+      return isset($this->direction);
+    }
+    public function clear() {
+      $this->clearProperty();
+      $this->clearDirection();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->property)) {
+        $res += 1;
+        $res += $this->lengthString($this->property->byteSizePartial());
+      }
+      if (isset($this->direction)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->direction);
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->property)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->property->byteSizePartial());
+        $this->property->outputPartial($out);
+      }
+      if (isset($this->direction)) {
+        $out->putVarInt32(16);
+        $out->putVarInt32($this->direction);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableProperty()->tryMerge($tmp);
+            break;
+          case 16:
+            $this->setDirection($d->getVarInt32());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if ((!isset($this->property)) || (!$this->property->isInitialized())) return 'property';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasProperty()) {
+        $this->mutableProperty()->mergeFrom($x->getProperty());
+      }
+      if ($x->hasDirection()) {
+        $this->setDirection($x->getDirection());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->property) !== isset($x->property)) return false;
+      if (isset($this->property) && !$this->property->equals($x->property)) return false;
+      if (isset($this->direction) !== isset($x->direction)) return false;
+      if (isset($this->direction) && $this->direction !== $x->direction) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->property)) {
+        $res .= $prefix . "property <\n" . $this->property->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->direction)) {
+        $res .= $prefix . "direction: " . ($this->direction) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Filter extends \google\net\ProtocolMessage {
+    public function getCompositeFilter() {
+      if (!isset($this->composite_filter)) {
+        return new \google\appengine\datastore\v4\CompositeFilter();
+      }
+      return $this->composite_filter;
+    }
+    public function mutableCompositeFilter() {
+      if (!isset($this->composite_filter)) {
+        $res = new \google\appengine\datastore\v4\CompositeFilter();
+        $this->composite_filter = $res;
+        return $res;
+      }
+      return $this->composite_filter;
+    }
+    public function clearCompositeFilter() {
+      if (isset($this->composite_filter)) {
+        unset($this->composite_filter);
+      }
+    }
+    public function hasCompositeFilter() {
+      return isset($this->composite_filter);
+    }
+    public function getPropertyFilter() {
+      if (!isset($this->property_filter)) {
+        return new \google\appengine\datastore\v4\PropertyFilter();
+      }
+      return $this->property_filter;
+    }
+    public function mutablePropertyFilter() {
+      if (!isset($this->property_filter)) {
+        $res = new \google\appengine\datastore\v4\PropertyFilter();
+        $this->property_filter = $res;
+        return $res;
+      }
+      return $this->property_filter;
+    }
+    public function clearPropertyFilter() {
+      if (isset($this->property_filter)) {
+        unset($this->property_filter);
+      }
+    }
+    public function hasPropertyFilter() {
+      return isset($this->property_filter);
+    }
+    public function clear() {
+      $this->clearCompositeFilter();
+      $this->clearPropertyFilter();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->composite_filter)) {
+        $res += 1;
+        $res += $this->lengthString($this->composite_filter->byteSizePartial());
+      }
+      if (isset($this->property_filter)) {
+        $res += 1;
+        $res += $this->lengthString($this->property_filter->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->composite_filter)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->composite_filter->byteSizePartial());
+        $this->composite_filter->outputPartial($out);
+      }
+      if (isset($this->property_filter)) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($this->property_filter->byteSizePartial());
+        $this->property_filter->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableCompositeFilter()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutablePropertyFilter()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->composite_filter) && (!$this->composite_filter->isInitialized())) return 'composite_filter';
+      if (isset($this->property_filter) && (!$this->property_filter->isInitialized())) return 'property_filter';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasCompositeFilter()) {
+        $this->mutableCompositeFilter()->mergeFrom($x->getCompositeFilter());
+      }
+      if ($x->hasPropertyFilter()) {
+        $this->mutablePropertyFilter()->mergeFrom($x->getPropertyFilter());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->composite_filter) !== isset($x->composite_filter)) return false;
+      if (isset($this->composite_filter) && !$this->composite_filter->equals($x->composite_filter)) return false;
+      if (isset($this->property_filter) !== isset($x->property_filter)) return false;
+      if (isset($this->property_filter) && !$this->property_filter->equals($x->property_filter)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->composite_filter)) {
+        $res .= $prefix . "composite_filter <\n" . $this->composite_filter->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->property_filter)) {
+        $res .= $prefix . "property_filter <\n" . $this->property_filter->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\CompositeFilter {
+  class Operator {
+    const AND_ = 1;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class CompositeFilter extends \google\net\ProtocolMessage {
+    private $filter = array();
+    public function getOperator() {
+      if (!isset($this->operator)) {
+        return 1;
+      }
+      return $this->operator;
+    }
+    public function setOperator($val) {
+      $this->operator = $val;
+      return $this;
+    }
+    public function clearOperator() {
+      unset($this->operator);
+      return $this;
+    }
+    public function hasOperator() {
+      return isset($this->operator);
+    }
+    public function getFilterSize() {
+      return sizeof($this->filter);
+    }
+    public function getFilterList() {
+      return $this->filter;
+    }
+    public function mutableFilter($idx) {
+      if (!isset($this->filter[$idx])) {
+        $val = new \google\appengine\datastore\v4\Filter();
+        $this->filter[$idx] = $val;
+        return $val;
+      }
+      return $this->filter[$idx];
+    }
+    public function getFilter($idx) {
+      if (isset($this->filter[$idx])) {
+        return $this->filter[$idx];
+      }
+      if ($idx >= end(array_keys($this->filter))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Filter();
+    }
+    public function addFilter() {
+      $val = new \google\appengine\datastore\v4\Filter();
+      $this->filter[] = $val;
+      return $val;
+    }
+    public function clearFilter() {
+      $this->filter = array();
+    }
+    public function clear() {
+      $this->clearOperator();
+      $this->clearFilter();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->operator)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->operator);
+      }
+      $this->checkProtoArray($this->filter);
+      $res += 1 * sizeof($this->filter);
+      foreach ($this->filter as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->operator)) {
+        $out->putVarInt32(8);
+        $out->putVarInt32($this->operator);
+      }
+      $this->checkProtoArray($this->filter);
+      foreach ($this->filter as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 8:
+            $this->setOperator($d->getVarInt32());
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addFilter()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->operator)) return 'operator';
+      foreach ($this->filter as $value) {
+        if (!$value->isInitialized()) return 'filter';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasOperator()) {
+        $this->setOperator($x->getOperator());
+      }
+      foreach ($x->getFilterList() as $v) {
+        $this->addFilter()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->operator) !== isset($x->operator)) return false;
+      if (isset($this->operator) && $this->operator !== $x->operator) return false;
+      if (sizeof($this->filter) !== sizeof($x->filter)) return false;
+      foreach (array_map(null, $this->filter, $x->filter) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->operator)) {
+        $res .= $prefix . "operator: " . ($this->operator) . "\n";
+      }
+      foreach ($this->filter as $value) {
+        $res .= $prefix . "filter <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\PropertyFilter {
+  class Operator {
+    const LESS_THAN = 1;
+    const LESS_THAN_OR_EQUAL = 2;
+    const GREATER_THAN = 3;
+    const GREATER_THAN_OR_EQUAL = 4;
+    const EQUAL = 5;
+    const HAS_ANCESTOR = 11;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class PropertyFilter extends \google\net\ProtocolMessage {
+    public function getProperty() {
+      if (!isset($this->property)) {
+        return new \google\appengine\datastore\v4\PropertyReference();
+      }
+      return $this->property;
+    }
+    public function mutableProperty() {
+      if (!isset($this->property)) {
+        $res = new \google\appengine\datastore\v4\PropertyReference();
+        $this->property = $res;
+        return $res;
+      }
+      return $this->property;
+    }
+    public function clearProperty() {
+      if (isset($this->property)) {
+        unset($this->property);
+      }
+    }
+    public function hasProperty() {
+      return isset($this->property);
+    }
+    public function getOperator() {
+      if (!isset($this->operator)) {
+        return 1;
+      }
+      return $this->operator;
+    }
+    public function setOperator($val) {
+      $this->operator = $val;
+      return $this;
+    }
+    public function clearOperator() {
+      unset($this->operator);
+      return $this;
+    }
+    public function hasOperator() {
+      return isset($this->operator);
+    }
+    public function getValue() {
+      if (!isset($this->value)) {
+        return new \google\appengine\datastore\v4\Value();
+      }
+      return $this->value;
+    }
+    public function mutableValue() {
+      if (!isset($this->value)) {
+        $res = new \google\appengine\datastore\v4\Value();
+        $this->value = $res;
+        return $res;
+      }
+      return $this->value;
+    }
+    public function clearValue() {
+      if (isset($this->value)) {
+        unset($this->value);
+      }
+    }
+    public function hasValue() {
+      return isset($this->value);
+    }
+    public function clear() {
+      $this->clearProperty();
+      $this->clearOperator();
+      $this->clearValue();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->property)) {
+        $res += 1;
+        $res += $this->lengthString($this->property->byteSizePartial());
+      }
+      if (isset($this->operator)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->operator);
+      }
+      if (isset($this->value)) {
+        $res += 1;
+        $res += $this->lengthString($this->value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->property)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->property->byteSizePartial());
+        $this->property->outputPartial($out);
+      }
+      if (isset($this->operator)) {
+        $out->putVarInt32(16);
+        $out->putVarInt32($this->operator);
+      }
+      if (isset($this->value)) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($this->value->byteSizePartial());
+        $this->value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableProperty()->tryMerge($tmp);
+            break;
+          case 16:
+            $this->setOperator($d->getVarInt32());
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableValue()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if ((!isset($this->property)) || (!$this->property->isInitialized())) return 'property';
+      if (!isset($this->operator)) return 'operator';
+      if ((!isset($this->value)) || (!$this->value->isInitialized())) return 'value';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasProperty()) {
+        $this->mutableProperty()->mergeFrom($x->getProperty());
+      }
+      if ($x->hasOperator()) {
+        $this->setOperator($x->getOperator());
+      }
+      if ($x->hasValue()) {
+        $this->mutableValue()->mergeFrom($x->getValue());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->property) !== isset($x->property)) return false;
+      if (isset($this->property) && !$this->property->equals($x->property)) return false;
+      if (isset($this->operator) !== isset($x->operator)) return false;
+      if (isset($this->operator) && $this->operator !== $x->operator) return false;
+      if (isset($this->value) !== isset($x->value)) return false;
+      if (isset($this->value) && !$this->value->equals($x->value)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->property)) {
+        $res .= $prefix . "property <\n" . $this->property->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->operator)) {
+        $res .= $prefix . "operator: " . ($this->operator) . "\n";
+      }
+      if (isset($this->value)) {
+        $res .= $prefix . "value <\n" . $this->value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class GqlQuery extends \google\net\ProtocolMessage {
+    private $name_arg = array();
+    private $number_arg = array();
+    public function getQueryString() {
+      if (!isset($this->query_string)) {
+        return '';
+      }
+      return $this->query_string;
+    }
+    public function setQueryString($val) {
+      $this->query_string = $val;
+      return $this;
+    }
+    public function clearQueryString() {
+      unset($this->query_string);
+      return $this;
+    }
+    public function hasQueryString() {
+      return isset($this->query_string);
+    }
+    public function getAllowLiteral() {
+      if (!isset($this->allow_literal)) {
+        return false;
+      }
+      return $this->allow_literal;
+    }
+    public function setAllowLiteral($val) {
+      $this->allow_literal = $val;
+      return $this;
+    }
+    public function clearAllowLiteral() {
+      unset($this->allow_literal);
+      return $this;
+    }
+    public function hasAllowLiteral() {
+      return isset($this->allow_literal);
+    }
+    public function getNameArgSize() {
+      return sizeof($this->name_arg);
+    }
+    public function getNameArgList() {
+      return $this->name_arg;
+    }
+    public function mutableNameArg($idx) {
+      if (!isset($this->name_arg[$idx])) {
+        $val = new \google\appengine\datastore\v4\GqlQueryArg();
+        $this->name_arg[$idx] = $val;
+        return $val;
+      }
+      return $this->name_arg[$idx];
+    }
+    public function getNameArg($idx) {
+      if (isset($this->name_arg[$idx])) {
+        return $this->name_arg[$idx];
+      }
+      if ($idx >= end(array_keys($this->name_arg))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\GqlQueryArg();
+    }
+    public function addNameArg() {
+      $val = new \google\appengine\datastore\v4\GqlQueryArg();
+      $this->name_arg[] = $val;
+      return $val;
+    }
+    public function clearNameArg() {
+      $this->name_arg = array();
+    }
+    public function getNumberArgSize() {
+      return sizeof($this->number_arg);
+    }
+    public function getNumberArgList() {
+      return $this->number_arg;
+    }
+    public function mutableNumberArg($idx) {
+      if (!isset($this->number_arg[$idx])) {
+        $val = new \google\appengine\datastore\v4\GqlQueryArg();
+        $this->number_arg[$idx] = $val;
+        return $val;
+      }
+      return $this->number_arg[$idx];
+    }
+    public function getNumberArg($idx) {
+      if (isset($this->number_arg[$idx])) {
+        return $this->number_arg[$idx];
+      }
+      if ($idx >= end(array_keys($this->number_arg))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\GqlQueryArg();
+    }
+    public function addNumberArg() {
+      $val = new \google\appengine\datastore\v4\GqlQueryArg();
+      $this->number_arg[] = $val;
+      return $val;
+    }
+    public function clearNumberArg() {
+      $this->number_arg = array();
+    }
+    public function clear() {
+      $this->clearQueryString();
+      $this->clearAllowLiteral();
+      $this->clearNameArg();
+      $this->clearNumberArg();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->query_string)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->query_string));
+      }
+      if (isset($this->allow_literal)) {
+        $res += 2;
+      }
+      $this->checkProtoArray($this->name_arg);
+      $res += 1 * sizeof($this->name_arg);
+      foreach ($this->name_arg as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->number_arg);
+      $res += 1 * sizeof($this->number_arg);
+      foreach ($this->number_arg as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->query_string)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->query_string);
+      }
+      if (isset($this->allow_literal)) {
+        $out->putVarInt32(16);
+        $out->putBoolean($this->allow_literal);
+      }
+      $this->checkProtoArray($this->name_arg);
+      foreach ($this->name_arg as $value) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->number_arg);
+      foreach ($this->number_arg as $value) {
+        $out->putVarInt32(34);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setQueryString(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 16:
+            $this->setAllowLiteral($d->getBoolean());
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addNameArg()->tryMerge($tmp);
+            break;
+          case 34:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addNumberArg()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->query_string)) return 'query_string';
+      foreach ($this->name_arg as $value) {
+        if (!$value->isInitialized()) return 'name_arg';
+      }
+      foreach ($this->number_arg as $value) {
+        if (!$value->isInitialized()) return 'number_arg';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasQueryString()) {
+        $this->setQueryString($x->getQueryString());
+      }
+      if ($x->hasAllowLiteral()) {
+        $this->setAllowLiteral($x->getAllowLiteral());
+      }
+      foreach ($x->getNameArgList() as $v) {
+        $this->addNameArg()->copyFrom($v);
+      }
+      foreach ($x->getNumberArgList() as $v) {
+        $this->addNumberArg()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->query_string) !== isset($x->query_string)) return false;
+      if (isset($this->query_string) && $this->query_string !== $x->query_string) return false;
+      if (isset($this->allow_literal) !== isset($x->allow_literal)) return false;
+      if (isset($this->allow_literal) && $this->allow_literal !== $x->allow_literal) return false;
+      if (sizeof($this->name_arg) !== sizeof($x->name_arg)) return false;
+      foreach (array_map(null, $this->name_arg, $x->name_arg) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->number_arg) !== sizeof($x->number_arg)) return false;
+      foreach (array_map(null, $this->number_arg, $x->number_arg) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->query_string)) {
+        $res .= $prefix . "query_string: " . $this->debugFormatString($this->query_string) . "\n";
+      }
+      if (isset($this->allow_literal)) {
+        $res .= $prefix . "allow_literal: " . $this->debugFormatBool($this->allow_literal) . "\n";
+      }
+      foreach ($this->name_arg as $value) {
+        $res .= $prefix . "name_arg <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->number_arg as $value) {
+        $res .= $prefix . "number_arg <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class GqlQueryArg extends \google\net\ProtocolMessage {
+    public function getName() {
+      if (!isset($this->name)) {
+        return '';
+      }
+      return $this->name;
+    }
+    public function setName($val) {
+      $this->name = $val;
+      return $this;
+    }
+    public function clearName() {
+      unset($this->name);
+      return $this;
+    }
+    public function hasName() {
+      return isset($this->name);
+    }
+    public function getValue() {
+      if (!isset($this->value)) {
+        return new \google\appengine\datastore\v4\Value();
+      }
+      return $this->value;
+    }
+    public function mutableValue() {
+      if (!isset($this->value)) {
+        $res = new \google\appengine\datastore\v4\Value();
+        $this->value = $res;
+        return $res;
+      }
+      return $this->value;
+    }
+    public function clearValue() {
+      if (isset($this->value)) {
+        unset($this->value);
+      }
+    }
+    public function hasValue() {
+      return isset($this->value);
+    }
+    public function getCursor() {
+      if (!isset($this->cursor)) {
+        return '';
+      }
+      return $this->cursor;
+    }
+    public function setCursor($val) {
+      $this->cursor = $val;
+      return $this;
+    }
+    public function clearCursor() {
+      unset($this->cursor);
+      return $this;
+    }
+    public function hasCursor() {
+      return isset($this->cursor);
+    }
+    public function clear() {
+      $this->clearName();
+      $this->clearValue();
+      $this->clearCursor();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->name)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->name));
+      }
+      if (isset($this->value)) {
+        $res += 1;
+        $res += $this->lengthString($this->value->byteSizePartial());
+      }
+      if (isset($this->cursor)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->cursor));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->name)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->name);
+      }
+      if (isset($this->value)) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($this->value->byteSizePartial());
+        $this->value->outputPartial($out);
+      }
+      if (isset($this->cursor)) {
+        $out->putVarInt32(26);
+        $out->putPrefixedString($this->cursor);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setName(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableValue()->tryMerge($tmp);
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $this->setCursor(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->value) && (!$this->value->isInitialized())) return 'value';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasName()) {
+        $this->setName($x->getName());
+      }
+      if ($x->hasValue()) {
+        $this->mutableValue()->mergeFrom($x->getValue());
+      }
+      if ($x->hasCursor()) {
+        $this->setCursor($x->getCursor());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->name) !== isset($x->name)) return false;
+      if (isset($this->name) && $this->name !== $x->name) return false;
+      if (isset($this->value) !== isset($x->value)) return false;
+      if (isset($this->value) && !$this->value->equals($x->value)) return false;
+      if (isset($this->cursor) !== isset($x->cursor)) return false;
+      if (isset($this->cursor) && $this->cursor !== $x->cursor) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->name)) {
+        $res .= $prefix . "name: " . $this->debugFormatString($this->name) . "\n";
+      }
+      if (isset($this->value)) {
+        $res .= $prefix . "value <\n" . $this->value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->cursor)) {
+        $res .= $prefix . "cursor: " . $this->debugFormatString($this->cursor) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\QueryResultBatch {
+  class MoreResultsType {
+    const NOT_FINISHED = 1;
+    const MORE_RESULTS_AFTER_LIMIT = 2;
+    const NO_MORE_RESULTS = 3;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class QueryResultBatch extends \google\net\ProtocolMessage {
+    private $entity_result = array();
+    public function getEntityResultType() {
+      if (!isset($this->entity_result_type)) {
+        return 1;
+      }
+      return $this->entity_result_type;
+    }
+    public function setEntityResultType($val) {
+      $this->entity_result_type = $val;
+      return $this;
+    }
+    public function clearEntityResultType() {
+      unset($this->entity_result_type);
+      return $this;
+    }
+    public function hasEntityResultType() {
+      return isset($this->entity_result_type);
+    }
+    public function getEntityResultSize() {
+      return sizeof($this->entity_result);
+    }
+    public function getEntityResultList() {
+      return $this->entity_result;
+    }
+    public function mutableEntityResult($idx) {
+      if (!isset($this->entity_result[$idx])) {
+        $val = new \google\appengine\datastore\v4\EntityResult();
+        $this->entity_result[$idx] = $val;
+        return $val;
+      }
+      return $this->entity_result[$idx];
+    }
+    public function getEntityResult($idx) {
+      if (isset($this->entity_result[$idx])) {
+        return $this->entity_result[$idx];
+      }
+      if ($idx >= end(array_keys($this->entity_result))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\EntityResult();
+    }
+    public function addEntityResult() {
+      $val = new \google\appengine\datastore\v4\EntityResult();
+      $this->entity_result[] = $val;
+      return $val;
+    }
+    public function clearEntityResult() {
+      $this->entity_result = array();
+    }
+    public function getEndCursor() {
+      if (!isset($this->end_cursor)) {
+        return '';
+      }
+      return $this->end_cursor;
+    }
+    public function setEndCursor($val) {
+      $this->end_cursor = $val;
+      return $this;
+    }
+    public function clearEndCursor() {
+      unset($this->end_cursor);
+      return $this;
+    }
+    public function hasEndCursor() {
+      return isset($this->end_cursor);
+    }
+    public function getMoreResults() {
+      if (!isset($this->more_results)) {
+        return 1;
+      }
+      return $this->more_results;
+    }
+    public function setMoreResults($val) {
+      $this->more_results = $val;
+      return $this;
+    }
+    public function clearMoreResults() {
+      unset($this->more_results);
+      return $this;
+    }
+    public function hasMoreResults() {
+      return isset($this->more_results);
+    }
+    public function getSkippedResults() {
+      if (!isset($this->skipped_results)) {
+        return 0;
+      }
+      return $this->skipped_results;
+    }
+    public function setSkippedResults($val) {
+      $this->skipped_results = $val;
+      return $this;
+    }
+    public function clearSkippedResults() {
+      unset($this->skipped_results);
+      return $this;
+    }
+    public function hasSkippedResults() {
+      return isset($this->skipped_results);
+    }
+    public function clear() {
+      $this->clearEntityResultType();
+      $this->clearEntityResult();
+      $this->clearEndCursor();
+      $this->clearMoreResults();
+      $this->clearSkippedResults();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->entity_result_type)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->entity_result_type);
+      }
+      $this->checkProtoArray($this->entity_result);
+      $res += 1 * sizeof($this->entity_result);
+      foreach ($this->entity_result as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      if (isset($this->end_cursor)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->end_cursor));
+      }
+      if (isset($this->more_results)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->more_results);
+      }
+      if (isset($this->skipped_results)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->skipped_results);
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->entity_result_type)) {
+        $out->putVarInt32(8);
+        $out->putVarInt32($this->entity_result_type);
+      }
+      $this->checkProtoArray($this->entity_result);
+      foreach ($this->entity_result as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      if (isset($this->end_cursor)) {
+        $out->putVarInt32(34);
+        $out->putPrefixedString($this->end_cursor);
+      }
+      if (isset($this->more_results)) {
+        $out->putVarInt32(40);
+        $out->putVarInt32($this->more_results);
+      }
+      if (isset($this->skipped_results)) {
+        $out->putVarInt32(48);
+        $out->putVarInt32($this->skipped_results);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 8:
+            $this->setEntityResultType($d->getVarInt32());
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addEntityResult()->tryMerge($tmp);
+            break;
+          case 34:
+            $length = $d->getVarInt32();
+            $this->setEndCursor(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 40:
+            $this->setMoreResults($d->getVarInt32());
+            break;
+          case 48:
+            $this->setSkippedResults($d->getVarInt32());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->entity_result_type)) return 'entity_result_type';
+      foreach ($this->entity_result as $value) {
+        if (!$value->isInitialized()) return 'entity_result';
+      }
+      if (!isset($this->more_results)) return 'more_results';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasEntityResultType()) {
+        $this->setEntityResultType($x->getEntityResultType());
+      }
+      foreach ($x->getEntityResultList() as $v) {
+        $this->addEntityResult()->copyFrom($v);
+      }
+      if ($x->hasEndCursor()) {
+        $this->setEndCursor($x->getEndCursor());
+      }
+      if ($x->hasMoreResults()) {
+        $this->setMoreResults($x->getMoreResults());
+      }
+      if ($x->hasSkippedResults()) {
+        $this->setSkippedResults($x->getSkippedResults());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->entity_result_type) !== isset($x->entity_result_type)) return false;
+      if (isset($this->entity_result_type) && $this->entity_result_type !== $x->entity_result_type) return false;
+      if (sizeof($this->entity_result) !== sizeof($x->entity_result)) return false;
+      foreach (array_map(null, $this->entity_result, $x->entity_result) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (isset($this->end_cursor) !== isset($x->end_cursor)) return false;
+      if (isset($this->end_cursor) && $this->end_cursor !== $x->end_cursor) return false;
+      if (isset($this->more_results) !== isset($x->more_results)) return false;
+      if (isset($this->more_results) && $this->more_results !== $x->more_results) return false;
+      if (isset($this->skipped_results) !== isset($x->skipped_results)) return false;
+      if (isset($this->skipped_results) && !$this->integerEquals($this->skipped_results, $x->skipped_results)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->entity_result_type)) {
+        $res .= $prefix . "entity_result_type: " . ($this->entity_result_type) . "\n";
+      }
+      foreach ($this->entity_result as $value) {
+        $res .= $prefix . "entity_result <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->end_cursor)) {
+        $res .= $prefix . "end_cursor: " . $this->debugFormatString($this->end_cursor) . "\n";
+      }
+      if (isset($this->more_results)) {
+        $res .= $prefix . "more_results: " . ($this->more_results) . "\n";
+      }
+      if (isset($this->skipped_results)) {
+        $res .= $prefix . "skipped_results: " . $this->debugFormatInt32($this->skipped_results) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Mutation extends \google\net\ProtocolMessage {
+    private $upsert = array();
+    private $update = array();
+    private $insert = array();
+    private $insert_auto_id = array();
+    private $delete = array();
+    public function getUpsertSize() {
+      return sizeof($this->upsert);
+    }
+    public function getUpsertList() {
+      return $this->upsert;
+    }
+    public function mutableUpsert($idx) {
+      if (!isset($this->upsert[$idx])) {
+        $val = new \google\appengine\datastore\v4\Entity();
+        $this->upsert[$idx] = $val;
+        return $val;
+      }
+      return $this->upsert[$idx];
+    }
+    public function getUpsert($idx) {
+      if (isset($this->upsert[$idx])) {
+        return $this->upsert[$idx];
+      }
+      if ($idx >= end(array_keys($this->upsert))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Entity();
+    }
+    public function addUpsert() {
+      $val = new \google\appengine\datastore\v4\Entity();
+      $this->upsert[] = $val;
+      return $val;
+    }
+    public function clearUpsert() {
+      $this->upsert = array();
+    }
+    public function getUpdateSize() {
+      return sizeof($this->update);
+    }
+    public function getUpdateList() {
+      return $this->update;
+    }
+    public function mutableUpdate($idx) {
+      if (!isset($this->update[$idx])) {
+        $val = new \google\appengine\datastore\v4\Entity();
+        $this->update[$idx] = $val;
+        return $val;
+      }
+      return $this->update[$idx];
+    }
+    public function getUpdate($idx) {
+      if (isset($this->update[$idx])) {
+        return $this->update[$idx];
+      }
+      if ($idx >= end(array_keys($this->update))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Entity();
+    }
+    public function addUpdate() {
+      $val = new \google\appengine\datastore\v4\Entity();
+      $this->update[] = $val;
+      return $val;
+    }
+    public function clearUpdate() {
+      $this->update = array();
+    }
+    public function getInsertSize() {
+      return sizeof($this->insert);
+    }
+    public function getInsertList() {
+      return $this->insert;
+    }
+    public function mutableInsert($idx) {
+      if (!isset($this->insert[$idx])) {
+        $val = new \google\appengine\datastore\v4\Entity();
+        $this->insert[$idx] = $val;
+        return $val;
+      }
+      return $this->insert[$idx];
+    }
+    public function getInsert($idx) {
+      if (isset($this->insert[$idx])) {
+        return $this->insert[$idx];
+      }
+      if ($idx >= end(array_keys($this->insert))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Entity();
+    }
+    public function addInsert() {
+      $val = new \google\appengine\datastore\v4\Entity();
+      $this->insert[] = $val;
+      return $val;
+    }
+    public function clearInsert() {
+      $this->insert = array();
+    }
+    public function getInsertAutoIdSize() {
+      return sizeof($this->insert_auto_id);
+    }
+    public function getInsertAutoIdList() {
+      return $this->insert_auto_id;
+    }
+    public function mutableInsertAutoId($idx) {
+      if (!isset($this->insert_auto_id[$idx])) {
+        $val = new \google\appengine\datastore\v4\Entity();
+        $this->insert_auto_id[$idx] = $val;
+        return $val;
+      }
+      return $this->insert_auto_id[$idx];
+    }
+    public function getInsertAutoId($idx) {
+      if (isset($this->insert_auto_id[$idx])) {
+        return $this->insert_auto_id[$idx];
+      }
+      if ($idx >= end(array_keys($this->insert_auto_id))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Entity();
+    }
+    public function addInsertAutoId() {
+      $val = new \google\appengine\datastore\v4\Entity();
+      $this->insert_auto_id[] = $val;
+      return $val;
+    }
+    public function clearInsertAutoId() {
+      $this->insert_auto_id = array();
+    }
+    public function getDeleteSize() {
+      return sizeof($this->delete);
+    }
+    public function getDeleteList() {
+      return $this->delete;
+    }
+    public function mutableDelete($idx) {
+      if (!isset($this->delete[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key();
+        $this->delete[$idx] = $val;
+        return $val;
+      }
+      return $this->delete[$idx];
+    }
+    public function getDelete($idx) {
+      if (isset($this->delete[$idx])) {
+        return $this->delete[$idx];
+      }
+      if ($idx >= end(array_keys($this->delete))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key();
+    }
+    public function addDelete() {
+      $val = new \google\appengine\datastore\v4\Key();
+      $this->delete[] = $val;
+      return $val;
+    }
+    public function clearDelete() {
+      $this->delete = array();
+    }
+    public function getForce() {
+      if (!isset($this->force)) {
+        return false;
+      }
+      return $this->force;
+    }
+    public function setForce($val) {
+      $this->force = $val;
+      return $this;
+    }
+    public function clearForce() {
+      unset($this->force);
+      return $this;
+    }
+    public function hasForce() {
+      return isset($this->force);
+    }
+    public function clear() {
+      $this->clearUpsert();
+      $this->clearUpdate();
+      $this->clearInsert();
+      $this->clearInsertAutoId();
+      $this->clearDelete();
+      $this->clearForce();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      $this->checkProtoArray($this->upsert);
+      $res += 1 * sizeof($this->upsert);
+      foreach ($this->upsert as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->update);
+      $res += 1 * sizeof($this->update);
+      foreach ($this->update as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->insert);
+      $res += 1 * sizeof($this->insert);
+      foreach ($this->insert as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->insert_auto_id);
+      $res += 1 * sizeof($this->insert_auto_id);
+      foreach ($this->insert_auto_id as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->delete);
+      $res += 1 * sizeof($this->delete);
+      foreach ($this->delete as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      if (isset($this->force)) {
+        $res += 2;
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      $this->checkProtoArray($this->upsert);
+      foreach ($this->upsert as $value) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->update);
+      foreach ($this->update as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->insert);
+      foreach ($this->insert as $value) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->insert_auto_id);
+      foreach ($this->insert_auto_id as $value) {
+        $out->putVarInt32(34);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->delete);
+      foreach ($this->delete as $value) {
+        $out->putVarInt32(42);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      if (isset($this->force)) {
+        $out->putVarInt32(48);
+        $out->putBoolean($this->force);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addUpsert()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addUpdate()->tryMerge($tmp);
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addInsert()->tryMerge($tmp);
+            break;
+          case 34:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addInsertAutoId()->tryMerge($tmp);
+            break;
+          case 42:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addDelete()->tryMerge($tmp);
+            break;
+          case 48:
+            $this->setForce($d->getBoolean());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      foreach ($this->upsert as $value) {
+        if (!$value->isInitialized()) return 'upsert';
+      }
+      foreach ($this->update as $value) {
+        if (!$value->isInitialized()) return 'update';
+      }
+      foreach ($this->insert as $value) {
+        if (!$value->isInitialized()) return 'insert';
+      }
+      foreach ($this->insert_auto_id as $value) {
+        if (!$value->isInitialized()) return 'insert_auto_id';
+      }
+      foreach ($this->delete as $value) {
+        if (!$value->isInitialized()) return 'delete';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      foreach ($x->getUpsertList() as $v) {
+        $this->addUpsert()->copyFrom($v);
+      }
+      foreach ($x->getUpdateList() as $v) {
+        $this->addUpdate()->copyFrom($v);
+      }
+      foreach ($x->getInsertList() as $v) {
+        $this->addInsert()->copyFrom($v);
+      }
+      foreach ($x->getInsertAutoIdList() as $v) {
+        $this->addInsertAutoId()->copyFrom($v);
+      }
+      foreach ($x->getDeleteList() as $v) {
+        $this->addDelete()->copyFrom($v);
+      }
+      if ($x->hasForce()) {
+        $this->setForce($x->getForce());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (sizeof($this->upsert) !== sizeof($x->upsert)) return false;
+      foreach (array_map(null, $this->upsert, $x->upsert) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->update) !== sizeof($x->update)) return false;
+      foreach (array_map(null, $this->update, $x->update) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->insert) !== sizeof($x->insert)) return false;
+      foreach (array_map(null, $this->insert, $x->insert) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->insert_auto_id) !== sizeof($x->insert_auto_id)) return false;
+      foreach (array_map(null, $this->insert_auto_id, $x->insert_auto_id) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->delete) !== sizeof($x->delete)) return false;
+      foreach (array_map(null, $this->delete, $x->delete) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (isset($this->force) !== isset($x->force)) return false;
+      if (isset($this->force) && $this->force !== $x->force) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      foreach ($this->upsert as $value) {
+        $res .= $prefix . "upsert <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->update as $value) {
+        $res .= $prefix . "update <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->insert as $value) {
+        $res .= $prefix . "insert <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->insert_auto_id as $value) {
+        $res .= $prefix . "insert_auto_id <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->delete as $value) {
+        $res .= $prefix . "delete <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->force)) {
+        $res .= $prefix . "force: " . $this->debugFormatBool($this->force) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class MutationResult extends \google\net\ProtocolMessage {
+    private $insert_auto_id_key = array();
+    private $upsert_version = array();
+    private $update_version = array();
+    private $insert_version = array();
+    private $insert_auto_id_version = array();
+    private $delete_version = array();
+    public function getIndexUpdates() {
+      if (!isset($this->index_updates)) {
+        return 0;
+      }
+      return $this->index_updates;
+    }
+    public function setIndexUpdates($val) {
+      $this->index_updates = $val;
+      return $this;
+    }
+    public function clearIndexUpdates() {
+      unset($this->index_updates);
+      return $this;
+    }
+    public function hasIndexUpdates() {
+      return isset($this->index_updates);
+    }
+    public function getInsertAutoIdKeySize() {
+      return sizeof($this->insert_auto_id_key);
+    }
+    public function getInsertAutoIdKeyList() {
+      return $this->insert_auto_id_key;
+    }
+    public function mutableInsertAutoIdKey($idx) {
+      if (!isset($this->insert_auto_id_key[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key();
+        $this->insert_auto_id_key[$idx] = $val;
+        return $val;
+      }
+      return $this->insert_auto_id_key[$idx];
+    }
+    public function getInsertAutoIdKey($idx) {
+      if (isset($this->insert_auto_id_key[$idx])) {
+        return $this->insert_auto_id_key[$idx];
+      }
+      if ($idx >= end(array_keys($this->insert_auto_id_key))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key();
+    }
+    public function addInsertAutoIdKey() {
+      $val = new \google\appengine\datastore\v4\Key();
+      $this->insert_auto_id_key[] = $val;
+      return $val;
+    }
+    public function clearInsertAutoIdKey() {
+      $this->insert_auto_id_key = array();
+    }
+    public function getUpsertVersionSize() {
+      return sizeof($this->upsert_version);
+    }
+    public function getUpsertVersionList() {
+      return $this->upsert_version;
+    }
+    public function getUpsertVersion($idx) {
+      return $this->upsert_version[$idx];
+    }
+    public function setUpsertVersion($idx, $val) {
+      if (is_double($val)) {
+        $this->upsert_version[$idx] = sprintf('%0.0F', $val);
+      } else {
+        $this->upsert_version[$idx] = $val;
+      }
+      return $this;
+    }
+    public function addUpsertVersion($val) {
+      if (is_double($val)) {
+        $this->upsert_version[] = sprintf('%0.0F', $val);
+      } else {
+        $this->upsert_version[] = $val;
+      }
+      return $this;
+    }
+    public function clearUpsertVersion() {
+      $this->upsert_version = array();
+    }
+    public function getUpdateVersionSize() {
+      return sizeof($this->update_version);
+    }
+    public function getUpdateVersionList() {
+      return $this->update_version;
+    }
+    public function getUpdateVersion($idx) {
+      return $this->update_version[$idx];
+    }
+    public function setUpdateVersion($idx, $val) {
+      if (is_double($val)) {
+        $this->update_version[$idx] = sprintf('%0.0F', $val);
+      } else {
+        $this->update_version[$idx] = $val;
+      }
+      return $this;
+    }
+    public function addUpdateVersion($val) {
+      if (is_double($val)) {
+        $this->update_version[] = sprintf('%0.0F', $val);
+      } else {
+        $this->update_version[] = $val;
+      }
+      return $this;
+    }
+    public function clearUpdateVersion() {
+      $this->update_version = array();
+    }
+    public function getInsertVersionSize() {
+      return sizeof($this->insert_version);
+    }
+    public function getInsertVersionList() {
+      return $this->insert_version;
+    }
+    public function getInsertVersion($idx) {
+      return $this->insert_version[$idx];
+    }
+    public function setInsertVersion($idx, $val) {
+      if (is_double($val)) {
+        $this->insert_version[$idx] = sprintf('%0.0F', $val);
+      } else {
+        $this->insert_version[$idx] = $val;
+      }
+      return $this;
+    }
+    public function addInsertVersion($val) {
+      if (is_double($val)) {
+        $this->insert_version[] = sprintf('%0.0F', $val);
+      } else {
+        $this->insert_version[] = $val;
+      }
+      return $this;
+    }
+    public function clearInsertVersion() {
+      $this->insert_version = array();
+    }
+    public function getInsertAutoIdVersionSize() {
+      return sizeof($this->insert_auto_id_version);
+    }
+    public function getInsertAutoIdVersionList() {
+      return $this->insert_auto_id_version;
+    }
+    public function getInsertAutoIdVersion($idx) {
+      return $this->insert_auto_id_version[$idx];
+    }
+    public function setInsertAutoIdVersion($idx, $val) {
+      if (is_double($val)) {
+        $this->insert_auto_id_version[$idx] = sprintf('%0.0F', $val);
+      } else {
+        $this->insert_auto_id_version[$idx] = $val;
+      }
+      return $this;
+    }
+    public function addInsertAutoIdVersion($val) {
+      if (is_double($val)) {
+        $this->insert_auto_id_version[] = sprintf('%0.0F', $val);
+      } else {
+        $this->insert_auto_id_version[] = $val;
+      }
+      return $this;
+    }
+    public function clearInsertAutoIdVersion() {
+      $this->insert_auto_id_version = array();
+    }
+    public function getDeleteVersionSize() {
+      return sizeof($this->delete_version);
+    }
+    public function getDeleteVersionList() {
+      return $this->delete_version;
+    }
+    public function getDeleteVersion($idx) {
+      return $this->delete_version[$idx];
+    }
+    public function setDeleteVersion($idx, $val) {
+      if (is_double($val)) {
+        $this->delete_version[$idx] = sprintf('%0.0F', $val);
+      } else {
+        $this->delete_version[$idx] = $val;
+      }
+      return $this;
+    }
+    public function addDeleteVersion($val) {
+      if (is_double($val)) {
+        $this->delete_version[] = sprintf('%0.0F', $val);
+      } else {
+        $this->delete_version[] = $val;
+      }
+      return $this;
+    }
+    public function clearDeleteVersion() {
+      $this->delete_version = array();
+    }
+    public function clear() {
+      $this->clearIndexUpdates();
+      $this->clearInsertAutoIdKey();
+      $this->clearUpsertVersion();
+      $this->clearUpdateVersion();
+      $this->clearInsertVersion();
+      $this->clearInsertAutoIdVersion();
+      $this->clearDeleteVersion();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->index_updates)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->index_updates);
+      }
+      $this->checkProtoArray($this->insert_auto_id_key);
+      $res += 1 * sizeof($this->insert_auto_id_key);
+      foreach ($this->insert_auto_id_key as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->upsert_version);
+      $res += 1 * sizeof($this->upsert_version);
+      foreach ($this->upsert_version as $value) {
+        $res += $this->lengthVarInt64($value);
+      }
+      $this->checkProtoArray($this->update_version);
+      $res += 1 * sizeof($this->update_version);
+      foreach ($this->update_version as $value) {
+        $res += $this->lengthVarInt64($value);
+      }
+      $this->checkProtoArray($this->insert_version);
+      $res += 1 * sizeof($this->insert_version);
+      foreach ($this->insert_version as $value) {
+        $res += $this->lengthVarInt64($value);
+      }
+      $this->checkProtoArray($this->insert_auto_id_version);
+      $res += 1 * sizeof($this->insert_auto_id_version);
+      foreach ($this->insert_auto_id_version as $value) {
+        $res += $this->lengthVarInt64($value);
+      }
+      $this->checkProtoArray($this->delete_version);
+      $res += 1 * sizeof($this->delete_version);
+      foreach ($this->delete_version as $value) {
+        $res += $this->lengthVarInt64($value);
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->index_updates)) {
+        $out->putVarInt32(8);
+        $out->putVarInt32($this->index_updates);
+      }
+      $this->checkProtoArray($this->insert_auto_id_key);
+      foreach ($this->insert_auto_id_key as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->upsert_version);
+      foreach ($this->upsert_version as $value) {
+        $out->putVarInt32(24);
+        $out->putVarInt64($value);
+      }
+      $this->checkProtoArray($this->update_version);
+      foreach ($this->update_version as $value) {
+        $out->putVarInt32(32);
+        $out->putVarInt64($value);
+      }
+      $this->checkProtoArray($this->insert_version);
+      foreach ($this->insert_version as $value) {
+        $out->putVarInt32(40);
+        $out->putVarInt64($value);
+      }
+      $this->checkProtoArray($this->insert_auto_id_version);
+      foreach ($this->insert_auto_id_version as $value) {
+        $out->putVarInt32(48);
+        $out->putVarInt64($value);
+      }
+      $this->checkProtoArray($this->delete_version);
+      foreach ($this->delete_version as $value) {
+        $out->putVarInt32(56);
+        $out->putVarInt64($value);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 8:
+            $this->setIndexUpdates($d->getVarInt32());
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addInsertAutoIdKey()->tryMerge($tmp);
+            break;
+          case 24:
+            $this->addUpsertVersion($d->getVarInt64());
+            break;
+          case 32:
+            $this->addUpdateVersion($d->getVarInt64());
+            break;
+          case 40:
+            $this->addInsertVersion($d->getVarInt64());
+            break;
+          case 48:
+            $this->addInsertAutoIdVersion($d->getVarInt64());
+            break;
+          case 56:
+            $this->addDeleteVersion($d->getVarInt64());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->index_updates)) return 'index_updates';
+      foreach ($this->insert_auto_id_key as $value) {
+        if (!$value->isInitialized()) return 'insert_auto_id_key';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasIndexUpdates()) {
+        $this->setIndexUpdates($x->getIndexUpdates());
+      }
+      foreach ($x->getInsertAutoIdKeyList() as $v) {
+        $this->addInsertAutoIdKey()->copyFrom($v);
+      }
+      foreach ($x->getUpsertVersionList() as $v) {
+        $this->addUpsertVersion($v);
+      }
+      foreach ($x->getUpdateVersionList() as $v) {
+        $this->addUpdateVersion($v);
+      }
+      foreach ($x->getInsertVersionList() as $v) {
+        $this->addInsertVersion($v);
+      }
+      foreach ($x->getInsertAutoIdVersionList() as $v) {
+        $this->addInsertAutoIdVersion($v);
+      }
+      foreach ($x->getDeleteVersionList() as $v) {
+        $this->addDeleteVersion($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->index_updates) !== isset($x->index_updates)) return false;
+      if (isset($this->index_updates) && !$this->integerEquals($this->index_updates, $x->index_updates)) return false;
+      if (sizeof($this->insert_auto_id_key) !== sizeof($x->insert_auto_id_key)) return false;
+      foreach (array_map(null, $this->insert_auto_id_key, $x->insert_auto_id_key) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->upsert_version) !== sizeof($x->upsert_version)) return false;
+      foreach (array_map(null, $this->upsert_version, $x->upsert_version) as $v) {
+        if (!$this->integerEquals($v[0], $v[1])) return false;
+      }
+      if (sizeof($this->update_version) !== sizeof($x->update_version)) return false;
+      foreach (array_map(null, $this->update_version, $x->update_version) as $v) {
+        if (!$this->integerEquals($v[0], $v[1])) return false;
+      }
+      if (sizeof($this->insert_version) !== sizeof($x->insert_version)) return false;
+      foreach (array_map(null, $this->insert_version, $x->insert_version) as $v) {
+        if (!$this->integerEquals($v[0], $v[1])) return false;
+      }
+      if (sizeof($this->insert_auto_id_version) !== sizeof($x->insert_auto_id_version)) return false;
+      foreach (array_map(null, $this->insert_auto_id_version, $x->insert_auto_id_version) as $v) {
+        if (!$this->integerEquals($v[0], $v[1])) return false;
+      }
+      if (sizeof($this->delete_version) !== sizeof($x->delete_version)) return false;
+      foreach (array_map(null, $this->delete_version, $x->delete_version) as $v) {
+        if (!$this->integerEquals($v[0], $v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->index_updates)) {
+        $res .= $prefix . "index_updates: " . $this->debugFormatInt32($this->index_updates) . "\n";
+      }
+      foreach ($this->insert_auto_id_key as $value) {
+        $res .= $prefix . "insert_auto_id_key <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->upsert_version as $value) {
+        $res .= $prefix . "upsert_version: " . $this->debugFormatInt64($value) . "\n";
+      }
+      foreach ($this->update_version as $value) {
+        $res .= $prefix . "update_version: " . $this->debugFormatInt64($value) . "\n";
+      }
+      foreach ($this->insert_version as $value) {
+        $res .= $prefix . "insert_version: " . $this->debugFormatInt64($value) . "\n";
+      }
+      foreach ($this->insert_auto_id_version as $value) {
+        $res .= $prefix . "insert_auto_id_version: " . $this->debugFormatInt64($value) . "\n";
+      }
+      foreach ($this->delete_version as $value) {
+        $res .= $prefix . "delete_version: " . $this->debugFormatInt64($value) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\ReadOptions {
+  class ReadConsistency {
+    const DEFAULT_ = 0;
+    const STRONG = 1;
+    const EVENTUAL = 2;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class ReadOptions extends \google\net\ProtocolMessage {
+    public function getReadConsistency() {
+      if (!isset($this->read_consistency)) {
+        return 0;
+      }
+      return $this->read_consistency;
+    }
+    public function setReadConsistency($val) {
+      $this->read_consistency = $val;
+      return $this;
+    }
+    public function clearReadConsistency() {
+      unset($this->read_consistency);
+      return $this;
+    }
+    public function hasReadConsistency() {
+      return isset($this->read_consistency);
+    }
+    public function getTransaction() {
+      if (!isset($this->transaction)) {
+        return '';
+      }
+      return $this->transaction;
+    }
+    public function setTransaction($val) {
+      $this->transaction = $val;
+      return $this;
+    }
+    public function clearTransaction() {
+      unset($this->transaction);
+      return $this;
+    }
+    public function hasTransaction() {
+      return isset($this->transaction);
+    }
+    public function clear() {
+      $this->clearReadConsistency();
+      $this->clearTransaction();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->read_consistency)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->read_consistency);
+      }
+      if (isset($this->transaction)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->transaction));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->read_consistency)) {
+        $out->putVarInt32(8);
+        $out->putVarInt32($this->read_consistency);
+      }
+      if (isset($this->transaction)) {
+        $out->putVarInt32(18);
+        $out->putPrefixedString($this->transaction);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 8:
+            $this->setReadConsistency($d->getVarInt32());
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $this->setTransaction(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasReadConsistency()) {
+        $this->setReadConsistency($x->getReadConsistency());
+      }
+      if ($x->hasTransaction()) {
+        $this->setTransaction($x->getTransaction());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->read_consistency) !== isset($x->read_consistency)) return false;
+      if (isset($this->read_consistency) && $this->read_consistency !== $x->read_consistency) return false;
+      if (isset($this->transaction) !== isset($x->transaction)) return false;
+      if (isset($this->transaction) && $this->transaction !== $x->transaction) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->read_consistency)) {
+        $res .= $prefix . "read_consistency: " . ($this->read_consistency) . "\n";
+      }
+      if (isset($this->transaction)) {
+        $res .= $prefix . "transaction: " . $this->debugFormatString($this->transaction) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class LookupRequest extends \google\net\ProtocolMessage {
+    private $key = array();
+    public function getReadOptions() {
+      if (!isset($this->read_options)) {
+        return new \google\appengine\datastore\v4\ReadOptions();
+      }
+      return $this->read_options;
+    }
+    public function mutableReadOptions() {
+      if (!isset($this->read_options)) {
+        $res = new \google\appengine\datastore\v4\ReadOptions();
+        $this->read_options = $res;
+        return $res;
+      }
+      return $this->read_options;
+    }
+    public function clearReadOptions() {
+      if (isset($this->read_options)) {
+        unset($this->read_options);
+      }
+    }
+    public function hasReadOptions() {
+      return isset($this->read_options);
+    }
+    public function getKeySize() {
+      return sizeof($this->key);
+    }
+    public function getKeyList() {
+      return $this->key;
+    }
+    public function mutableKey($idx) {
+      if (!isset($this->key[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key();
+        $this->key[$idx] = $val;
+        return $val;
+      }
+      return $this->key[$idx];
+    }
+    public function getKey($idx) {
+      if (isset($this->key[$idx])) {
+        return $this->key[$idx];
+      }
+      if ($idx >= end(array_keys($this->key))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key();
+    }
+    public function addKey() {
+      $val = new \google\appengine\datastore\v4\Key();
+      $this->key[] = $val;
+      return $val;
+    }
+    public function clearKey() {
+      $this->key = array();
+    }
+    public function clear() {
+      $this->clearReadOptions();
+      $this->clearKey();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->read_options)) {
+        $res += 1;
+        $res += $this->lengthString($this->read_options->byteSizePartial());
+      }
+      $this->checkProtoArray($this->key);
+      $res += 1 * sizeof($this->key);
+      foreach ($this->key as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->read_options)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->read_options->byteSizePartial());
+        $this->read_options->outputPartial($out);
+      }
+      $this->checkProtoArray($this->key);
+      foreach ($this->key as $value) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableReadOptions()->tryMerge($tmp);
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addKey()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->read_options) && (!$this->read_options->isInitialized())) return 'read_options';
+      foreach ($this->key as $value) {
+        if (!$value->isInitialized()) return 'key';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasReadOptions()) {
+        $this->mutableReadOptions()->mergeFrom($x->getReadOptions());
+      }
+      foreach ($x->getKeyList() as $v) {
+        $this->addKey()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->read_options) !== isset($x->read_options)) return false;
+      if (isset($this->read_options) && !$this->read_options->equals($x->read_options)) return false;
+      if (sizeof($this->key) !== sizeof($x->key)) return false;
+      foreach (array_map(null, $this->key, $x->key) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->read_options)) {
+        $res .= $prefix . "read_options <\n" . $this->read_options->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->key as $value) {
+        $res .= $prefix . "key <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class LookupResponse extends \google\net\ProtocolMessage {
+    private $found = array();
+    private $missing = array();
+    private $deferred = array();
+    public function getFoundSize() {
+      return sizeof($this->found);
+    }
+    public function getFoundList() {
+      return $this->found;
+    }
+    public function mutableFound($idx) {
+      if (!isset($this->found[$idx])) {
+        $val = new \google\appengine\datastore\v4\EntityResult();
+        $this->found[$idx] = $val;
+        return $val;
+      }
+      return $this->found[$idx];
+    }
+    public function getFound($idx) {
+      if (isset($this->found[$idx])) {
+        return $this->found[$idx];
+      }
+      if ($idx >= end(array_keys($this->found))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\EntityResult();
+    }
+    public function addFound() {
+      $val = new \google\appengine\datastore\v4\EntityResult();
+      $this->found[] = $val;
+      return $val;
+    }
+    public function clearFound() {
+      $this->found = array();
+    }
+    public function getMissingSize() {
+      return sizeof($this->missing);
+    }
+    public function getMissingList() {
+      return $this->missing;
+    }
+    public function mutableMissing($idx) {
+      if (!isset($this->missing[$idx])) {
+        $val = new \google\appengine\datastore\v4\EntityResult();
+        $this->missing[$idx] = $val;
+        return $val;
+      }
+      return $this->missing[$idx];
+    }
+    public function getMissing($idx) {
+      if (isset($this->missing[$idx])) {
+        return $this->missing[$idx];
+      }
+      if ($idx >= end(array_keys($this->missing))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\EntityResult();
+    }
+    public function addMissing() {
+      $val = new \google\appengine\datastore\v4\EntityResult();
+      $this->missing[] = $val;
+      return $val;
+    }
+    public function clearMissing() {
+      $this->missing = array();
+    }
+    public function getDeferredSize() {
+      return sizeof($this->deferred);
+    }
+    public function getDeferredList() {
+      return $this->deferred;
+    }
+    public function mutableDeferred($idx) {
+      if (!isset($this->deferred[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key();
+        $this->deferred[$idx] = $val;
+        return $val;
+      }
+      return $this->deferred[$idx];
+    }
+    public function getDeferred($idx) {
+      if (isset($this->deferred[$idx])) {
+        return $this->deferred[$idx];
+      }
+      if ($idx >= end(array_keys($this->deferred))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key();
+    }
+    public function addDeferred() {
+      $val = new \google\appengine\datastore\v4\Key();
+      $this->deferred[] = $val;
+      return $val;
+    }
+    public function clearDeferred() {
+      $this->deferred = array();
+    }
+    public function clear() {
+      $this->clearFound();
+      $this->clearMissing();
+      $this->clearDeferred();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      $this->checkProtoArray($this->found);
+      $res += 1 * sizeof($this->found);
+      foreach ($this->found as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->missing);
+      $res += 1 * sizeof($this->missing);
+      foreach ($this->missing as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->deferred);
+      $res += 1 * sizeof($this->deferred);
+      foreach ($this->deferred as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      $this->checkProtoArray($this->found);
+      foreach ($this->found as $value) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->missing);
+      foreach ($this->missing as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->deferred);
+      foreach ($this->deferred as $value) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addFound()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addMissing()->tryMerge($tmp);
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addDeferred()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      foreach ($this->found as $value) {
+        if (!$value->isInitialized()) return 'found';
+      }
+      foreach ($this->missing as $value) {
+        if (!$value->isInitialized()) return 'missing';
+      }
+      foreach ($this->deferred as $value) {
+        if (!$value->isInitialized()) return 'deferred';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      foreach ($x->getFoundList() as $v) {
+        $this->addFound()->copyFrom($v);
+      }
+      foreach ($x->getMissingList() as $v) {
+        $this->addMissing()->copyFrom($v);
+      }
+      foreach ($x->getDeferredList() as $v) {
+        $this->addDeferred()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (sizeof($this->found) !== sizeof($x->found)) return false;
+      foreach (array_map(null, $this->found, $x->found) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->missing) !== sizeof($x->missing)) return false;
+      foreach (array_map(null, $this->missing, $x->missing) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->deferred) !== sizeof($x->deferred)) return false;
+      foreach (array_map(null, $this->deferred, $x->deferred) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      foreach ($this->found as $value) {
+        $res .= $prefix . "found <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->missing as $value) {
+        $res .= $prefix . "missing <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->deferred as $value) {
+        $res .= $prefix . "deferred <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class RunQueryRequest extends \google\net\ProtocolMessage {
+    public function getReadOptions() {
+      if (!isset($this->read_options)) {
+        return new \google\appengine\datastore\v4\ReadOptions();
+      }
+      return $this->read_options;
+    }
+    public function mutableReadOptions() {
+      if (!isset($this->read_options)) {
+        $res = new \google\appengine\datastore\v4\ReadOptions();
+        $this->read_options = $res;
+        return $res;
+      }
+      return $this->read_options;
+    }
+    public function clearReadOptions() {
+      if (isset($this->read_options)) {
+        unset($this->read_options);
+      }
+    }
+    public function hasReadOptions() {
+      return isset($this->read_options);
+    }
+    public function getPartitionId() {
+      if (!isset($this->partition_id)) {
+        return new \google\appengine\datastore\v4\PartitionId();
+      }
+      return $this->partition_id;
+    }
+    public function mutablePartitionId() {
+      if (!isset($this->partition_id)) {
+        $res = new \google\appengine\datastore\v4\PartitionId();
+        $this->partition_id = $res;
+        return $res;
+      }
+      return $this->partition_id;
+    }
+    public function clearPartitionId() {
+      if (isset($this->partition_id)) {
+        unset($this->partition_id);
+      }
+    }
+    public function hasPartitionId() {
+      return isset($this->partition_id);
+    }
+    public function getQuery() {
+      if (!isset($this->query)) {
+        return new \google\appengine\datastore\v4\Query();
+      }
+      return $this->query;
+    }
+    public function mutableQuery() {
+      if (!isset($this->query)) {
+        $res = new \google\appengine\datastore\v4\Query();
+        $this->query = $res;
+        return $res;
+      }
+      return $this->query;
+    }
+    public function clearQuery() {
+      if (isset($this->query)) {
+        unset($this->query);
+      }
+    }
+    public function hasQuery() {
+      return isset($this->query);
+    }
+    public function getMinSafeTimeSeconds() {
+      if (!isset($this->min_safe_time_seconds)) {
+        return "0";
+      }
+      return $this->min_safe_time_seconds;
+    }
+    public function setMinSafeTimeSeconds($val) {
+      if (is_double($val)) {
+        $this->min_safe_time_seconds = sprintf('%0.0F', $val);
+      } else {
+        $this->min_safe_time_seconds = $val;
+      }
+      return $this;
+    }
+    public function clearMinSafeTimeSeconds() {
+      unset($this->min_safe_time_seconds);
+      return $this;
+    }
+    public function hasMinSafeTimeSeconds() {
+      return isset($this->min_safe_time_seconds);
+    }
+    public function getSuggestedBatchSize() {
+      if (!isset($this->suggested_batch_size)) {
+        return 0;
+      }
+      return $this->suggested_batch_size;
+    }
+    public function setSuggestedBatchSize($val) {
+      $this->suggested_batch_size = $val;
+      return $this;
+    }
+    public function clearSuggestedBatchSize() {
+      unset($this->suggested_batch_size);
+      return $this;
+    }
+    public function hasSuggestedBatchSize() {
+      return isset($this->suggested_batch_size);
+    }
+    public function getGqlQuery() {
+      if (!isset($this->gql_query)) {
+        return new \google\appengine\datastore\v4\GqlQuery();
+      }
+      return $this->gql_query;
+    }
+    public function mutableGqlQuery() {
+      if (!isset($this->gql_query)) {
+        $res = new \google\appengine\datastore\v4\GqlQuery();
+        $this->gql_query = $res;
+        return $res;
+      }
+      return $this->gql_query;
+    }
+    public function clearGqlQuery() {
+      if (isset($this->gql_query)) {
+        unset($this->gql_query);
+      }
+    }
+    public function hasGqlQuery() {
+      return isset($this->gql_query);
+    }
+    public function clear() {
+      $this->clearReadOptions();
+      $this->clearPartitionId();
+      $this->clearQuery();
+      $this->clearMinSafeTimeSeconds();
+      $this->clearSuggestedBatchSize();
+      $this->clearGqlQuery();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->read_options)) {
+        $res += 1;
+        $res += $this->lengthString($this->read_options->byteSizePartial());
+      }
+      if (isset($this->partition_id)) {
+        $res += 1;
+        $res += $this->lengthString($this->partition_id->byteSizePartial());
+      }
+      if (isset($this->query)) {
+        $res += 1;
+        $res += $this->lengthString($this->query->byteSizePartial());
+      }
+      if (isset($this->min_safe_time_seconds)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->min_safe_time_seconds);
+      }
+      if (isset($this->suggested_batch_size)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->suggested_batch_size);
+      }
+      if (isset($this->gql_query)) {
+        $res += 1;
+        $res += $this->lengthString($this->gql_query->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->read_options)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->read_options->byteSizePartial());
+        $this->read_options->outputPartial($out);
+      }
+      if (isset($this->partition_id)) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($this->partition_id->byteSizePartial());
+        $this->partition_id->outputPartial($out);
+      }
+      if (isset($this->query)) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($this->query->byteSizePartial());
+        $this->query->outputPartial($out);
+      }
+      if (isset($this->min_safe_time_seconds)) {
+        $out->putVarInt32(32);
+        $out->putVarInt64($this->min_safe_time_seconds);
+      }
+      if (isset($this->suggested_batch_size)) {
+        $out->putVarInt32(40);
+        $out->putVarInt32($this->suggested_batch_size);
+      }
+      if (isset($this->gql_query)) {
+        $out->putVarInt32(58);
+        $out->putVarInt32($this->gql_query->byteSizePartial());
+        $this->gql_query->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableReadOptions()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutablePartitionId()->tryMerge($tmp);
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableQuery()->tryMerge($tmp);
+            break;
+          case 32:
+            $this->setMinSafeTimeSeconds($d->getVarInt64());
+            break;
+          case 40:
+            $this->setSuggestedBatchSize($d->getVarInt32());
+            break;
+          case 58:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableGqlQuery()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->read_options) && (!$this->read_options->isInitialized())) return 'read_options';
+      if (isset($this->partition_id) && (!$this->partition_id->isInitialized())) return 'partition_id';
+      if (isset($this->query) && (!$this->query->isInitialized())) return 'query';
+      if (isset($this->gql_query) && (!$this->gql_query->isInitialized())) return 'gql_query';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasReadOptions()) {
+        $this->mutableReadOptions()->mergeFrom($x->getReadOptions());
+      }
+      if ($x->hasPartitionId()) {
+        $this->mutablePartitionId()->mergeFrom($x->getPartitionId());
+      }
+      if ($x->hasQuery()) {
+        $this->mutableQuery()->mergeFrom($x->getQuery());
+      }
+      if ($x->hasMinSafeTimeSeconds()) {
+        $this->setMinSafeTimeSeconds($x->getMinSafeTimeSeconds());
+      }
+      if ($x->hasSuggestedBatchSize()) {
+        $this->setSuggestedBatchSize($x->getSuggestedBatchSize());
+      }
+      if ($x->hasGqlQuery()) {
+        $this->mutableGqlQuery()->mergeFrom($x->getGqlQuery());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->read_options) !== isset($x->read_options)) return false;
+      if (isset($this->read_options) && !$this->read_options->equals($x->read_options)) return false;
+      if (isset($this->partition_id) !== isset($x->partition_id)) return false;
+      if (isset($this->partition_id) && !$this->partition_id->equals($x->partition_id)) return false;
+      if (isset($this->query) !== isset($x->query)) return false;
+      if (isset($this->query) && !$this->query->equals($x->query)) return false;
+      if (isset($this->min_safe_time_seconds) !== isset($x->min_safe_time_seconds)) return false;
+      if (isset($this->min_safe_time_seconds) && !$this->integerEquals($this->min_safe_time_seconds, $x->min_safe_time_seconds)) return false;
+      if (isset($this->suggested_batch_size) !== isset($x->suggested_batch_size)) return false;
+      if (isset($this->suggested_batch_size) && !$this->integerEquals($this->suggested_batch_size, $x->suggested_batch_size)) return false;
+      if (isset($this->gql_query) !== isset($x->gql_query)) return false;
+      if (isset($this->gql_query) && !$this->gql_query->equals($x->gql_query)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->read_options)) {
+        $res .= $prefix . "read_options <\n" . $this->read_options->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->partition_id)) {
+        $res .= $prefix . "partition_id <\n" . $this->partition_id->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->query)) {
+        $res .= $prefix . "query <\n" . $this->query->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->min_safe_time_seconds)) {
+        $res .= $prefix . "min_safe_time_seconds: " . $this->debugFormatInt64($this->min_safe_time_seconds) . "\n";
+      }
+      if (isset($this->suggested_batch_size)) {
+        $res .= $prefix . "suggested_batch_size: " . $this->debugFormatInt32($this->suggested_batch_size) . "\n";
+      }
+      if (isset($this->gql_query)) {
+        $res .= $prefix . "gql_query <\n" . $this->gql_query->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class RunQueryResponse extends \google\net\ProtocolMessage {
+    public function getBatch() {
+      if (!isset($this->batch)) {
+        return new \google\appengine\datastore\v4\QueryResultBatch();
+      }
+      return $this->batch;
+    }
+    public function mutableBatch() {
+      if (!isset($this->batch)) {
+        $res = new \google\appengine\datastore\v4\QueryResultBatch();
+        $this->batch = $res;
+        return $res;
+      }
+      return $this->batch;
+    }
+    public function clearBatch() {
+      if (isset($this->batch)) {
+        unset($this->batch);
+      }
+    }
+    public function hasBatch() {
+      return isset($this->batch);
+    }
+    public function getQueryHandle() {
+      if (!isset($this->query_handle)) {
+        return '';
+      }
+      return $this->query_handle;
+    }
+    public function setQueryHandle($val) {
+      $this->query_handle = $val;
+      return $this;
+    }
+    public function clearQueryHandle() {
+      unset($this->query_handle);
+      return $this;
+    }
+    public function hasQueryHandle() {
+      return isset($this->query_handle);
+    }
+    public function clear() {
+      $this->clearBatch();
+      $this->clearQueryHandle();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->batch)) {
+        $res += 1;
+        $res += $this->lengthString($this->batch->byteSizePartial());
+      }
+      if (isset($this->query_handle)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->query_handle));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->batch)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->batch->byteSizePartial());
+        $this->batch->outputPartial($out);
+      }
+      if (isset($this->query_handle)) {
+        $out->putVarInt32(18);
+        $out->putPrefixedString($this->query_handle);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableBatch()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $this->setQueryHandle(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if ((!isset($this->batch)) || (!$this->batch->isInitialized())) return 'batch';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasBatch()) {
+        $this->mutableBatch()->mergeFrom($x->getBatch());
+      }
+      if ($x->hasQueryHandle()) {
+        $this->setQueryHandle($x->getQueryHandle());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->batch) !== isset($x->batch)) return false;
+      if (isset($this->batch) && !$this->batch->equals($x->batch)) return false;
+      if (isset($this->query_handle) !== isset($x->query_handle)) return false;
+      if (isset($this->query_handle) && $this->query_handle !== $x->query_handle) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->batch)) {
+        $res .= $prefix . "batch <\n" . $this->batch->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->query_handle)) {
+        $res .= $prefix . "query_handle: " . $this->debugFormatString($this->query_handle) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class ContinueQueryRequest extends \google\net\ProtocolMessage {
+    public function getQueryHandle() {
+      if (!isset($this->query_handle)) {
+        return '';
+      }
+      return $this->query_handle;
+    }
+    public function setQueryHandle($val) {
+      $this->query_handle = $val;
+      return $this;
+    }
+    public function clearQueryHandle() {
+      unset($this->query_handle);
+      return $this;
+    }
+    public function hasQueryHandle() {
+      return isset($this->query_handle);
+    }
+    public function clear() {
+      $this->clearQueryHandle();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->query_handle)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->query_handle));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->query_handle)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->query_handle);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setQueryHandle(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->query_handle)) return 'query_handle';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasQueryHandle()) {
+        $this->setQueryHandle($x->getQueryHandle());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->query_handle) !== isset($x->query_handle)) return false;
+      if (isset($this->query_handle) && $this->query_handle !== $x->query_handle) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->query_handle)) {
+        $res .= $prefix . "query_handle: " . $this->debugFormatString($this->query_handle) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class ContinueQueryResponse extends \google\net\ProtocolMessage {
+    public function getBatch() {
+      if (!isset($this->batch)) {
+        return new \google\appengine\datastore\v4\QueryResultBatch();
+      }
+      return $this->batch;
+    }
+    public function mutableBatch() {
+      if (!isset($this->batch)) {
+        $res = new \google\appengine\datastore\v4\QueryResultBatch();
+        $this->batch = $res;
+        return $res;
+      }
+      return $this->batch;
+    }
+    public function clearBatch() {
+      if (isset($this->batch)) {
+        unset($this->batch);
+      }
+    }
+    public function hasBatch() {
+      return isset($this->batch);
+    }
+    public function clear() {
+      $this->clearBatch();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->batch)) {
+        $res += 1;
+        $res += $this->lengthString($this->batch->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->batch)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->batch->byteSizePartial());
+        $this->batch->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableBatch()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if ((!isset($this->batch)) || (!$this->batch->isInitialized())) return 'batch';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasBatch()) {
+        $this->mutableBatch()->mergeFrom($x->getBatch());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->batch) !== isset($x->batch)) return false;
+      if (isset($this->batch) && !$this->batch->equals($x->batch)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->batch)) {
+        $res .= $prefix . "batch <\n" . $this->batch->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class BeginTransactionRequest extends \google\net\ProtocolMessage {
+    public function getCrossGroup() {
+      if (!isset($this->cross_group)) {
+        return false;
+      }
+      return $this->cross_group;
+    }
+    public function setCrossGroup($val) {
+      $this->cross_group = $val;
+      return $this;
+    }
+    public function clearCrossGroup() {
+      unset($this->cross_group);
+      return $this;
+    }
+    public function hasCrossGroup() {
+      return isset($this->cross_group);
+    }
+    public function getCrossRequest() {
+      if (!isset($this->cross_request)) {
+        return false;
+      }
+      return $this->cross_request;
+    }
+    public function setCrossRequest($val) {
+      $this->cross_request = $val;
+      return $this;
+    }
+    public function clearCrossRequest() {
+      unset($this->cross_request);
+      return $this;
+    }
+    public function hasCrossRequest() {
+      return isset($this->cross_request);
+    }
+    public function clear() {
+      $this->clearCrossGroup();
+      $this->clearCrossRequest();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->cross_group)) {
+        $res += 2;
+      }
+      if (isset($this->cross_request)) {
+        $res += 2;
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->cross_group)) {
+        $out->putVarInt32(8);
+        $out->putBoolean($this->cross_group);
+      }
+      if (isset($this->cross_request)) {
+        $out->putVarInt32(16);
+        $out->putBoolean($this->cross_request);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 8:
+            $this->setCrossGroup($d->getBoolean());
+            break;
+          case 16:
+            $this->setCrossRequest($d->getBoolean());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasCrossGroup()) {
+        $this->setCrossGroup($x->getCrossGroup());
+      }
+      if ($x->hasCrossRequest()) {
+        $this->setCrossRequest($x->getCrossRequest());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->cross_group) !== isset($x->cross_group)) return false;
+      if (isset($this->cross_group) && $this->cross_group !== $x->cross_group) return false;
+      if (isset($this->cross_request) !== isset($x->cross_request)) return false;
+      if (isset($this->cross_request) && $this->cross_request !== $x->cross_request) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->cross_group)) {
+        $res .= $prefix . "cross_group: " . $this->debugFormatBool($this->cross_group) . "\n";
+      }
+      if (isset($this->cross_request)) {
+        $res .= $prefix . "cross_request: " . $this->debugFormatBool($this->cross_request) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class BeginTransactionResponse extends \google\net\ProtocolMessage {
+    public function getTransaction() {
+      if (!isset($this->transaction)) {
+        return '';
+      }
+      return $this->transaction;
+    }
+    public function setTransaction($val) {
+      $this->transaction = $val;
+      return $this;
+    }
+    public function clearTransaction() {
+      unset($this->transaction);
+      return $this;
+    }
+    public function hasTransaction() {
+      return isset($this->transaction);
+    }
+    public function clear() {
+      $this->clearTransaction();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->transaction)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->transaction));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->transaction)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->transaction);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setTransaction(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->transaction)) return 'transaction';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasTransaction()) {
+        $this->setTransaction($x->getTransaction());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->transaction) !== isset($x->transaction)) return false;
+      if (isset($this->transaction) && $this->transaction !== $x->transaction) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->transaction)) {
+        $res .= $prefix . "transaction: " . $this->debugFormatString($this->transaction) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class RollbackRequest extends \google\net\ProtocolMessage {
+    public function getTransaction() {
+      if (!isset($this->transaction)) {
+        return '';
+      }
+      return $this->transaction;
+    }
+    public function setTransaction($val) {
+      $this->transaction = $val;
+      return $this;
+    }
+    public function clearTransaction() {
+      unset($this->transaction);
+      return $this;
+    }
+    public function hasTransaction() {
+      return isset($this->transaction);
+    }
+    public function clear() {
+      $this->clearTransaction();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->transaction)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->transaction));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->transaction)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->transaction);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setTransaction(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->transaction)) return 'transaction';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasTransaction()) {
+        $this->setTransaction($x->getTransaction());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->transaction) !== isset($x->transaction)) return false;
+      if (isset($this->transaction) && $this->transaction !== $x->transaction) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->transaction)) {
+        $res .= $prefix . "transaction: " . $this->debugFormatString($this->transaction) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class RollbackResponse extends \google\net\ProtocolMessage {
+    public function clear() {
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      return $res;
+    }
+    public function outputPartial($out) {
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\CommitRequest {
+  class Mode {
+    const TRANSACTIONAL = 1;
+    const NON_TRANSACTIONAL = 2;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class CommitRequest extends \google\net\ProtocolMessage {
+    public function getTransaction() {
+      if (!isset($this->transaction)) {
+        return '';
+      }
+      return $this->transaction;
+    }
+    public function setTransaction($val) {
+      $this->transaction = $val;
+      return $this;
+    }
+    public function clearTransaction() {
+      unset($this->transaction);
+      return $this;
+    }
+    public function hasTransaction() {
+      return isset($this->transaction);
+    }
+    public function getMutation() {
+      if (!isset($this->mutation)) {
+        return new \google\appengine\datastore\v4\Mutation();
+      }
+      return $this->mutation;
+    }
+    public function mutableMutation() {
+      if (!isset($this->mutation)) {
+        $res = new \google\appengine\datastore\v4\Mutation();
+        $this->mutation = $res;
+        return $res;
+      }
+      return $this->mutation;
+    }
+    public function clearMutation() {
+      if (isset($this->mutation)) {
+        unset($this->mutation);
+      }
+    }
+    public function hasMutation() {
+      return isset($this->mutation);
+    }
+    public function getMode() {
+      if (!isset($this->mode)) {
+        return 1;
+      }
+      return $this->mode;
+    }
+    public function setMode($val) {
+      $this->mode = $val;
+      return $this;
+    }
+    public function clearMode() {
+      unset($this->mode);
+      return $this;
+    }
+    public function hasMode() {
+      return isset($this->mode);
+    }
+    public function clear() {
+      $this->clearTransaction();
+      $this->clearMutation();
+      $this->clearMode();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->transaction)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->transaction));
+      }
+      if (isset($this->mutation)) {
+        $res += 1;
+        $res += $this->lengthString($this->mutation->byteSizePartial());
+      }
+      if (isset($this->mode)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->mode);
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->transaction)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->transaction);
+      }
+      if (isset($this->mutation)) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($this->mutation->byteSizePartial());
+        $this->mutation->outputPartial($out);
+      }
+      if (isset($this->mode)) {
+        $out->putVarInt32(32);
+        $out->putVarInt32($this->mode);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setTransaction(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableMutation()->tryMerge($tmp);
+            break;
+          case 32:
+            $this->setMode($d->getVarInt32());
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->mutation) && (!$this->mutation->isInitialized())) return 'mutation';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasTransaction()) {
+        $this->setTransaction($x->getTransaction());
+      }
+      if ($x->hasMutation()) {
+        $this->mutableMutation()->mergeFrom($x->getMutation());
+      }
+      if ($x->hasMode()) {
+        $this->setMode($x->getMode());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->transaction) !== isset($x->transaction)) return false;
+      if (isset($this->transaction) && $this->transaction !== $x->transaction) return false;
+      if (isset($this->mutation) !== isset($x->mutation)) return false;
+      if (isset($this->mutation) && !$this->mutation->equals($x->mutation)) return false;
+      if (isset($this->mode) !== isset($x->mode)) return false;
+      if (isset($this->mode) && $this->mode !== $x->mode) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->transaction)) {
+        $res .= $prefix . "transaction: " . $this->debugFormatString($this->transaction) . "\n";
+      }
+      if (isset($this->mutation)) {
+        $res .= $prefix . "mutation <\n" . $this->mutation->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->mode)) {
+        $res .= $prefix . "mode: " . ($this->mode) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class CommitResponse extends \google\net\ProtocolMessage {
+    public function getMutationResult() {
+      if (!isset($this->mutation_result)) {
+        return new \google\appengine\datastore\v4\MutationResult();
+      }
+      return $this->mutation_result;
+    }
+    public function mutableMutationResult() {
+      if (!isset($this->mutation_result)) {
+        $res = new \google\appengine\datastore\v4\MutationResult();
+        $this->mutation_result = $res;
+        return $res;
+      }
+      return $this->mutation_result;
+    }
+    public function clearMutationResult() {
+      if (isset($this->mutation_result)) {
+        unset($this->mutation_result);
+      }
+    }
+    public function hasMutationResult() {
+      return isset($this->mutation_result);
+    }
+    public function clear() {
+      $this->clearMutationResult();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->mutation_result)) {
+        $res += 1;
+        $res += $this->lengthString($this->mutation_result->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->mutation_result)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->mutation_result->byteSizePartial());
+        $this->mutation_result->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableMutationResult()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->mutation_result) && (!$this->mutation_result->isInitialized())) return 'mutation_result';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasMutationResult()) {
+        $this->mutableMutationResult()->mergeFrom($x->getMutationResult());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->mutation_result) !== isset($x->mutation_result)) return false;
+      if (isset($this->mutation_result) && !$this->mutation_result->equals($x->mutation_result)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->mutation_result)) {
+        $res .= $prefix . "mutation_result <\n" . $this->mutation_result->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class AllocateIdsRequest extends \google\net\ProtocolMessage {
+    private $allocate = array();
+    private $reserve = array();
+    public function getAllocateSize() {
+      return sizeof($this->allocate);
+    }
+    public function getAllocateList() {
+      return $this->allocate;
+    }
+    public function mutableAllocate($idx) {
+      if (!isset($this->allocate[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key();
+        $this->allocate[$idx] = $val;
+        return $val;
+      }
+      return $this->allocate[$idx];
+    }
+    public function getAllocate($idx) {
+      if (isset($this->allocate[$idx])) {
+        return $this->allocate[$idx];
+      }
+      if ($idx >= end(array_keys($this->allocate))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key();
+    }
+    public function addAllocate() {
+      $val = new \google\appengine\datastore\v4\Key();
+      $this->allocate[] = $val;
+      return $val;
+    }
+    public function clearAllocate() {
+      $this->allocate = array();
+    }
+    public function getReserveSize() {
+      return sizeof($this->reserve);
+    }
+    public function getReserveList() {
+      return $this->reserve;
+    }
+    public function mutableReserve($idx) {
+      if (!isset($this->reserve[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key();
+        $this->reserve[$idx] = $val;
+        return $val;
+      }
+      return $this->reserve[$idx];
+    }
+    public function getReserve($idx) {
+      if (isset($this->reserve[$idx])) {
+        return $this->reserve[$idx];
+      }
+      if ($idx >= end(array_keys($this->reserve))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key();
+    }
+    public function addReserve() {
+      $val = new \google\appengine\datastore\v4\Key();
+      $this->reserve[] = $val;
+      return $val;
+    }
+    public function clearReserve() {
+      $this->reserve = array();
+    }
+    public function clear() {
+      $this->clearAllocate();
+      $this->clearReserve();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      $this->checkProtoArray($this->allocate);
+      $res += 1 * sizeof($this->allocate);
+      foreach ($this->allocate as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->reserve);
+      $res += 1 * sizeof($this->reserve);
+      foreach ($this->reserve as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      $this->checkProtoArray($this->allocate);
+      foreach ($this->allocate as $value) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->reserve);
+      foreach ($this->reserve as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addAllocate()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addReserve()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      foreach ($this->allocate as $value) {
+        if (!$value->isInitialized()) return 'allocate';
+      }
+      foreach ($this->reserve as $value) {
+        if (!$value->isInitialized()) return 'reserve';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      foreach ($x->getAllocateList() as $v) {
+        $this->addAllocate()->copyFrom($v);
+      }
+      foreach ($x->getReserveList() as $v) {
+        $this->addReserve()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (sizeof($this->allocate) !== sizeof($x->allocate)) return false;
+      foreach (array_map(null, $this->allocate, $x->allocate) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (sizeof($this->reserve) !== sizeof($x->reserve)) return false;
+      foreach (array_map(null, $this->reserve, $x->reserve) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      foreach ($this->allocate as $value) {
+        $res .= $prefix . "allocate <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->reserve as $value) {
+        $res .= $prefix . "reserve <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class AllocateIdsResponse extends \google\net\ProtocolMessage {
+    private $allocated = array();
+    public function getAllocatedSize() {
+      return sizeof($this->allocated);
+    }
+    public function getAllocatedList() {
+      return $this->allocated;
+    }
+    public function mutableAllocated($idx) {
+      if (!isset($this->allocated[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key();
+        $this->allocated[$idx] = $val;
+        return $val;
+      }
+      return $this->allocated[$idx];
+    }
+    public function getAllocated($idx) {
+      if (isset($this->allocated[$idx])) {
+        return $this->allocated[$idx];
+      }
+      if ($idx >= end(array_keys($this->allocated))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key();
+    }
+    public function addAllocated() {
+      $val = new \google\appengine\datastore\v4\Key();
+      $this->allocated[] = $val;
+      return $val;
+    }
+    public function clearAllocated() {
+      $this->allocated = array();
+    }
+    public function clear() {
+      $this->clearAllocated();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      $this->checkProtoArray($this->allocated);
+      $res += 1 * sizeof($this->allocated);
+      foreach ($this->allocated as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      $this->checkProtoArray($this->allocated);
+      foreach ($this->allocated as $value) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addAllocated()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      foreach ($this->allocated as $value) {
+        if (!$value->isInitialized()) return 'allocated';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      foreach ($x->getAllocatedList() as $v) {
+        $this->addAllocated()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (sizeof($this->allocated) !== sizeof($x->allocated)) return false;
+      foreach (array_map(null, $this->allocated, $x->allocated) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      foreach ($this->allocated as $value) {
+        $res .= $prefix . "allocated <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class WriteRequest extends \google\net\ProtocolMessage {
+    public function getMutation() {
+      if (!isset($this->mutation)) {
+        return new \google\appengine\datastore\v4\Mutation();
+      }
+      return $this->mutation;
+    }
+    public function mutableMutation() {
+      if (!isset($this->mutation)) {
+        $res = new \google\appengine\datastore\v4\Mutation();
+        $this->mutation = $res;
+        return $res;
+      }
+      return $this->mutation;
+    }
+    public function clearMutation() {
+      if (isset($this->mutation)) {
+        unset($this->mutation);
+      }
+    }
+    public function hasMutation() {
+      return isset($this->mutation);
+    }
+    public function clear() {
+      $this->clearMutation();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->mutation)) {
+        $res += 1;
+        $res += $this->lengthString($this->mutation->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->mutation)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->mutation->byteSizePartial());
+        $this->mutation->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableMutation()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if ((!isset($this->mutation)) || (!$this->mutation->isInitialized())) return 'mutation';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasMutation()) {
+        $this->mutableMutation()->mergeFrom($x->getMutation());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->mutation) !== isset($x->mutation)) return false;
+      if (isset($this->mutation) && !$this->mutation->equals($x->mutation)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->mutation)) {
+        $res .= $prefix . "mutation <\n" . $this->mutation->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
diff --git a/php/sdk/google/appengine/datastore/entity_v4_pb.php b/php/sdk/google/appengine/datastore/entity_v4_pb.php
new file mode 100644
index 0000000..caceb47
--- /dev/null
+++ b/php/sdk/google/appengine/datastore/entity_v4_pb.php
@@ -0,0 +1,1360 @@
+<?php
+/**
+ * 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.
+ */
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: google/appengine/datastore/entity_v4.proto
+
+namespace dummy {
+  require_once 'google/appengine/runtime/proto/ProtocolMessage.php';
+}
+namespace google\appengine\datastore\v4\PartitionId {
+  class Constants {
+    const MAX_DIMENSION_TAG = 100;
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class PartitionId extends \google\net\ProtocolMessage {
+    public function getDatasetId() {
+      if (!isset($this->dataset_id)) {
+        return '';
+      }
+      return $this->dataset_id;
+    }
+    public function setDatasetId($val) {
+      $this->dataset_id = $val;
+      return $this;
+    }
+    public function clearDatasetId() {
+      unset($this->dataset_id);
+      return $this;
+    }
+    public function hasDatasetId() {
+      return isset($this->dataset_id);
+    }
+    public function getNamespace() {
+      if (!isset($this->namespace)) {
+        return '';
+      }
+      return $this->namespace;
+    }
+    public function setNamespace($val) {
+      $this->namespace = $val;
+      return $this;
+    }
+    public function clearNamespace() {
+      unset($this->namespace);
+      return $this;
+    }
+    public function hasNamespace() {
+      return isset($this->namespace);
+    }
+    public function clear() {
+      $this->clearDatasetId();
+      $this->clearNamespace();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->dataset_id)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->dataset_id));
+      }
+      if (isset($this->namespace)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->namespace));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->dataset_id)) {
+        $out->putVarInt32(26);
+        $out->putPrefixedString($this->dataset_id);
+      }
+      if (isset($this->namespace)) {
+        $out->putVarInt32(34);
+        $out->putPrefixedString($this->namespace);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 26:
+            $length = $d->getVarInt32();
+            $this->setDatasetId(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 34:
+            $length = $d->getVarInt32();
+            $this->setNamespace(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasDatasetId()) {
+        $this->setDatasetId($x->getDatasetId());
+      }
+      if ($x->hasNamespace()) {
+        $this->setNamespace($x->getNamespace());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->dataset_id) !== isset($x->dataset_id)) return false;
+      if (isset($this->dataset_id) && $this->dataset_id !== $x->dataset_id) return false;
+      if (isset($this->namespace) !== isset($x->namespace)) return false;
+      if (isset($this->namespace) && $this->namespace !== $x->namespace) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->dataset_id)) {
+        $res .= $prefix . "dataset_id: " . $this->debugFormatString($this->dataset_id) . "\n";
+      }
+      if (isset($this->namespace)) {
+        $res .= $prefix . "namespace: " . $this->debugFormatString($this->namespace) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4\Key {
+  class PathElement extends \google\net\ProtocolMessage {
+    public function getKind() {
+      if (!isset($this->kind)) {
+        return '';
+      }
+      return $this->kind;
+    }
+    public function setKind($val) {
+      $this->kind = $val;
+      return $this;
+    }
+    public function clearKind() {
+      unset($this->kind);
+      return $this;
+    }
+    public function hasKind() {
+      return isset($this->kind);
+    }
+    public function getId() {
+      if (!isset($this->id)) {
+        return "0";
+      }
+      return $this->id;
+    }
+    public function setId($val) {
+      if (is_double($val)) {
+        $this->id = sprintf('%0.0F', $val);
+      } else {
+        $this->id = $val;
+      }
+      return $this;
+    }
+    public function clearId() {
+      unset($this->id);
+      return $this;
+    }
+    public function hasId() {
+      return isset($this->id);
+    }
+    public function getName() {
+      if (!isset($this->name)) {
+        return '';
+      }
+      return $this->name;
+    }
+    public function setName($val) {
+      $this->name = $val;
+      return $this;
+    }
+    public function clearName() {
+      unset($this->name);
+      return $this;
+    }
+    public function hasName() {
+      return isset($this->name);
+    }
+    public function clear() {
+      $this->clearKind();
+      $this->clearId();
+      $this->clearName();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->kind)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->kind));
+      }
+      if (isset($this->id)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->id);
+      }
+      if (isset($this->name)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->name));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->kind)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->kind);
+      }
+      if (isset($this->id)) {
+        $out->putVarInt32(16);
+        $out->putVarInt64($this->id);
+      }
+      if (isset($this->name)) {
+        $out->putVarInt32(26);
+        $out->putPrefixedString($this->name);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setKind(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 16:
+            $this->setId($d->getVarInt64());
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $this->setName(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->kind)) return 'kind';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasKind()) {
+        $this->setKind($x->getKind());
+      }
+      if ($x->hasId()) {
+        $this->setId($x->getId());
+      }
+      if ($x->hasName()) {
+        $this->setName($x->getName());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->kind) !== isset($x->kind)) return false;
+      if (isset($this->kind) && $this->kind !== $x->kind) return false;
+      if (isset($this->id) !== isset($x->id)) return false;
+      if (isset($this->id) && !$this->integerEquals($this->id, $x->id)) return false;
+      if (isset($this->name) !== isset($x->name)) return false;
+      if (isset($this->name) && $this->name !== $x->name) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->kind)) {
+        $res .= $prefix . "kind: " . $this->debugFormatString($this->kind) . "\n";
+      }
+      if (isset($this->id)) {
+        $res .= $prefix . "id: " . $this->debugFormatInt64($this->id) . "\n";
+      }
+      if (isset($this->name)) {
+        $res .= $prefix . "name: " . $this->debugFormatString($this->name) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Key extends \google\net\ProtocolMessage {
+    private $path_element = array();
+    public function getPartitionId() {
+      if (!isset($this->partition_id)) {
+        return new \google\appengine\datastore\v4\PartitionId();
+      }
+      return $this->partition_id;
+    }
+    public function mutablePartitionId() {
+      if (!isset($this->partition_id)) {
+        $res = new \google\appengine\datastore\v4\PartitionId();
+        $this->partition_id = $res;
+        return $res;
+      }
+      return $this->partition_id;
+    }
+    public function clearPartitionId() {
+      if (isset($this->partition_id)) {
+        unset($this->partition_id);
+      }
+    }
+    public function hasPartitionId() {
+      return isset($this->partition_id);
+    }
+    public function getPathElementSize() {
+      return sizeof($this->path_element);
+    }
+    public function getPathElementList() {
+      return $this->path_element;
+    }
+    public function mutablePathElement($idx) {
+      if (!isset($this->path_element[$idx])) {
+        $val = new \google\appengine\datastore\v4\Key\PathElement();
+        $this->path_element[$idx] = $val;
+        return $val;
+      }
+      return $this->path_element[$idx];
+    }
+    public function getPathElement($idx) {
+      if (isset($this->path_element[$idx])) {
+        return $this->path_element[$idx];
+      }
+      if ($idx >= end(array_keys($this->path_element))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Key\PathElement();
+    }
+    public function addPathElement() {
+      $val = new \google\appengine\datastore\v4\Key\PathElement();
+      $this->path_element[] = $val;
+      return $val;
+    }
+    public function clearPathElement() {
+      $this->path_element = array();
+    }
+    public function clear() {
+      $this->clearPartitionId();
+      $this->clearPathElement();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->partition_id)) {
+        $res += 1;
+        $res += $this->lengthString($this->partition_id->byteSizePartial());
+      }
+      $this->checkProtoArray($this->path_element);
+      $res += 1 * sizeof($this->path_element);
+      foreach ($this->path_element as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->partition_id)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->partition_id->byteSizePartial());
+        $this->partition_id->outputPartial($out);
+      }
+      $this->checkProtoArray($this->path_element);
+      foreach ($this->path_element as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutablePartitionId()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addPathElement()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->partition_id) && (!$this->partition_id->isInitialized())) return 'partition_id';
+      foreach ($this->path_element as $value) {
+        if (!$value->isInitialized()) return 'path_element';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasPartitionId()) {
+        $this->mutablePartitionId()->mergeFrom($x->getPartitionId());
+      }
+      foreach ($x->getPathElementList() as $v) {
+        $this->addPathElement()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->partition_id) !== isset($x->partition_id)) return false;
+      if (isset($this->partition_id) && !$this->partition_id->equals($x->partition_id)) return false;
+      if (sizeof($this->path_element) !== sizeof($x->path_element)) return false;
+      foreach (array_map(null, $this->path_element, $x->path_element) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->partition_id)) {
+        $res .= $prefix . "partition_id <\n" . $this->partition_id->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->path_element as $value) {
+        $res .= $prefix . "path_element <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Value extends \google\net\ProtocolMessage {
+    private $list_value = array();
+    public function getBooleanValue() {
+      if (!isset($this->boolean_value)) {
+        return false;
+      }
+      return $this->boolean_value;
+    }
+    public function setBooleanValue($val) {
+      $this->boolean_value = $val;
+      return $this;
+    }
+    public function clearBooleanValue() {
+      unset($this->boolean_value);
+      return $this;
+    }
+    public function hasBooleanValue() {
+      return isset($this->boolean_value);
+    }
+    public function getIntegerValue() {
+      if (!isset($this->integer_value)) {
+        return "0";
+      }
+      return $this->integer_value;
+    }
+    public function setIntegerValue($val) {
+      if (is_double($val)) {
+        $this->integer_value = sprintf('%0.0F', $val);
+      } else {
+        $this->integer_value = $val;
+      }
+      return $this;
+    }
+    public function clearIntegerValue() {
+      unset($this->integer_value);
+      return $this;
+    }
+    public function hasIntegerValue() {
+      return isset($this->integer_value);
+    }
+    public function getDoubleValue() {
+      if (!isset($this->double_value)) {
+        return 0.0;
+      }
+      return $this->double_value;
+    }
+    public function setDoubleValue($val) {
+      $this->double_value = $val;
+      return $this;
+    }
+    public function clearDoubleValue() {
+      unset($this->double_value);
+      return $this;
+    }
+    public function hasDoubleValue() {
+      return isset($this->double_value);
+    }
+    public function getTimestampMicrosecondsValue() {
+      if (!isset($this->timestamp_microseconds_value)) {
+        return "0";
+      }
+      return $this->timestamp_microseconds_value;
+    }
+    public function setTimestampMicrosecondsValue($val) {
+      if (is_double($val)) {
+        $this->timestamp_microseconds_value = sprintf('%0.0F', $val);
+      } else {
+        $this->timestamp_microseconds_value = $val;
+      }
+      return $this;
+    }
+    public function clearTimestampMicrosecondsValue() {
+      unset($this->timestamp_microseconds_value);
+      return $this;
+    }
+    public function hasTimestampMicrosecondsValue() {
+      return isset($this->timestamp_microseconds_value);
+    }
+    public function getKeyValue() {
+      if (!isset($this->key_value)) {
+        return new \google\appengine\datastore\v4\Key();
+      }
+      return $this->key_value;
+    }
+    public function mutableKeyValue() {
+      if (!isset($this->key_value)) {
+        $res = new \google\appengine\datastore\v4\Key();
+        $this->key_value = $res;
+        return $res;
+      }
+      return $this->key_value;
+    }
+    public function clearKeyValue() {
+      if (isset($this->key_value)) {
+        unset($this->key_value);
+      }
+    }
+    public function hasKeyValue() {
+      return isset($this->key_value);
+    }
+    public function getEntityValue() {
+      if (!isset($this->entity_value)) {
+        return new \google\appengine\datastore\v4\Entity();
+      }
+      return $this->entity_value;
+    }
+    public function mutableEntityValue() {
+      if (!isset($this->entity_value)) {
+        $res = new \google\appengine\datastore\v4\Entity();
+        $this->entity_value = $res;
+        return $res;
+      }
+      return $this->entity_value;
+    }
+    public function clearEntityValue() {
+      if (isset($this->entity_value)) {
+        unset($this->entity_value);
+      }
+    }
+    public function hasEntityValue() {
+      return isset($this->entity_value);
+    }
+    public function getListValueSize() {
+      return sizeof($this->list_value);
+    }
+    public function getListValueList() {
+      return $this->list_value;
+    }
+    public function mutableListValue($idx) {
+      if (!isset($this->list_value[$idx])) {
+        $val = new \google\appengine\datastore\v4\Value();
+        $this->list_value[$idx] = $val;
+        return $val;
+      }
+      return $this->list_value[$idx];
+    }
+    public function getListValue($idx) {
+      if (isset($this->list_value[$idx])) {
+        return $this->list_value[$idx];
+      }
+      if ($idx >= end(array_keys($this->list_value))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Value();
+    }
+    public function addListValue() {
+      $val = new \google\appengine\datastore\v4\Value();
+      $this->list_value[] = $val;
+      return $val;
+    }
+    public function clearListValue() {
+      $this->list_value = array();
+    }
+    public function getMeaning() {
+      if (!isset($this->meaning)) {
+        return 0;
+      }
+      return $this->meaning;
+    }
+    public function setMeaning($val) {
+      $this->meaning = $val;
+      return $this;
+    }
+    public function clearMeaning() {
+      unset($this->meaning);
+      return $this;
+    }
+    public function hasMeaning() {
+      return isset($this->meaning);
+    }
+    public function getIndexed() {
+      if (!isset($this->indexed)) {
+        return true;
+      }
+      return $this->indexed;
+    }
+    public function setIndexed($val) {
+      $this->indexed = $val;
+      return $this;
+    }
+    public function clearIndexed() {
+      unset($this->indexed);
+      return $this;
+    }
+    public function hasIndexed() {
+      return isset($this->indexed);
+    }
+    public function getBlobKeyValue() {
+      if (!isset($this->blob_key_value)) {
+        return '';
+      }
+      return $this->blob_key_value;
+    }
+    public function setBlobKeyValue($val) {
+      $this->blob_key_value = $val;
+      return $this;
+    }
+    public function clearBlobKeyValue() {
+      unset($this->blob_key_value);
+      return $this;
+    }
+    public function hasBlobKeyValue() {
+      return isset($this->blob_key_value);
+    }
+    public function getStringValue() {
+      if (!isset($this->string_value)) {
+        return '';
+      }
+      return $this->string_value;
+    }
+    public function setStringValue($val) {
+      $this->string_value = $val;
+      return $this;
+    }
+    public function clearStringValue() {
+      unset($this->string_value);
+      return $this;
+    }
+    public function hasStringValue() {
+      return isset($this->string_value);
+    }
+    public function getBlobValue() {
+      if (!isset($this->blob_value)) {
+        return '';
+      }
+      return $this->blob_value;
+    }
+    public function setBlobValue($val) {
+      $this->blob_value = $val;
+      return $this;
+    }
+    public function clearBlobValue() {
+      unset($this->blob_value);
+      return $this;
+    }
+    public function hasBlobValue() {
+      return isset($this->blob_value);
+    }
+    public function clear() {
+      $this->clearBooleanValue();
+      $this->clearIntegerValue();
+      $this->clearDoubleValue();
+      $this->clearTimestampMicrosecondsValue();
+      $this->clearKeyValue();
+      $this->clearEntityValue();
+      $this->clearListValue();
+      $this->clearMeaning();
+      $this->clearIndexed();
+      $this->clearBlobKeyValue();
+      $this->clearStringValue();
+      $this->clearBlobValue();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->boolean_value)) {
+        $res += 2;
+      }
+      if (isset($this->integer_value)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->integer_value);
+      }
+      if (isset($this->double_value)) {
+        $res += 9;
+      }
+      if (isset($this->timestamp_microseconds_value)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->timestamp_microseconds_value);
+      }
+      if (isset($this->key_value)) {
+        $res += 1;
+        $res += $this->lengthString($this->key_value->byteSizePartial());
+      }
+      if (isset($this->entity_value)) {
+        $res += 1;
+        $res += $this->lengthString($this->entity_value->byteSizePartial());
+      }
+      $this->checkProtoArray($this->list_value);
+      $res += 1 * sizeof($this->list_value);
+      foreach ($this->list_value as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      if (isset($this->meaning)) {
+        $res += 1;
+        $res += $this->lengthVarInt64($this->meaning);
+      }
+      if (isset($this->indexed)) {
+        $res += 2;
+      }
+      if (isset($this->blob_key_value)) {
+        $res += 2;
+        $res += $this->lengthString(strlen($this->blob_key_value));
+      }
+      if (isset($this->string_value)) {
+        $res += 2;
+        $res += $this->lengthString(strlen($this->string_value));
+      }
+      if (isset($this->blob_value)) {
+        $res += 2;
+        $res += $this->lengthString(strlen($this->blob_value));
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->boolean_value)) {
+        $out->putVarInt32(8);
+        $out->putBoolean($this->boolean_value);
+      }
+      if (isset($this->integer_value)) {
+        $out->putVarInt32(16);
+        $out->putVarInt64($this->integer_value);
+      }
+      if (isset($this->double_value)) {
+        $out->putVarInt32(25);
+        $out->putDouble($this->double_value);
+      }
+      if (isset($this->timestamp_microseconds_value)) {
+        $out->putVarInt32(32);
+        $out->putVarInt64($this->timestamp_microseconds_value);
+      }
+      if (isset($this->key_value)) {
+        $out->putVarInt32(42);
+        $out->putVarInt32($this->key_value->byteSizePartial());
+        $this->key_value->outputPartial($out);
+      }
+      if (isset($this->entity_value)) {
+        $out->putVarInt32(50);
+        $out->putVarInt32($this->entity_value->byteSizePartial());
+        $this->entity_value->outputPartial($out);
+      }
+      $this->checkProtoArray($this->list_value);
+      foreach ($this->list_value as $value) {
+        $out->putVarInt32(58);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      if (isset($this->meaning)) {
+        $out->putVarInt32(112);
+        $out->putVarInt32($this->meaning);
+      }
+      if (isset($this->indexed)) {
+        $out->putVarInt32(120);
+        $out->putBoolean($this->indexed);
+      }
+      if (isset($this->blob_key_value)) {
+        $out->putVarInt32(130);
+        $out->putPrefixedString($this->blob_key_value);
+      }
+      if (isset($this->string_value)) {
+        $out->putVarInt32(138);
+        $out->putPrefixedString($this->string_value);
+      }
+      if (isset($this->blob_value)) {
+        $out->putVarInt32(146);
+        $out->putPrefixedString($this->blob_value);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 8:
+            $this->setBooleanValue($d->getBoolean());
+            break;
+          case 16:
+            $this->setIntegerValue($d->getVarInt64());
+            break;
+          case 25:
+            $this->setDoubleValue($d->getDouble());
+            break;
+          case 32:
+            $this->setTimestampMicrosecondsValue($d->getVarInt64());
+            break;
+          case 42:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableKeyValue()->tryMerge($tmp);
+            break;
+          case 50:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableEntityValue()->tryMerge($tmp);
+            break;
+          case 58:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addListValue()->tryMerge($tmp);
+            break;
+          case 112:
+            $this->setMeaning($d->getVarInt32());
+            break;
+          case 120:
+            $this->setIndexed($d->getBoolean());
+            break;
+          case 130:
+            $length = $d->getVarInt32();
+            $this->setBlobKeyValue(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 138:
+            $length = $d->getVarInt32();
+            $this->setStringValue(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 146:
+            $length = $d->getVarInt32();
+            $this->setBlobValue(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->key_value) && (!$this->key_value->isInitialized())) return 'key_value';
+      if (isset($this->entity_value) && (!$this->entity_value->isInitialized())) return 'entity_value';
+      foreach ($this->list_value as $value) {
+        if (!$value->isInitialized()) return 'list_value';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasBooleanValue()) {
+        $this->setBooleanValue($x->getBooleanValue());
+      }
+      if ($x->hasIntegerValue()) {
+        $this->setIntegerValue($x->getIntegerValue());
+      }
+      if ($x->hasDoubleValue()) {
+        $this->setDoubleValue($x->getDoubleValue());
+      }
+      if ($x->hasTimestampMicrosecondsValue()) {
+        $this->setTimestampMicrosecondsValue($x->getTimestampMicrosecondsValue());
+      }
+      if ($x->hasKeyValue()) {
+        $this->mutableKeyValue()->mergeFrom($x->getKeyValue());
+      }
+      if ($x->hasEntityValue()) {
+        $this->mutableEntityValue()->mergeFrom($x->getEntityValue());
+      }
+      foreach ($x->getListValueList() as $v) {
+        $this->addListValue()->copyFrom($v);
+      }
+      if ($x->hasMeaning()) {
+        $this->setMeaning($x->getMeaning());
+      }
+      if ($x->hasIndexed()) {
+        $this->setIndexed($x->getIndexed());
+      }
+      if ($x->hasBlobKeyValue()) {
+        $this->setBlobKeyValue($x->getBlobKeyValue());
+      }
+      if ($x->hasStringValue()) {
+        $this->setStringValue($x->getStringValue());
+      }
+      if ($x->hasBlobValue()) {
+        $this->setBlobValue($x->getBlobValue());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->boolean_value) !== isset($x->boolean_value)) return false;
+      if (isset($this->boolean_value) && $this->boolean_value !== $x->boolean_value) return false;
+      if (isset($this->integer_value) !== isset($x->integer_value)) return false;
+      if (isset($this->integer_value) && !$this->integerEquals($this->integer_value, $x->integer_value)) return false;
+      if (isset($this->double_value) !== isset($x->double_value)) return false;
+      if (isset($this->double_value) && $this->double_value !== $x->double_value) return false;
+      if (isset($this->timestamp_microseconds_value) !== isset($x->timestamp_microseconds_value)) return false;
+      if (isset($this->timestamp_microseconds_value) && !$this->integerEquals($this->timestamp_microseconds_value, $x->timestamp_microseconds_value)) return false;
+      if (isset($this->key_value) !== isset($x->key_value)) return false;
+      if (isset($this->key_value) && !$this->key_value->equals($x->key_value)) return false;
+      if (isset($this->entity_value) !== isset($x->entity_value)) return false;
+      if (isset($this->entity_value) && !$this->entity_value->equals($x->entity_value)) return false;
+      if (sizeof($this->list_value) !== sizeof($x->list_value)) return false;
+      foreach (array_map(null, $this->list_value, $x->list_value) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (isset($this->meaning) !== isset($x->meaning)) return false;
+      if (isset($this->meaning) && !$this->integerEquals($this->meaning, $x->meaning)) return false;
+      if (isset($this->indexed) !== isset($x->indexed)) return false;
+      if (isset($this->indexed) && $this->indexed !== $x->indexed) return false;
+      if (isset($this->blob_key_value) !== isset($x->blob_key_value)) return false;
+      if (isset($this->blob_key_value) && $this->blob_key_value !== $x->blob_key_value) return false;
+      if (isset($this->string_value) !== isset($x->string_value)) return false;
+      if (isset($this->string_value) && $this->string_value !== $x->string_value) return false;
+      if (isset($this->blob_value) !== isset($x->blob_value)) return false;
+      if (isset($this->blob_value) && $this->blob_value !== $x->blob_value) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->boolean_value)) {
+        $res .= $prefix . "boolean_value: " . $this->debugFormatBool($this->boolean_value) . "\n";
+      }
+      if (isset($this->integer_value)) {
+        $res .= $prefix . "integer_value: " . $this->debugFormatInt64($this->integer_value) . "\n";
+      }
+      if (isset($this->double_value)) {
+        $res .= $prefix . "double_value: " . $this->debugFormatDouble($this->double_value) . "\n";
+      }
+      if (isset($this->timestamp_microseconds_value)) {
+        $res .= $prefix . "timestamp_microseconds_value: " . $this->debugFormatInt64($this->timestamp_microseconds_value) . "\n";
+      }
+      if (isset($this->key_value)) {
+        $res .= $prefix . "key_value <\n" . $this->key_value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->entity_value)) {
+        $res .= $prefix . "entity_value <\n" . $this->entity_value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->list_value as $value) {
+        $res .= $prefix . "list_value <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->meaning)) {
+        $res .= $prefix . "meaning: " . $this->debugFormatInt32($this->meaning) . "\n";
+      }
+      if (isset($this->indexed)) {
+        $res .= $prefix . "indexed: " . $this->debugFormatBool($this->indexed) . "\n";
+      }
+      if (isset($this->blob_key_value)) {
+        $res .= $prefix . "blob_key_value: " . $this->debugFormatString($this->blob_key_value) . "\n";
+      }
+      if (isset($this->string_value)) {
+        $res .= $prefix . "string_value: " . $this->debugFormatString($this->string_value) . "\n";
+      }
+      if (isset($this->blob_value)) {
+        $res .= $prefix . "blob_value: " . $this->debugFormatString($this->blob_value) . "\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Property extends \google\net\ProtocolMessage {
+    private $deprecated_value = array();
+    public function getName() {
+      if (!isset($this->name)) {
+        return '';
+      }
+      return $this->name;
+    }
+    public function setName($val) {
+      $this->name = $val;
+      return $this;
+    }
+    public function clearName() {
+      unset($this->name);
+      return $this;
+    }
+    public function hasName() {
+      return isset($this->name);
+    }
+    public function getDeprecatedMulti() {
+      if (!isset($this->deprecated_multi)) {
+        return false;
+      }
+      return $this->deprecated_multi;
+    }
+    public function setDeprecatedMulti($val) {
+      $this->deprecated_multi = $val;
+      return $this;
+    }
+    public function clearDeprecatedMulti() {
+      unset($this->deprecated_multi);
+      return $this;
+    }
+    public function hasDeprecatedMulti() {
+      return isset($this->deprecated_multi);
+    }
+    public function getDeprecatedValueSize() {
+      return sizeof($this->deprecated_value);
+    }
+    public function getDeprecatedValueList() {
+      return $this->deprecated_value;
+    }
+    public function mutableDeprecatedValue($idx) {
+      if (!isset($this->deprecated_value[$idx])) {
+        $val = new \google\appengine\datastore\v4\Value();
+        $this->deprecated_value[$idx] = $val;
+        return $val;
+      }
+      return $this->deprecated_value[$idx];
+    }
+    public function getDeprecatedValue($idx) {
+      if (isset($this->deprecated_value[$idx])) {
+        return $this->deprecated_value[$idx];
+      }
+      if ($idx >= end(array_keys($this->deprecated_value))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Value();
+    }
+    public function addDeprecatedValue() {
+      $val = new \google\appengine\datastore\v4\Value();
+      $this->deprecated_value[] = $val;
+      return $val;
+    }
+    public function clearDeprecatedValue() {
+      $this->deprecated_value = array();
+    }
+    public function getValue() {
+      if (!isset($this->value)) {
+        return new \google\appengine\datastore\v4\Value();
+      }
+      return $this->value;
+    }
+    public function mutableValue() {
+      if (!isset($this->value)) {
+        $res = new \google\appengine\datastore\v4\Value();
+        $this->value = $res;
+        return $res;
+      }
+      return $this->value;
+    }
+    public function clearValue() {
+      if (isset($this->value)) {
+        unset($this->value);
+      }
+    }
+    public function hasValue() {
+      return isset($this->value);
+    }
+    public function clear() {
+      $this->clearName();
+      $this->clearDeprecatedMulti();
+      $this->clearDeprecatedValue();
+      $this->clearValue();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->name)) {
+        $res += 1;
+        $res += $this->lengthString(strlen($this->name));
+      }
+      if (isset($this->deprecated_multi)) {
+        $res += 2;
+      }
+      $this->checkProtoArray($this->deprecated_value);
+      $res += 1 * sizeof($this->deprecated_value);
+      foreach ($this->deprecated_value as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      if (isset($this->value)) {
+        $res += 1;
+        $res += $this->lengthString($this->value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->name)) {
+        $out->putVarInt32(10);
+        $out->putPrefixedString($this->name);
+      }
+      if (isset($this->deprecated_multi)) {
+        $out->putVarInt32(16);
+        $out->putBoolean($this->deprecated_multi);
+      }
+      $this->checkProtoArray($this->deprecated_value);
+      foreach ($this->deprecated_value as $value) {
+        $out->putVarInt32(26);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+      if (isset($this->value)) {
+        $out->putVarInt32(34);
+        $out->putVarInt32($this->value->byteSizePartial());
+        $this->value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $this->setName(substr($d->buffer(), $d->pos(), $length));
+            $d->skip($length);
+            break;
+          case 16:
+            $this->setDeprecatedMulti($d->getBoolean());
+            break;
+          case 26:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addDeprecatedValue()->tryMerge($tmp);
+            break;
+          case 34:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableValue()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (!isset($this->name)) return 'name';
+      foreach ($this->deprecated_value as $value) {
+        if (!$value->isInitialized()) return 'deprecated_value';
+      }
+      if (isset($this->value) && (!$this->value->isInitialized())) return 'value';
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasName()) {
+        $this->setName($x->getName());
+      }
+      if ($x->hasDeprecatedMulti()) {
+        $this->setDeprecatedMulti($x->getDeprecatedMulti());
+      }
+      foreach ($x->getDeprecatedValueList() as $v) {
+        $this->addDeprecatedValue()->copyFrom($v);
+      }
+      if ($x->hasValue()) {
+        $this->mutableValue()->mergeFrom($x->getValue());
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->name) !== isset($x->name)) return false;
+      if (isset($this->name) && $this->name !== $x->name) return false;
+      if (isset($this->deprecated_multi) !== isset($x->deprecated_multi)) return false;
+      if (isset($this->deprecated_multi) && $this->deprecated_multi !== $x->deprecated_multi) return false;
+      if (sizeof($this->deprecated_value) !== sizeof($x->deprecated_value)) return false;
+      foreach (array_map(null, $this->deprecated_value, $x->deprecated_value) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      if (isset($this->value) !== isset($x->value)) return false;
+      if (isset($this->value) && !$this->value->equals($x->value)) return false;
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->name)) {
+        $res .= $prefix . "name: " . $this->debugFormatString($this->name) . "\n";
+      }
+      if (isset($this->deprecated_multi)) {
+        $res .= $prefix . "deprecated_multi: " . $this->debugFormatBool($this->deprecated_multi) . "\n";
+      }
+      foreach ($this->deprecated_value as $value) {
+        $res .= $prefix . "deprecated_value <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      if (isset($this->value)) {
+        $res .= $prefix . "value <\n" . $this->value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
+namespace google\appengine\datastore\v4 {
+  class Entity extends \google\net\ProtocolMessage {
+    private $property = array();
+    public function getKey() {
+      if (!isset($this->key)) {
+        return new \google\appengine\datastore\v4\Key();
+      }
+      return $this->key;
+    }
+    public function mutableKey() {
+      if (!isset($this->key)) {
+        $res = new \google\appengine\datastore\v4\Key();
+        $this->key = $res;
+        return $res;
+      }
+      return $this->key;
+    }
+    public function clearKey() {
+      if (isset($this->key)) {
+        unset($this->key);
+      }
+    }
+    public function hasKey() {
+      return isset($this->key);
+    }
+    public function getPropertySize() {
+      return sizeof($this->property);
+    }
+    public function getPropertyList() {
+      return $this->property;
+    }
+    public function mutableProperty($idx) {
+      if (!isset($this->property[$idx])) {
+        $val = new \google\appengine\datastore\v4\Property();
+        $this->property[$idx] = $val;
+        return $val;
+      }
+      return $this->property[$idx];
+    }
+    public function getProperty($idx) {
+      if (isset($this->property[$idx])) {
+        return $this->property[$idx];
+      }
+      if ($idx >= end(array_keys($this->property))) {
+        throw new \OutOfRangeException('index out of range: ' + $idx);
+      }
+      return new \google\appengine\datastore\v4\Property();
+    }
+    public function addProperty() {
+      $val = new \google\appengine\datastore\v4\Property();
+      $this->property[] = $val;
+      return $val;
+    }
+    public function clearProperty() {
+      $this->property = array();
+    }
+    public function clear() {
+      $this->clearKey();
+      $this->clearProperty();
+    }
+    public function byteSizePartial() {
+      $res = 0;
+      if (isset($this->key)) {
+        $res += 1;
+        $res += $this->lengthString($this->key->byteSizePartial());
+      }
+      $this->checkProtoArray($this->property);
+      $res += 1 * sizeof($this->property);
+      foreach ($this->property as $value) {
+        $res += $this->lengthString($value->byteSizePartial());
+      }
+      return $res;
+    }
+    public function outputPartial($out) {
+      if (isset($this->key)) {
+        $out->putVarInt32(10);
+        $out->putVarInt32($this->key->byteSizePartial());
+        $this->key->outputPartial($out);
+      }
+      $this->checkProtoArray($this->property);
+      foreach ($this->property as $value) {
+        $out->putVarInt32(18);
+        $out->putVarInt32($value->byteSizePartial());
+        $value->outputPartial($out);
+      }
+    }
+    public function tryMerge($d) {
+      while($d->avail() > 0) {
+        $tt = $d->getVarInt32();
+        switch ($tt) {
+          case 10:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->mutableKey()->tryMerge($tmp);
+            break;
+          case 18:
+            $length = $d->getVarInt32();
+            $tmp = new \google\net\Decoder($d->buffer(), $d->pos(), $d->pos() + $length);
+            $d->skip($length);
+            $this->addProperty()->tryMerge($tmp);
+            break;
+          case 0:
+            throw new \google\net\ProtocolBufferDecodeError();
+            break;
+          default:
+            $d->skipData($tt);
+        }
+      };
+    }
+    public function checkInitialized() {
+      if (isset($this->key) && (!$this->key->isInitialized())) return 'key';
+      foreach ($this->property as $value) {
+        if (!$value->isInitialized()) return 'property';
+      }
+      return null;
+    }
+    public function mergeFrom($x) {
+      if ($x === $this) { throw new \IllegalArgumentException('Cannot copy message to itself'); }
+      if ($x->hasKey()) {
+        $this->mutableKey()->mergeFrom($x->getKey());
+      }
+      foreach ($x->getPropertyList() as $v) {
+        $this->addProperty()->copyFrom($v);
+      }
+    }
+    public function equals($x) {
+      if ($x === $this) { return true; }
+      if (isset($this->key) !== isset($x->key)) return false;
+      if (isset($this->key) && !$this->key->equals($x->key)) return false;
+      if (sizeof($this->property) !== sizeof($x->property)) return false;
+      foreach (array_map(null, $this->property, $x->property) as $v) {
+        if (!$v[0]->equals($v[1])) return false;
+      }
+      return true;
+    }
+    public function shortDebugString($prefix = "") {
+      $res = '';
+      if (isset($this->key)) {
+        $res .= $prefix . "key <\n" . $this->key->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      foreach ($this->property as $value) {
+        $res .= $prefix . "property <\n" . $value->shortDebugString($prefix . "  ") . $prefix . ">\n";
+      }
+      return $res;
+    }
+  }
+}
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageClient.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageClient.php
index 3c737b8..59b58fb 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageClient.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageClient.php
@@ -22,21 +22,13 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/api/app_identity/AppIdentityService.php';
-require_once 'google/appengine/api/cloud_storage/CloudStorageTools.php';
-require_once 'google/appengine/api/urlfetch_service_pb.php';
-require_once 'google/appengine/ext/cloud_storage_streams/HttpResponse.php';
-require_once 'google/appengine/runtime/ApiProxy.php';
-require_once 'google/appengine/runtime/ApplicationError.php';
-require_once 'google/appengine/util/array_util.php';
-
 use google\appengine\api\app_identity\AppIdentityService;
 use google\appengine\api\app_identity\AppIdentityException;
 use google\appengine\api\cloud_storage\CloudStorageTools;
 use google\appengine\runtime\ApiProxy;
 use google\appengine\runtime\ApplicationError;
 use google\appengine\URLFetchRequest\RequestMethod;
-use google\appengine\util as util;
+use google\appengine\util\ArrayUtil;
 
 /**
  * CloudStorageClient provides default fail implementations for all of the
@@ -53,6 +45,11 @@
   // The default amount of time that reads will be held in the cache.
   const DEFAULT_READ_CACHE_EXPIRY_SECONDS = 3600;  // one hour
 
+  // The default maximum number of times that certain (see retryable_statuses)
+  // failed Google Cloud Storage requests will be retried before returning
+  // failure.
+  const DEFAULT_MAXIMUM_NUMBER_OF_RETRIES = 2;
+
   // The default time the writable state of a bucket will be cached for.
   const DEFAULT_WRITABLE_CACHE_EXPIRY_SECONDS = 600;  // ten minutes
 
@@ -155,9 +152,18 @@
       "PATCH" => RequestMethod::PATCH
   ];
 
+  private static $retryable_statuses = [
+      408,  // Request Timeout
+      500,  // Internal Server Error
+      502,  // Bad Gateway
+      503,  // Service Unavailable
+      504,  // Gateway Timeout
+  ];
+
   private static $default_gs_context_options = [
       "enable_cache" => true,
       "enable_optimistic_cache" => false,
+      "max_retries" => self::DEFAULT_MAXIMUM_NUMBER_OF_RETRIES,
       "read_cache_expiry_seconds" => self::DEFAULT_READ_CACHE_EXPIRY_SECONDS,
       "writable_cache_expiry_seconds" =>
           self::DEFAULT_WRITABLE_CACHE_EXPIRY_SECONDS,
@@ -190,8 +196,8 @@
     } else {
       $this->context_options = self::$default_gs_context_options;
     }
-    $this->anonymous = util\findByKeyOrNull($this->context_options,
-                                            "anonymous");
+    $this->anonymous = ArrayUtil::findByKeyOrNull($this->context_options,
+                                                  "anonymous");
 
     $this->url = $this->createObjectUrl($bucket, $object);
   }
@@ -364,13 +370,27 @@
 
     $resp = new \google\appengine\URLFetchResponse();
 
-    try {
-      ApiProxy::makeSyncCall('urlfetch', 'Fetch', $req, $resp);
-    } catch (ApplicationError $e) {
-      syslog(LOG_ERR,
-             sprintf("Call to URLFetch failed with application error %d.",
-                     $e->getApplicationError()));
-      return false;
+    for ($num_retries = 0; ; $num_retries++) {
+      try {
+        ApiProxy::makeSyncCall('urlfetch', 'Fetch', $req, $resp);
+      } catch (ApplicationError $e) {
+        syslog(LOG_ERR,
+               sprintf("Call to URLFetch failed with application error %d.",
+                       $e->getApplicationError()));
+        return false;
+      }
+      $status_code = $resp->getStatusCode();
+
+      if ($num_retries < $this->context_options['max_retries'] &&
+          in_array($status_code, self::$retryable_statuses) &&
+          (connection_status() & CONNECTION_TIMEOUT) == 0) {
+        usleep(rand(0, 1000000 * pow(2, $num_retries)));
+        if ((connection_status() & CONNECTION_TIMEOUT) == CONNECTION_TIMEOUT) {
+          break;
+        }
+      } else {
+        break;
+      }
     }
 
     $response_headers = [];
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDeleteClient.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDeleteClient.php
index 7a79a3f..a95935b 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDeleteClient.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDeleteClient.php
@@ -21,16 +21,10 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-
 /**
  * Client for deleting objects from Google Cloud Storage.
  */
 final class CloudStorageDeleteClient extends CloudStorageClient {
-  public function __construct($bucket, $object, $context) {
-    parent::__construct($bucket, $object, $context);
-  }
-
   public function delete() {
     $token_header = $this->getOAuthTokenHeader(parent::WRITE_SCOPE);
     if ($token_header === false) {
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDirectoryClient.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDirectoryClient.php
index 52ee334..0c78034 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDirectoryClient.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageDirectoryClient.php
@@ -22,10 +22,7 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-require_once 'google/appengine/util/string_util.php';
-
-use google\appengine\util as util;
+use google\appengine\util\StringUtil;
 
 /**
  * Client for deleting objects from Google Cloud Storage.
@@ -50,7 +47,7 @@
     parent::__construct($bucket_name, $object_prefix, $context);
     // Ignore the leading slash
     if (isset($object_prefix)) {
-      if (!util\endsWith($object_prefix, '/')) {
+      if (!StringUtil::endsWith($object_prefix, '/')) {
         $object_prefix .= '/';
       }
       $this->prefix = substr($object_prefix, 1);
@@ -272,7 +269,7 @@
       // to be consistent with the folder behaviour of Google Cloud Storage
       // Manager, which supports the creating of 'folders' in the UI. See
       // https://developers.google.com/storage/docs/gsmanager
-      if (util\endsWith($key, self::FOLDER_SUFFIX)) {
+      if (StringUtil::endsWith($key, self::FOLDER_SUFFIX)) {
         $key = substr_replace($key,
                               parent::DELIMITER,
                               -strlen(parent::FOLDER_SUFFIX));
@@ -285,7 +282,7 @@
   private function getCorrectPathForDirectoryName() {
     // Replace the trailing MARKER from the prefix and replace it with the
     // FOLDER_SUFFIX.
-    if (util\endsWith($this->object_name, parent::DELIMITER)) {
+    if (StringUtil::endsWith($this->object_name, parent::DELIMITER)) {
       return substr_replace($this->object_name,
                             parent::FOLDER_SUFFIX,
                             -strlen(parent::DELIMITER));
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageReadClient.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageReadClient.php
index 0b055b6..b2596e1 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageReadClient.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageReadClient.php
@@ -23,8 +23,7 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-require_once 'google/appengine/ext/cloud_storage_streams/HttpResponse.php';
+use google\appengine\util\StringUtil;
 
 /**
  * Google Cloud Storage Client for reading objects.
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageRenameClient.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageRenameClient.php
index 392f417..6256152 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageRenameClient.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageRenameClient.php
@@ -21,8 +21,6 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-
 /**
  * Client for deleting objects from Google Cloud Storage.
  */
@@ -88,10 +86,23 @@
         'x-goog-copy-source' =>
             sprintf("/%s%s", $this->from_bucket, $this->from_object),
         'x-goog-copy-source-if-match' => $from_etag,
-        'content-type' => $content_type,
-        'x-goog-metadata-directive' => 'COPY',
     ];
 
+    if (array_key_exists('Content-Type', $this->context_options)) {
+      $copy_headers['content-type'] = $this->context_options['Content-Type'];
+    } else {
+      $copy_headers['content-type'] = $content_type;
+    }
+
+    if (array_key_exists('metadata', $this->context_options)) {
+      $copy_headers['x-goog-metadata-directive'] = 'REPLACE';
+      foreach ($this->context_options['metadata'] as $key => $val) {
+        $copy_headers['x-goog-meta-' . $key] = $val;
+      }
+    } else {
+      $copy_headers['x-goog-metadata-directive'] = 'COPY';
+    }
+
     if (array_key_exists("acl", $this->context_options)) {
       $acl = $this->context_options["acl"];
       if (in_array($acl, parent::$valid_acl_values)) {
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapper.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapper.php
index a605022..14dbe8a 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapper.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapper.php
@@ -23,18 +23,8 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/api/cloud_storage/CloudStorageTools.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageDeleteClient.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageDirectoryClient.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageReadClient.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageRenameClient.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageUrlStatClient.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageWriteClient.php';
-require_once 'google/appengine/util/array_util.php';
-
 use google\appengine\api\cloud_storage\CloudStorageTools;
-use google\appengine\util as util;
+use google\appengine\util\ArrayUtil;
 
 /**
  * Allowed stream_context options.
@@ -81,14 +71,14 @@
    * Open a directory handle.
    */
   public function dir_opendir($path, $options) {
-    if (!$this->getBucketAndObjectFromPath($path, $bucket, $path)) {
+    if (!CloudStorageTools::parseFilename($path, $bucket, $object)) {
       trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
                     E_USER_ERROR);
       return false;
     }
 
     $this->client = new CloudStorageDirectoryClient($bucket,
-                                                    $path,
+                                                    $object,
                                                     $this->context);
     return $this->client->initialise();
   }
@@ -115,7 +105,7 @@
   }
 
   public function mkdir($path, $mode, $options) {
-    if (!$this->getBucketAndObjectFromPath($path, $bucket, $object) ||
+    if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
         !isset($object)) {
       if (($options | STREAM_REPORT_ERRORS) != 0) {
         trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
@@ -130,7 +120,7 @@
   }
 
   public function rmdir($path, $options) {
-    if (!$this->getBucketAndObjectFromPath($path, $bucket, $object) ||
+    if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
         !isset($object)) {
       if (($options | STREAM_REPORT_ERRORS) != 0) {
         trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
@@ -150,13 +140,13 @@
    * @return TRUE if the object was renamed, FALSE otherwise
    */
   public function rename($from, $to) {
-    if (!$this->getBucketAndObjectFromPath($from, $from_bucket, $from_object) ||
+    if (!CloudStorageTools::parseFilename($from, $from_bucket, $from_object) ||
         !isset($from_object)) {
       trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $from),
                     E_USER_ERROR);
       return false;
     }
-    if (!$this->getBucketAndObjectFromPath($to, $to_bucket, $to_object) ||
+    if (!CloudStorageTools::parseFilename($to, $to_bucket, $to_object) ||
         !isset($to_object)) {
       trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $to),
                     E_USER_ERROR);
@@ -219,7 +209,7 @@
   }
 
   public function stream_open($path, $mode, $options, &$opened_path) {
-    if (!$this->getBucketAndObjectFromPath($path, $bucket, $object) ||
+    if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
         !isset($object)) {
       if (($options & STREAM_REPORT_ERRORS) != 0) {
         trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
@@ -307,7 +297,7 @@
    * Deletes a file. Called in response to unlink($filename).
    */
   public function unlink($path) {
-    if (!$this->getBucketAndObjectFromPath($path, $bucket, $object) ||
+    if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
         !isset($object)) {
       trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
               E_USER_ERROR);
@@ -321,7 +311,7 @@
   }
 
   public function url_stat($path, $flags) {
-    if (!$this->getBucketAndObjectFromPath($path, $bucket, $object)) {
+    if (!CloudStorageTools::parseFilename($path, $bucket, $object)) {
       if (($flags & STREAM_URL_STAT_QUIET) != 0) {
         trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
                 E_USER_ERROR);
@@ -336,37 +326,4 @@
     return $client->stat();
   }
 
-  /**
-   * Parse the supplied path and extract the bucket and object names from it.
-   * It is possible that there is no object name in the path and a null will be
-   * returned in the $object parameters if this is the case.
-   */
-  private function getBucketAndObjectFromPath($path, &$bucket, &$object) {
-    // Decompose the $path into the GCS url components and check
-    $url_parts = parse_url($path);
-
-    if ($url_parts === false) {
-      return false;
-    }
-    if ($url_parts['scheme'] !== 'gs' || empty($url_parts['host'])) {
-      trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
-                    E_USER_ERROR);
-      return false;
-    }
-    $bucket = $url_parts['host'];
-    $object = null;
-    $path = util\findByKeyOrNull($url_parts, 'path');
-    if (isset($path) && $path !== "/") {
-      $object = $path;
-    }
-
-    // Validate bucket & object names.
-    try {
-      CloudStorageTools::getFilename($bucket, $object);
-    } catch (\InvalidArgumentException $e) {
-      return false;
-    }
-
-    return true;
-  }
 }
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapperTest.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapperTest.php
index 7e5a298..2a7d9c1 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapperTest.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapperTest.php
@@ -81,6 +81,10 @@
     parent::setUp();
     $this->_SERVER = $_SERVER;
 
+    if (!defined("GAE_INCLUDE_GS_BUCKETS")) {
+      define("GAE_INCLUDE_GS_BUCKETS", "foo, bucket, bar");
+    }
+
     stream_wrapper_register("gs",
         "\\google\\appengine\\ext\\cloud_storage_streams\\CloudStorageStreamWrapper",
         STREAM_IS_URL);
@@ -99,6 +103,14 @@
 
     $this->mock_memcached = $this->getMock('\Memcached');
     \Memcached::setMockMemcached($this->mock_memcached);
+
+    $this->triggered_errors = [];
+    set_error_handler(array($this, "errorHandler"));
+  }
+
+  public function errorHandler(
+      $errno , $errstr, $errfile=null, $errline=null, $errcontext=null) {
+    $this->triggered_errors[] = ["errno" => $errno, "errstr" => $errstr];
   }
 
   protected function tearDown() {
@@ -108,45 +120,60 @@
     parent::tearDown();
   }
 
-  public function testInvalidPathName() {
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen("gs:///object.png", "r"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen("gs://", "r"));
-    $invalid_bucket_names = [
-        'BadBucketName',
-        '.another_bad_bucket',
-        'a',
-        'goog_bucket',
-        str_repeat('a', 224),
-        'a.bucket',
-        'foobar' . str_repeat('a', 64)
-    ];
-    foreach ($invalid_bucket_names as $invalid_name) {
-      $this->setExpectedException(
-          "\PHPUnit_Framework_Error",
-          sprintf("Invalid cloud storage bucket name '%s'", $invalid_name));
-      $this->assertFalse(fopen(sprintf('gs://%s/file.txt', $invalid_name),
-                                       'r'));
-    }
+  /**
+   * @dataProvider invalidGCSPaths
+   */
+  public function testInvalidPathName($path) {
+    $this->assertFalse(fopen($path, "r"));
+    $this->assertEquals(E_WARNING, $this->triggered_errors[0]["errno"]);
   }
 
-  public function testInvalidMode() {
+  public function invalidGCSPaths() {
+    return [["gs:///object.png"],
+            ["gs://"],
+            ];
+  }
+
+  /**
+   * @dataProvider invalidGCSBuckets
+   */
+  public function testInvalidBucketName($bucket_name) {
+    $gcs_name = sprintf('gs://%s/file.txt', $bucket_name);
+    $this->assertFalse(fopen($gcs_name, 'r'));
+
+    $this->assertEquals(E_USER_ERROR, $this->triggered_errors[0]["errno"]);
+    $this->assertEquals("Invalid cloud storage bucket name '$bucket_name'",
+                        $this->triggered_errors[0]["errstr"]);
+    $this->assertEquals(E_WARNING, $this->triggered_errors[1]["errno"]);
+    $this->assertStringStartsWith("fopen($gcs_name): failed to open stream",
+                                  $this->triggered_errors[1]["errstr"]);
+  }
+
+  public function invalidGCSBuckets() {
+    return [["BadBucketName"],
+            [".another_bad_bucket"],
+            ["a"],
+            ["goog_bucket"],
+            [str_repeat('a', 224)],
+            ["a.bucket"],
+            ["foobar" . str_repeat('a', 64)],
+            ];
+  }
+
+  /**
+   * @dataProvider invalidGCSModes
+   */
+  public function testInvalidMode($mode) {
     $valid_path = "gs://bucket/object_name.png";
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen($valid_path, "r+"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen($valid_path, "w+"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen($valid_path, "a"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen($valid_path, "a+"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen($valid_path, "x+"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen($valid_path, "c"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen($valid_path, "c+"));
+    $this->assertFalse(fopen($valid_path, $mode));
+    $this->assertEquals(E_WARNING, $this->triggered_errors[0]["errno"]);
+    $this->assertStringStartsWith(
+        "fopen($valid_path): failed to open stream",
+        $this->triggered_errors[0]["errstr"]);
+  }
+
+  public function invalidGCSModes() {
+    return [["r+"], ["w+"], ["a"], ["a+"], ["x+"], ["c"], ["c+"]];
   }
 
   public function testReadObjectSuccess() {
@@ -164,6 +191,135 @@
     $this->apiProxyMock->verify();
   }
 
+  public function testReadObjectFailure() {
+    $body = "Hello from PHP";
+
+    $this->expectGetAccessTokenRequest(CloudStorageClient::READ_SCOPE);
+    $exected_url = self::makeCloudStorageObjectUrl("bucket",
+                                                   "/object_name.png");
+    $request_headers = [
+        "Authorization" => "OAuth foo token",
+        "Range" => sprintf("bytes=0-%d",
+                           CloudStorageReadClient::DEFAULT_READ_SIZE-1),
+        "x-goog-api-version" => 2,
+    ];
+    $failure_response = [
+        "status_code" => 400,
+        "headers" => [],
+        "body" => "",
+    ];
+    $this->expectHttpRequest($exected_url,
+                             RequestMethod::GET,
+                             $request_headers,
+                             null,
+                             $failure_response);
+
+    $this->assertFalse(file_get_contents("gs://bucket/object_name.png"));
+    $this->apiProxyMock->verify();
+
+    $this->assertEquals(E_USER_WARNING, $this->triggered_errors[0]["errno"]);
+    $this->assertEquals("Cloud Storage Error: BAD REQUEST",
+                        $this->triggered_errors[0]["errstr"]);
+    $this->assertEquals(E_WARNING, $this->triggered_errors[1]["errno"]);
+    $this->assertStringStartsWith(
+        "file_get_contents(gs://bucket/object_name.png): failed to open stream",
+        $this->triggered_errors[1]["errstr"]);
+  }
+
+  public function testReadObjectTransientFailureThenSuccess() {
+    $body = "Hello from PHP";
+
+    $this->expectGetAccessTokenRequest(CloudStorageClient::READ_SCOPE);
+    $exected_url = self::makeCloudStorageObjectUrl("bucket",
+                                                   "/object_name.png");
+    $request_headers = [
+        "Authorization" => "OAuth foo token",
+        "Range" => sprintf("bytes=0-%d",
+                           CloudStorageReadClient::DEFAULT_READ_SIZE-1),
+        "x-goog-api-version" => 2,
+    ];
+
+    // The first request will fail with a 500 error, which can be retried.
+    $failure_response = [
+        "status_code" => 500,
+        "headers" => [],
+        "body" => "",
+    ];
+    $this->expectHttpRequest($exected_url,
+                             RequestMethod::GET,
+                             $request_headers,
+                             null,
+                             $failure_response);
+
+    // The second request will succeed.
+    $response_headers = [
+        "ETag" => "deadbeef",
+        "Content-Type" => "text/plain",
+        "Last-Modified" => "Mon, 02 Jul 2012 01:41:01 GMT",
+    ];
+    $response = $this->createSuccessfulGetHttpResponse(
+        $response_headers,
+         $body,
+         0,
+         CloudStorageReadClient::DEFAULT_READ_SIZE,
+         null);
+    $this->expectHttpRequest($exected_url,
+                             RequestMethod::GET,
+                             $request_headers,
+                             null,
+                             $response);
+
+    $data = file_get_contents("gs://bucket/object_name.png");
+    $this->assertEquals($body, $data);
+    $this->apiProxyMock->verify();
+  }
+
+  public function testReadObjectRepeatedTransientFailure() {
+    $body = "Hello from PHP";
+
+    $this->expectGetAccessTokenRequest(CloudStorageClient::READ_SCOPE);
+    $request_headers = [
+        "Authorization" => "OAuth foo token",
+        "Range" => sprintf("bytes=0-%d",
+                           CloudStorageReadClient::DEFAULT_READ_SIZE-1),
+        "x-goog-api-version" => 2,
+    ];
+    $exected_url = self::makeCloudStorageObjectUrl("bucket",
+                                                   "/object_name.png");
+
+    // The first request will fail with a 500 error, which can be retried.
+    $failure_response = [
+        "status_code" => 500,
+        "headers" => [],
+        "body" => "",
+    ];
+    $this->expectHttpRequest($exected_url,
+                             RequestMethod::GET,
+                             $request_headers,
+                             null,
+                             $failure_response);
+    $this->expectHttpRequest($exected_url,
+                             RequestMethod::GET,
+                             $request_headers,
+                             null,
+                             $failure_response);
+    $this->expectHttpRequest($exected_url,
+                             RequestMethod::GET,
+                             $request_headers,
+                             null,
+                             $failure_response);
+
+    $this->assertFalse(file_get_contents("gs://bucket/object_name.png"));
+    $this->apiProxyMock->verify();
+    $this->assertEquals(E_USER_WARNING, $this->triggered_errors[0]["errno"]);
+    $this->assertEquals("Cloud Storage Error: INTERNAL SERVER ERROR",
+                        $this->triggered_errors[0]["errstr"]);
+    $this->assertEquals(E_WARNING, $this->triggered_errors[1]["errno"]);
+    $this->assertStringStartsWith(
+        "file_get_contents(gs://bucket/object_name.png): failed to open stream",
+        $this->triggered_errors[1]["errstr"]);
+  }
+
   public function testReadObjectCacheHitSuccess() {
     $body = "Hello from PHP";
 
@@ -428,10 +584,12 @@
                              null,
                              $response);
 
-    $this->setExpectedException("PHPUnit_Framework_Error_Warning",
-        "Cloud Storage Error: No Such Bucket (NoSuchBucket)");
     $this->assertFalse(unlink("gs://bucket/object.png"));
     $this->apiProxyMock->verify();
+    $this->assertEquals(
+        [["errno" => E_USER_WARNING,
+          "errstr" => "Cloud Storage Error: No Such Bucket (NoSuchBucket)"]],
+        $this->triggered_errors);
   }
 
   public function testStatBucketSuccess() {
@@ -611,19 +769,35 @@
                              null,
                              $response);
 
-    $this->setExpectedException("PHPUnit_Framework_Error_Warning");
     $result = stat("gs://bucket/object.png");
     $this->apiProxyMock->verify();
+    $this->assertEquals(
+        [["errno" => E_USER_WARNING,
+          "errstr" => "Cloud Storage Error: NOT FOUND"],
+         ["errno" => E_WARNING,
+          "errstr" => "stat(): stat failed for gs://bucket/object.png"]],
+        $this->triggered_errors);
   }
 
-  public function testInvalidRenamePaths() {
-    $this->setExpectedException("\PHPUnit_Framework_Error");
+  public function testRenameInvalidToPath() {
     $this->assertFalse(rename("gs://bucket/object.png", "gs://to/"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(rename("gs://bucket/", "gs://to/object.png"));
+    $this->assertEquals(
+        [["errno" => E_USER_ERROR,
+          "errstr" => "Invalid cloud storage bucket name 'to'"],
+         ["errno" => E_USER_ERROR,
+          "errstr" => "Invalid Google Cloud Storage path: gs://to/"]],
+        $this->triggered_errors);
   }
 
-  public function testRenameObjectSuccess() {
+  public function testRenameInvalidFromPath() {
+    $this->assertFalse(rename("gs://bucket/", "gs://to/object.png"));
+    $this->assertEquals(
+        [["errno" => E_USER_ERROR,
+          "errstr" => "Invalid Google Cloud Storage path: gs://bucket/"]],
+        $this->triggered_errors);
+  }
+
+  public function testRenameObjectWithoutContextSuccess() {
     $this->expectGetAccessTokenRequest(CloudStorageClient::WRITE_SCOPE);
 
     // First there is a stat
@@ -686,6 +860,75 @@
     $this->apiProxyMock->verify();
   }
 
+  public function testRenameObjectWithContextSuccess() {
+    $this->expectGetAccessTokenRequest(CloudStorageClient::WRITE_SCOPE);
+
+    // First there is a stat
+    $request_headers = $this->getStandardRequestHeaders();
+    $response = [
+        'status_code' => 200,
+        'headers' => [
+            'Content-Length' => 37337,
+            'ETag' => 'abcdef',
+            'Content-Type' => 'text/plain',
+        ],
+    ];
+
+    $expected_url = $this->makeCloudStorageObjectUrl();
+    $this->expectHttpRequest($expected_url,
+                             RequestMethod::HEAD,
+                             $request_headers,
+                             null,
+                             $response);
+
+    // Then there is a copy with new context
+    $request_headers = [
+        "Authorization" => "OAuth foo token",
+        "x-goog-copy-source" => "/bucket/object.png",
+        "x-goog-copy-source-if-match" => "abcdef",
+        "content-type" => "image/png",
+        "x-goog-metadata-directive" => "REPLACE",
+        "x-goog-meta-foo" => "bar",
+        "x-goog-acl" => "public-read-write",
+        "x-goog-api-version" => 2,
+    ];
+    $response = [
+        'status_code' => 200,
+        'headers' => [
+        ]
+    ];
+    $expected_url = $this->makeCloudStorageObjectUrl("to_bucket", "/to.png");
+    $this->expectHttpRequest($expected_url,
+                             RequestMethod::PUT,
+                             $request_headers,
+                             null,
+                             $response);
+
+    // Then we unlink the original.
+    $request_headers = $this->getStandardRequestHeaders();
+    $response = [
+        'status_code' => 204,
+        'headers' => [
+        ],
+    ];
+    $expected_url = $this->makeCloudStorageObjectUrl();
+    $this->expectHttpRequest($expected_url,
+                             RequestMethod::DELETE,
+                             $request_headers,
+                             null,
+                             $response);
+
+    $from = "gs://bucket/object.png";
+    $to = "gs://to_bucket/to.png";
+    $ctx = stream_context_create([
+        "gs" => ["Content-Type" => "image/png",
+                 "acl" => "public-read-write",
+                 "metadata" => ["foo"=> "bar"]]]);
+
+    $this->assertTrue(rename($from, $to, $ctx));
+    $this->apiProxyMock->verify();
+  }
+
   public function testRenameObjectFromObjectNotFound() {
     $this->expectGetAccessTokenRequest(CloudStorageClient::WRITE_SCOPE);
 
@@ -707,9 +950,13 @@
     $from = "gs://bucket/object.png";
     $to = "gs://to_bucket/to_object";
 
-    $this->setExpectedException("\PHPUnit_Framework_Error_Warning");
-    rename($from, $to);
+    $this->assertFalse(rename($from, $to));
     $this->apiProxyMock->verify();
+    $this->assertEquals(
+        [["errno" => E_USER_WARNING,
+          "errstr" => "Unable to rename: gs://to_bucket/to_object. " .
+                      "Cloud Storage Error: NOT FOUND"]],
+        $this->triggered_errors);
   }
 
   public function testRenameObjectCopyFailed() {
@@ -757,9 +1004,13 @@
     $from = "gs://bucket/object.png";
     $to = "gs://to_bucket/to_object";
 
-    $this->setExpectedException("\PHPUnit_Framework_Error_Warning");
     $this->assertFalse(rename($from, $to));
     $this->apiProxyMock->verify();
+    $this->assertEquals(
+        [["errno" => E_USER_WARNING,
+          "errstr" => "Error copying to gs://to_bucket/to_object. " .
+                      "Cloud Storage Error: PRECONDITION FAILED"]],
+        $this->triggered_errors);
   }
 
   public function testRenameObjectUnlinkFailed() {
@@ -822,9 +1073,13 @@
     $from = "gs://bucket/object.png";
     $to = "gs://to_bucket/to_object";
 
-    $this->setExpectedException("\PHPUnit_Framework_Error_Warning");
     $this->assertFalse(rename($from, $to));
     $this->apiProxyMock->verify();
+    $this->assertEquals(
+        [["errno" => E_USER_WARNING,
+          "errstr" => "Unable to unlink: gs://bucket/object.png. " .
+                      "Cloud Storage Error: NOT FOUND"]],
+         $this->triggered_errors);
   }
 
   public function testWriteObjectSuccess() {
@@ -887,8 +1142,12 @@
     ];
     stream_context_set_default($context);
     $this->expectGetAccessTokenRequest(CloudStorageClient::WRITE_SCOPE);
-    $this->setExpectedException("\PHPUnit_Framework_Error");
     file_put_contents("gs://bucket/object.png", "Some data");
+    $this->apiProxyMock->verify();
+    $this->assertEquals(
+        ["errno" => E_USER_WARNING,
+         "errstr" => "Invalid metadata key: f o o"],
+        $this->triggered_errors[0]);
   }
 
   public function testWriteLargeObjectSuccess() {
@@ -988,13 +1247,22 @@
   }
 
   public function testInvalidBucketForInclude() {
+    // Uses GAE_INCLUDE_GS_BUCKETS, which is not defined.
     stream_wrapper_unregister("gs");
     stream_wrapper_register("gs",
         "\\google\\appengine\\ext\\cloud_storage_streams\\CloudStorageStreamWrapper",
         0);
 
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    include 'gs://bucket/object.php';
+    include 'gs://unknownbucket/object.php';
+
+    $this->assertEquals(E_WARNING, $this->triggered_errors[0]["errno"]);
+    $this->assertStringStartsWith(
+        "include(gs://unknownbucket/object.php): failed to open stream:",
+        $this->triggered_errors[0]["errstr"]);
+    $this->assertEquals(E_WARNING, $this->triggered_errors[1]["errno"]);
+    $this->assertStringStartsWith(
+        "include(): Failed opening 'gs://unknownbucket/object.php'",
+        $this->triggered_errors[1]["errstr"]);
   }
 
   public function testValidBucketForInclude() {
@@ -1010,7 +1278,6 @@
                                  CloudStorageReadClient::DEFAULT_READ_SIZE,
                                  null);
 
-    define("GAE_INCLUDE_GS_BUCKETS", "foo, bucket, bar");
     $valid_path = "gs://bucket/object_name.png";
     require $valid_path;
 
@@ -1018,11 +1285,20 @@
     $this->apiProxyMock->verify();
   }
 
-  public function testInvalidPathForOpenDir() {
-    $this->setExpectedException("\PHPUnit_Framework_Error");
+  public function testOpenDirEmptyBucket() {
     $this->assertFalse(opendir("gs:///"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
-    $this->assertFalse(fopen("gs://"));
+    $this->assertEquals(
+        ["errno" => E_USER_ERROR,
+         "errstr" => "Invalid Google Cloud Storage path: gs:///"],
+        $this->triggered_errors[0]);
+  }
+
+  public function testOpenDirNoBucket() {
+    $this->assertFalse(opendir("gs://"));
+    $this->assertEquals(
+        ["errno" => E_USER_ERROR,
+         "errstr" => "Invalid Google Cloud Storage path: gs://"],
+        $this->triggered_errors[0]);
   }
 
   public function testReaddirSuccess() {
@@ -1183,11 +1459,21 @@
     $this->apiProxyMock->verify();
   }
 
-  public function testInvalidPathForMkDir() {
-    $this->setExpectedException("\PHPUnit_Framework_Error");
+  public function testMkDirBucketWithoutObject() {
     $this->assertFalse(mkdir("gs://bucket_without_object/"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
+    $this->assertEquals(
+        [["errno" => E_USER_ERROR,
+          "errstr" => "Invalid Google Cloud Storage path: " .
+                      "gs://bucket_without_object/"]],
+        $this->triggered_errors);
+  }
+
+  public function testMkDirBucketWithoutBucket() {
     $this->assertFalse(mkdir("gs://"));
+    $this->assertEquals(
+        [["errno" => E_USER_ERROR,
+          "errstr" => "Invalid Google Cloud Storage path: gs://"]],
+        $this->triggered_errors);
   }
 
   public function testMkDirSuccess() {
@@ -1217,11 +1503,21 @@
     $this->apiProxyMock->verify();
   }
 
-  public function testInvalidPathForRmDir() {
-    $this->setExpectedException("\PHPUnit_Framework_Error");
+  public function testRmDirNoFile() {
     $this->assertFalse(rmdir("gs://bucket_without_object/"));
-    $this->setExpectedException("\PHPUnit_Framework_Error");
+    $this->assertEquals(
+        [["errno" => E_USER_ERROR,
+          "errstr" => "Invalid Google Cloud Storage path: " .
+                      "gs://bucket_without_object/"]],
+        $this->triggered_errors);
+  }
+
+  public function testRmDirNoBucket() {
     $this->assertFalse(rmdir("gs://"));
+    $this->assertEquals(
+        [["errno" => E_USER_ERROR,
+          "errstr" => "Invalid Google Cloud Storage path: gs://"]],
+        $this->triggered_errors);
   }
 
   public function testRmDirSuccess() {
@@ -1270,7 +1566,7 @@
     $this->apiProxyMock->verify();
   }
 
-  public function testRmDirNotEmpry() {
+  public function testRmDirNotEmpty() {
     // Expect a request to list the contents of the bucket to ensure that it is
     // empty.
     $this->expectGetAccessTokenRequest(CloudStorageClient::READ_SCOPE);
@@ -1297,9 +1593,12 @@
                              null,
                              $response);
 
-    $this->setExpectedException("\PHPUnit_Framework_Error");
     $this->assertFalse(rmdir("gs://bucket/dira/dirb"));
     $this->apiProxyMock->verify();
+    $this->assertEquals(
+        [["errno" => E_USER_WARNING,
+          "errstr" => "The directory is not empty."]],
+        $this->triggered_errors);
   }
 
   public function testStreamCast() {
@@ -1311,12 +1610,13 @@
                                  null);
 
     $valid_path = "gs://bucket/object_name.png";
-    $this->setExpectedException(
-        '\PHPUnit_Framework_Error',
-        'gzopen(): cannot represent a stream of type user-space as a ' .
-        'File Descriptor');
     $this->assertFalse(gzopen($valid_path, 'rb'));
     $this->apiProxyMock->verify();
+    $this->assertEquals(
+        [["errno" => E_WARNING,
+          "errstr" => "gzopen(): cannot represent a stream of type " .
+                      "user-space as a File Descriptor"]],
+        $this->triggered_errors);
   }
 
   private function expectFileReadRequest($body,
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageUrlStatClient.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageUrlStatClient.php
index f11be36..52693c5 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageUrlStatClient.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageUrlStatClient.php
@@ -21,10 +21,7 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-require_once 'google/appengine/util/string_util.php';
-
-use google\appengine\util as util;
+use google\appengine\util\StringUtil;
 
 /**
  * Client for stating objects in Google Cloud Storage.
@@ -57,7 +54,7 @@
    */
   public function stat() {
     $prefix = $this->prefix;
-    if (util\endsWith($prefix, parent::DELIMITER)) {
+    if (StringUtil::endsWith($prefix, parent::DELIMITER)) {
       $prefix = substr($prefix, 0, strlen($prefix) - 1);
     }
 
diff --git a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageWriteClient.php b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageWriteClient.php
index 6e8f2af..61673ba 100644
--- a/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageWriteClient.php
+++ b/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageWriteClient.php
@@ -22,8 +22,6 @@
 
 namespace google\appengine\ext\cloud_storage_streams;
 
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php';
-
 // TODO: Retry on transient errors.
 
 final class CloudStorageWriteClient extends CloudStorageClient {
diff --git a/php/sdk/google/appengine/ext/session/MemcacheSessionHandler.php b/php/sdk/google/appengine/ext/session/MemcacheSessionHandler.php
index 09cae5c..59de535 100644
--- a/php/sdk/google/appengine/ext/session/MemcacheSessionHandler.php
+++ b/php/sdk/google/appengine/ext/session/MemcacheSessionHandler.php
@@ -24,8 +24,6 @@
 
 namespace google\appengine\ext\session;
 
-require_once 'google/appengine/runtime/Memcache.php';
-
 /**
  * Remove direct interaction with Memcache object for ease of mocking in tests.
  */
diff --git a/php/sdk/google/appengine/ext/session/MemcacheSessionHandlerTest.php b/php/sdk/google/appengine/ext/session/MemcacheSessionHandlerTest.php
index a8eedc1..9fb6182 100644
--- a/php/sdk/google/appengine/ext/session/MemcacheSessionHandlerTest.php
+++ b/php/sdk/google/appengine/ext/session/MemcacheSessionHandlerTest.php
@@ -68,4 +68,8 @@
 
     $this->assertEquals("Memcache", ini_get("session.save_path"));
   }
+
+  public function testConstant() {
+    $this->assertEquals(1, MEMCACHE_HAVE_SESSION);
+  }
 }
diff --git a/php/sdk/google/appengine/runtime/ApiProxy.php b/php/sdk/google/appengine/runtime/ApiProxy.php
index 7aa6a0b..8bfe3bb 100644
--- a/php/sdk/google/appengine/runtime/ApiProxy.php
+++ b/php/sdk/google/appengine/runtime/ApiProxy.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/RealApiProxy.php';
-
 class ApiProxy {
   private static $apiProxy = null;
 
@@ -35,11 +33,11 @@
       $request,
       $response,
       $deadline = null) {
-    if (ApiProxy::$apiProxy === null) {
-      ApiProxy::$apiProxy = new RealApiProxy();
+    if (self::$apiProxy === null) {
+      self::$apiProxy = new RealApiProxy();
     }
-    ApiProxy::$apiProxy->makeSyncCall(
-      $package, $call_name, $request, $response, $deadline);
+    self::$apiProxy->makeSyncCall(
+        $package, $call_name, $request, $response, $deadline);
   }
 
   /**
@@ -48,6 +46,6 @@
    * @param resource $apiProxy API Proxy instance to use
    */
   public static function setApiProxy($apiProxy) {
-    ApiProxy::$apiProxy = $apiProxy;
+    self::$apiProxy = $apiProxy;
   }
 }
diff --git a/php/sdk/google/appengine/runtime/ApiProxyBase.php b/php/sdk/google/appengine/runtime/ApiProxyBase.php
index 283b894..9f85eef 100644
--- a/php/sdk/google/appengine/runtime/ApiProxyBase.php
+++ b/php/sdk/google/appengine/runtime/ApiProxyBase.php
@@ -16,18 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/ApplicationError.php';
-require_once 'google/appengine/runtime/ArgumentError.php';
-require_once 'google/appengine/runtime/CallNotFoundError.php';
-require_once 'google/appengine/runtime/CancelledError.php';
-require_once 'google/appengine/runtime/CapabilityDisabledError.php';
-require_once 'google/appengine/runtime/DeadlineExceededError.php';
-require_once 'google/appengine/runtime/FeatureNotEnabledError.php';
-require_once 'google/appengine/runtime/OverQuotaError.php';
-require_once 'google/appengine/runtime/RequestTooLargeError.php';
-require_once 'google/appengine/runtime/ResponseTooLargeError.php';
-require_once 'google/appengine/runtime/RPCFailedError.php';
-
 abstract class ApiProxyBase {
   const OK                  =  0;
   const RPC_FAILED          =  1;
@@ -44,32 +32,32 @@
   const RESPONSE_TOO_LARGE  = 12;
 
   protected static $exceptionLookupTable = array(
-    ApiProxyBase::RPC_FAILED => array(
+    self::RPC_FAILED => array(
       '\google\appengine\runtime\RPCFailedError',
       'The remote RPC to the application server failed for the call %s.%s().'),
-    ApiProxyBase::CALL_NOT_FOUND => array(
+    self::CALL_NOT_FOUND => array(
       '\google\appengine\runtime\CallNotFoundError',
       "The API package '%s' or call '%s()' was not found."),
-    ApiProxyBase::ARGUMENT_ERROR => array(
+    self::ARGUMENT_ERROR => array(
       '\google\appengine\runtime\ArgumentError',
       'An error occurred parsing (locally or remotely) the arguments to %s.%s().'
     ),
-    ApiProxyBase::DEADLINE_EXCEEDED => array(
+    self::DEADLINE_EXCEEDED => array(
       '\google\appengine\runtime\DeadlineExceededError',
       'The API call %s.%s() took too long to respond and was cancelled.'),
-    ApiProxyBase::CANCELLED => array(
+    self::CANCELLED => array(
       '\google\appengine\runtime\CancelledError',
       'The API call %s.%s() was explicitly cancelled.'),
-    ApiProxyBase::OTHER_ERROR => array(
+    self::OTHER_ERROR => array(
       '\google\appengine\runtime\Error',
       'An error occurred for the API request %s.%s().'),
-    ApiProxyBase::OVER_QUOTA => array(
+    self::OVER_QUOTA => array(
       '\google\appengine\runtime\OverQuotaError',
       'The API call %s.%s() required more quota than is available.'),
-    ApiProxyBase::REQUEST_TOO_LARGE => array(
+    self::REQUEST_TOO_LARGE => array(
       '\google\appengine\runtime\RequestTooLargeError',
       'The request to API call %s.%s() was too large.'),
-    ApiProxyBase::RESPONSE_TOO_LARGE => array(
+    self::RESPONSE_TOO_LARGE => array(
       '\google\appengine\runtime\ResponseTooLargeError',
       'The response from API call %s.%s() was too large.'),
 
diff --git a/php/sdk/google/appengine/runtime/ApplicationError.php b/php/sdk/google/appengine/runtime/ApplicationError.php
index 8f7f917..5ba2e2a 100644
--- a/php/sdk/google/appengine/runtime/ApplicationError.php
+++ b/php/sdk/google/appengine/runtime/ApplicationError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy in the event of an application-level error.
  */
diff --git a/php/sdk/google/appengine/runtime/ArgumentError.php b/php/sdk/google/appengine/runtime/ArgumentError.php
index 26eb14e..307f3e4 100644
--- a/php/sdk/google/appengine/runtime/ArgumentError.php
+++ b/php/sdk/google/appengine/runtime/ArgumentError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy calls if there is an error parsing the arguments.
  */
diff --git a/php/sdk/google/appengine/runtime/CallNotFoundError.php b/php/sdk/google/appengine/runtime/CallNotFoundError.php
index 9601b11..54cf48e 100644
--- a/php/sdk/google/appengine/runtime/CallNotFoundError.php
+++ b/php/sdk/google/appengine/runtime/CallNotFoundError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy calls when the requested method cannot be found.
  */
diff --git a/php/sdk/google/appengine/runtime/CancelledError.php b/php/sdk/google/appengine/runtime/CancelledError.php
index 233cef6..539fa44 100644
--- a/php/sdk/google/appengine/runtime/CancelledError.php
+++ b/php/sdk/google/appengine/runtime/CancelledError.php
@@ -16,11 +16,9 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy calls if the call was cancelled, such as when
-  the user's request is exiting.
+ * the user's request is exiting.
  */
 class CancelledError extends Error {
 }
diff --git a/php/sdk/google/appengine/runtime/CapabilityDisabledError.php b/php/sdk/google/appengine/runtime/CapabilityDisabledError.php
index 54bd175..86c8965 100644
--- a/php/sdk/google/appengine/runtime/CapabilityDisabledError.php
+++ b/php/sdk/google/appengine/runtime/CapabilityDisabledError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy when API calls are temporarily disabled.
  */
diff --git a/php/sdk/google/appengine/runtime/DeadlineExceededError.php b/php/sdk/google/appengine/runtime/DeadlineExceededError.php
index cb9a79a..e16a948 100644
--- a/php/sdk/google/appengine/runtime/DeadlineExceededError.php
+++ b/php/sdk/google/appengine/runtime/DeadlineExceededError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy calls if the call took too long to respond.
  */
diff --git a/php/sdk/google/appengine/runtime/FeatureNotEnabledError.php b/php/sdk/google/appengine/runtime/FeatureNotEnabledError.php
index 02185d2..5b3c9a4 100644
--- a/php/sdk/google/appengine/runtime/FeatureNotEnabledError.php
+++ b/php/sdk/google/appengine/runtime/FeatureNotEnabledError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy when the app must enable a feature to use this call.
  */
diff --git a/php/sdk/google/appengine/runtime/GetHostName.php b/php/sdk/google/appengine/runtime/GetHostName.php
new file mode 100644
index 0000000..4fa8431
--- /dev/null
+++ b/php/sdk/google/appengine/runtime/GetHostName.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * 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.
+ */
+/**
+ * Provide _gethostname() function that wraps ModulesService::getHostname().
+ *
+ * In order to allow unit testing on an unmodified PHP runtime the function
+ * implementation definition is prefixed with an underscore. The actual function
+ * name is mapped in Setup.php which simply calls the implementation.
+ *
+ * @see http://us2.php.net/manual/en/function.gethostname.php
+ */
+
+namespace google\appengine\runtime;
+
+use google\appengine\api\modules\ModulesService;
+
+/**
+ * Gets the standard host name for the local machine.
+ *
+ * @return bool|string
+ *   a string with the hostname on success, otherwise FALSE is returned.
+ */
+function _gethostname() {
+  // In order to be consistent with PHP core implementation, wrap any exception
+  // and return false.
+  try {
+    return ModulesService::getHostname();
+  }
+  catch (\Exception $e) {
+    return false;
+  }
+}
diff --git a/php/sdk/google/appengine/runtime/GetHostNameTest.php b/php/sdk/google/appengine/runtime/GetHostNameTest.php
new file mode 100644
index 0000000..25acb54
--- /dev/null
+++ b/php/sdk/google/appengine/runtime/GetHostNameTest.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * 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.
+ */
+/**
+ * Unit tests for GetHostName.php [_gethostname()].
+ */
+
+namespace google\appengine\runtime;
+
+require_once 'google/appengine/api/modules/modules_service_pb.php';
+require_once 'google/appengine/runtime/ApplicationError.php';
+require_once 'google/appengine/runtime/GetHostName.php';
+require_once 'google/appengine/testing/ApiProxyTestBase.php';
+
+use google\appengine\GetHostnameRequest;
+use google\appengine\GetHostnameResponse;
+use google\appengine\ModulesServiceError\ErrorCode;
+use google\appengine\runtime\ApplicationError;
+use google\appengine\testing\ApiProxyTestBase;
+
+class GetHostNameTest extends ApiProxyTestBase {
+  // See api\modules\ModulesServiceTest::testGetHostname().
+  public function testGetHostName() {
+    $req = new GetHostnameRequest();
+    $resp = new GetHostnameResponse();
+
+    $resp->setHostname('hostname');
+
+    $this->apiProxyMock->expectCall('modules', 'GetHostname', $req, $resp);
+
+    $this->assertEquals('hostname', _gethostname());
+    $this->apiProxyMock->verify();
+  }
+
+  public function testGetHostNameException() {
+    $req = new GetHostnameRequest();
+    $resp = new ApplicationError(ErrorCode::TRANSIENT_ERROR, 'unkonwn');
+
+    $this->apiProxyMock->expectCall('modules', 'GetHostname', $req, $resp);
+
+    $this->assertEquals(false, _gethostname());
+    $this->apiProxyMock->verify();
+  }
+}
diff --git a/php/sdk/google/appengine/runtime/Memcache.php b/php/sdk/google/appengine/runtime/Memcache.php
index 621277f..f0715e6 100644
--- a/php/sdk/google/appengine/runtime/Memcache.php
+++ b/php/sdk/google/appengine/runtime/Memcache.php
@@ -42,10 +42,9 @@
 use google\appengine\runtime\Error;
 use google\appengine\runtime\MemcacheUtils;
 
-require_once 'google/appengine/api/memcache/memcache_service_pb.php';
-require_once 'google/appengine/runtime/MemcacheUtils.php';
-require_once 'google/appengine/runtime/ApiProxy.php';
-require_once 'google/appengine/runtime/Error.php';
+// Define constants for compatibility, but they will be ignored.
+const MEMCACHE_COMPRESSED = 2;
+const MEMCACHE_HAVE_SESSION = 1; // See ext/session/MemcacheSessionHandler.
 
 /**
  * Adds a new item to the cache. Will fail if the key is already present in the
diff --git a/php/sdk/google/appengine/runtime/MemcacheTest.php b/php/sdk/google/appengine/runtime/MemcacheTest.php
index 427be2a..6c8e6a1 100644
--- a/php/sdk/google/appengine/runtime/MemcacheTest.php
+++ b/php/sdk/google/appengine/runtime/MemcacheTest.php
@@ -345,4 +345,27 @@
     $this->assertTrue(memcache_set($memcache, "float", 2.0, null, 30));
     $this->apiProxyMock->verify();
   }
+
+  public function testSetSuccessCompressed() {
+    $memcache = new Memcache();
+
+    $request = new MemcacheSetRequest();
+    $item = $request->addItem();
+    $item->setKey("float");
+    $item->setValue("3");
+    $item->setFlags(6);  // float
+    $item->setSetPolicy(SetPolicy::SET);
+    $item->setExpirationTime(30);
+
+    $response = new MemcacheSetResponse();
+    $response->addSetStatus(SetStatusCode::STORED);
+
+    $this->apiProxyMock->expectCall('memcache',
+                                    'Set',
+                                    $request,
+                                    $response);
+    $this->assertTrue(memcache_set($memcache, "float", 3.0, MEMCACHE_COMPRESSED,
+                                   30));
+    $this->apiProxyMock->verify();
+  }
 }
diff --git a/php/sdk/google/appengine/runtime/MemcacheUtils.php b/php/sdk/google/appengine/runtime/MemcacheUtils.php
index 7dd86e6..e74fa9e 100644
--- a/php/sdk/google/appengine/runtime/MemcacheUtils.php
+++ b/php/sdk/google/appengine/runtime/MemcacheUtils.php
@@ -24,9 +24,6 @@
 use google\appengine\MemcacheSetRequest;
 use google\appengine\MemcacheSetResponse;
 
-require_once 'google/appengine/api/memcache/memcache_service_pb.php';
-require_once 'google/appengine/runtime/ApiProxy.php';
-
 class MemcacheUtils {
 
   const FLAG_TYPE_MASK = 7;
@@ -35,7 +32,7 @@
   const TYPE_UNICODE = 1;  // Value can be read but is never written.
   // TYPE_PICKLED = 2
   const TYPE_INT = 3;
-  // TYPE_LONG = 4
+  const TYPE_LONG = 4;
   const TYPE_BOOL = 5;
   // These flags are unique to PHP:
   const TYPE_FLOAT = 6;
@@ -80,6 +77,7 @@
           return (double) $value;
         }
       case self::TYPE_INT:
+      case self::TYPE_LONG:
         return (integer) $value;
       case self::TYPE_STR:
       case self::TYPE_UNICODE:
diff --git a/php/sdk/google/appengine/runtime/Memcached.php b/php/sdk/google/appengine/runtime/Memcached.php
index 4774951..72dbf07 100644
--- a/php/sdk/google/appengine/runtime/Memcached.php
+++ b/php/sdk/google/appengine/runtime/Memcached.php
@@ -38,12 +38,6 @@
 use google\appengine\runtime\Error;
 use google\appengine\runtime\MemcacheUtils;
 
-require_once 'google/appengine/api/memcache/memcache_service_pb.php';
-require_once 'google/appengine/runtime/MemcacheUtils.php';
-require_once 'google/appengine/runtime/Memcache.php';
-require_once 'google/appengine/runtime/ApiProxy.php';
-require_once 'google/appengine/runtime/Error.php';
-
 class Memcached {
 
   /**
@@ -876,7 +870,7 @@
   /**
    * Replace is similar to Memcache::set(), but the operation will fail if the
    * key is not found on the server.
-   * 
+   *
    * @param string $key The key under which to store the value.
    * @param mixed $value The value to store.
    * @param int $expiration The expiration time, defaults to 0.
@@ -891,7 +885,7 @@
   }
 
   /**
-   * @see Memecached::replace()
+   * @see Memcached::replace()
    *
    * @param string $server_key This parameter is ignored.
    * @param string $key The key under which to store the value.
diff --git a/php/sdk/google/appengine/runtime/OverQuotaError.php b/php/sdk/google/appengine/runtime/OverQuotaError.php
index e9ffb8a..614301b 100644
--- a/php/sdk/google/appengine/runtime/OverQuotaError.php
+++ b/php/sdk/google/appengine/runtime/OverQuotaError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy calls when they have been blocked due to a lack of
  * available quota.
diff --git a/php/sdk/google/appengine/runtime/RPCFailedError.php b/php/sdk/google/appengine/runtime/RPCFailedError.php
index 3c1948d..b83f7fd 100644
--- a/php/sdk/google/appengine/runtime/RPCFailedError.php
+++ b/php/sdk/google/appengine/runtime/RPCFailedError.php
@@ -19,10 +19,8 @@
 
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
- * Thrown by APIProxy calls when the RPC to the application server 
+ * Thrown by APIProxy calls when the RPC to the application server
  * fails.
  */
 class RPCFailedError extends Error {
diff --git a/php/sdk/google/appengine/runtime/RealApiProxy.php b/php/sdk/google/appengine/runtime/RealApiProxy.php
index ec3a831..84be23d 100644
--- a/php/sdk/google/appengine/runtime/RealApiProxy.php
+++ b/php/sdk/google/appengine/runtime/RealApiProxy.php
@@ -16,12 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/ApiProxyBase.php';
-
-require_once 'google/appengine/runtime/ApplicationError.php';
-require_once 'google/appengine/runtime/CapabilityDisabledError.php';
-require_once 'google/appengine/runtime/FeatureNotEnabledError.php';
-
 class RealApiProxy extends ApiProxyBase {
   // Specifying a value of -1.0 for the default deadline ensures that the
   // default for each package is used when making the call in the App Server.
diff --git a/php/sdk/google/appengine/runtime/RemoteApiProxy.php b/php/sdk/google/appengine/runtime/RemoteApiProxy.php
index 6859e88..d0f1613 100644
--- a/php/sdk/google/appengine/runtime/RemoteApiProxy.php
+++ b/php/sdk/google/appengine/runtime/RemoteApiProxy.php
@@ -16,21 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/ApiProxyBase.php';
-require_once 'google/appengine/ext/remote_api/remote_api_pb.php';
-
-require_once 'google/appengine/runtime/CallNotFoundError.php';
-require_once 'google/appengine/runtime/ArgumentError.php';
-require_once 'google/appengine/runtime/DeadlineExceededError.php';
-require_once 'google/appengine/runtime/CancelledError.php';
-require_once 'google/appengine/runtime/ApplicationError.php';
-require_once 'google/appengine/runtime/OverQuotaError.php';
-require_once 'google/appengine/runtime/RequestTooLargeError.php';
-require_once 'google/appengine/runtime/ResponseTooLargeError.php';
-require_once 'google/appengine/runtime/CapabilityDisabledError.php';
-require_once 'google/appengine/runtime/FeatureNotEnabledError.php';
-require_once 'google/appengine/runtime/RPCFailedError.php';
-
 use google\appengine\ext\remote_api\Request;
 use google\appengine\ext\remote_api\Response;
 use google\appengine\runtime\RPCFailedError;
diff --git a/php/sdk/google/appengine/runtime/RequestTooLargeError.php b/php/sdk/google/appengine/runtime/RequestTooLargeError.php
index 60a3a0f..95a568a 100644
--- a/php/sdk/google/appengine/runtime/RequestTooLargeError.php
+++ b/php/sdk/google/appengine/runtime/RequestTooLargeError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy calls if the request was too large.
  */
diff --git a/php/sdk/google/appengine/runtime/ResponseTooLargeError.php b/php/sdk/google/appengine/runtime/ResponseTooLargeError.php
index 0f2daa8..11f7174 100644
--- a/php/sdk/google/appengine/runtime/ResponseTooLargeError.php
+++ b/php/sdk/google/appengine/runtime/ResponseTooLargeError.php
@@ -16,8 +16,6 @@
  */
 namespace google\appengine\runtime;
 
-require_once 'google/appengine/runtime/Error.php';
-
 /**
  * Thrown by APIProxy calls if the response was too large.
  */
diff --git a/php/sdk/google/appengine/runtime/Setup.php b/php/sdk/google/appengine/runtime/Setup.php
index 20a5ba8..ce214cb 100644
--- a/php/sdk/google/appengine/runtime/Setup.php
+++ b/php/sdk/google/appengine/runtime/Setup.php
@@ -18,9 +18,10 @@
  * Performs any required initialization before the user's script is run.
  */
 
+// Ensure that the class autoloader is the first include.
+require_once 'google/appengine/runtime/autoloader.php';
 require_once 'google/appengine/runtime/Memcache.php';
 require_once 'google/appengine/runtime/Memcached.php';
-require_once 'google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapper.php';
 require_once 'google/appengine/ext/session/MemcacheSessionHandler.php';
 require_once 'google/appengine/api/mail/MailService.php';
 
@@ -38,3 +39,19 @@
 stream_wrapper_register('gs',
     '\google\appengine\ext\cloud_storage_streams\CloudStorageStreamWrapper',
     $url_flags);
+
+// Map core PHP function implementations to proper function names. All function
+// implementations should be prefixed with an underscore. The implementations
+// should be mapped to the real (un-prefixed) function name and lazy-loaded.
+// The underscore prefixed functions may then be used for unit testing on an
+// unmodified PHP interpreter which will not allow functions to be redeclared.
+//
+// Additionally due to e2e tests also running on devappserver with an
+// unmodified PHP interpreter the function definitions must be defined
+// conditionally and those e2e tests excluded from devappserver.
+if (strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false) {
+  function gethostname() {
+    require_once 'google/appengine/runtime/GetHostName.php';
+    return google\appengine\runtime\_gethostname();
+  }
+}
diff --git a/php/sdk/google/appengine/runtime/autoloader.php b/php/sdk/google/appengine/runtime/autoloader.php
new file mode 100644
index 0000000..ccf8d3f
--- /dev/null
+++ b/php/sdk/google/appengine/runtime/autoloader.php
@@ -0,0 +1,449 @@
+<?php
+/**
+ * Google App Engine SDK Auto Loader.
+ *
+ * !!! THIS FILE IS AUTO GENERATED - DO NOT EDIT !!!
+ */
+
+namespace google\appengine\runtime;
+
+final class ClassLoader {
+  private static $classmap = null;
+
+  public static function loadClass($class_name) {
+    if (self::$classmap === null) {
+      self::$classmap = [
+        'google\appengine\util\stringutil' => 'google/appengine/util/string_util.php',
+        'google\appengine\util\arrayutil' => 'google/appengine/util/array_util.php',
+        'google\appengine\testing\apiproxytestbase' => 'google/appengine/testing/ApiProxyTestBase.php',
+        'google\appengine\testing\apiproxymock' => 'google/appengine/testing/ApiProxyMock.php',
+        'google\appengine\testing\apicallarguments' => 'google/appengine/testing/ApiCallArguments.php',
+        'google\appengine\runtime\responsetoolargeerror' => 'google/appengine/runtime/ResponseTooLargeError.php',
+        'google\appengine\runtime\requesttoolargeerror' => 'google/appengine/runtime/RequestTooLargeError.php',
+        'google\appengine\runtime\remoteapiproxy' => 'google/appengine/runtime/RemoteApiProxy.php',
+        'google\appengine\runtime\realapiproxy' => 'google/appengine/runtime/RealApiProxy.php',
+        'google\appengine\runtime\rpcfailederror' => 'google/appengine/runtime/RPCFailedError.php',
+        'google\appengine\runtime\overquotaerror' => 'google/appengine/runtime/OverQuotaError.php',
+        'memcached' => 'google/appengine/runtime/Memcached.php',
+        'google\appengine\runtime\memcacheutils' => 'google/appengine/runtime/MemcacheUtils.php',
+        'memcache' => 'google/appengine/runtime/Memcache.php',
+        'google\appengine\runtime\featurenotenablederror' => 'google/appengine/runtime/FeatureNotEnabledError.php',
+        'google\appengine\runtime\error' => 'google/appengine/runtime/Error.php',
+        'google\appengine\runtime\deadlineexceedederror' => 'google/appengine/runtime/DeadlineExceededError.php',
+        'google\appengine\runtime\capabilitydisablederror' => 'google/appengine/runtime/CapabilityDisabledError.php',
+        'google\appengine\runtime\cancellederror' => 'google/appengine/runtime/CancelledError.php',
+        'google\appengine\runtime\callnotfounderror' => 'google/appengine/runtime/CallNotFoundError.php',
+        'google\appengine\runtime\argumenterror' => 'google/appengine/runtime/ArgumentError.php',
+        'google\appengine\runtime\applicationerror' => 'google/appengine/runtime/ApplicationError.php',
+        'google\appengine\runtime\apiproxybase' => 'google/appengine/runtime/ApiProxyBase.php',
+        'google\appengine\runtime\apiproxy' => 'google/appengine/runtime/ApiProxy.php',
+        'google\net\protocolmessage' => 'google/appengine/runtime/proto/ProtocolMessage.php',
+        'google\net\protocolbufferencodeerror' => 'google/appengine/runtime/proto/ProtocolBufferEncodeError.php',
+        'google\net\protocolbufferdecodeerror' => 'google/appengine/runtime/proto/ProtocolBufferDecodeError.php',
+        'google\net\encoder' => 'google/appengine/runtime/proto/Encoder.php',
+        'google\net\decoder' => 'google/appengine/runtime/proto/Decoder.php',
+        'google\appengine\ext\session\memcachecontainer' => 'google/appengine/ext/session/MemcacheSessionHandler.php',
+        'google\appengine\ext\session\memcachesessionhandler' => 'google/appengine/ext/session/MemcacheSessionHandler.php',
+        'google\appengine\ext\remote_api\request' => 'google/appengine/ext/remote_api/remote_api_pb.php',
+        'google\appengine\ext\remote_api\applicationerror' => 'google/appengine/ext/remote_api/remote_api_pb.php',
+        'google\appengine\ext\remote_api\response' => 'google/appengine/ext/remote_api/remote_api_pb.php',
+        'google\appengine\ext\remote_api\transactionrequest\precondition' => 'google/appengine/ext/remote_api/remote_api_pb.php',
+        'google\appengine\ext\remote_api\transactionrequest' => 'google/appengine/ext/remote_api/remote_api_pb.php',
+        'google\appengine\ext\remote_api\transactionqueryresult' => 'google/appengine/ext/remote_api/remote_api_pb.php',
+        'google\appengine\ext\cloud_storage_streams\httpresponse' => 'google/appengine/ext/cloud_storage_streams/HttpResponse.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstoragewriteclient' => 'google/appengine/ext/cloud_storage_streams/CloudStorageWriteClient.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstorageurlstatclient' => 'google/appengine/ext/cloud_storage_streams/CloudStorageUrlStatClient.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstoragestreamwrapper' => 'google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapper.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstoragerenameclient' => 'google/appengine/ext/cloud_storage_streams/CloudStorageRenameClient.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstoragereadclient' => 'google/appengine/ext/cloud_storage_streams/CloudStorageReadClient.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstoragedirectoryclient' => 'google/appengine/ext/cloud_storage_streams/CloudStorageDirectoryClient.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstoragedeleteclient' => 'google/appengine/ext/cloud_storage_streams/CloudStorageDeleteClient.php',
+        'google\appengine\ext\cloud_storage_streams\cloudstorageclient' => 'google/appengine/ext/cloud_storage_streams/CloudStorageClient.php',
+        'storage_onestore_v3\snapshot\status' => 'google/appengine/datastore/snapshot_pb.php',
+        'storage_onestore_v3\snapshot' => 'google/appengine/datastore/snapshot_pb.php',
+        'google\appengine\datastore\v4\partitionid\constants' => 'google/appengine/datastore/entity_v4_pb.php',
+        'google\appengine\datastore\v4\partitionid' => 'google/appengine/datastore/entity_v4_pb.php',
+        'google\appengine\datastore\v4\key\pathelement' => 'google/appengine/datastore/entity_v4_pb.php',
+        'google\appengine\datastore\v4\key' => 'google/appengine/datastore/entity_v4_pb.php',
+        'google\appengine\datastore\v4\value' => 'google/appengine/datastore/entity_v4_pb.php',
+        'google\appengine\datastore\v4\property' => 'google/appengine/datastore/entity_v4_pb.php',
+        'google\appengine\datastore\v4\entity' => 'google/appengine/datastore/entity_v4_pb.php',
+        'storage_onestore_v3\propertyvalue\pointvalue' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\propertyvalue\uservalue' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\propertyvalue\referencevalue\pathelement' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\propertyvalue\referencevalue' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\propertyvalue' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\property\meaning' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\property\ftstokenizationoption' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\property' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\path\element' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\path' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\reference' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\user' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\entityproto\kind' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\entityproto' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\compositeproperty' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\index\property\direction' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\index\property' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\index' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\compositeindex\state' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\compositeindex' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\indexpostfix\indexvalue' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\indexpostfix' => 'google/appengine/datastore/entity_pb.php',
+        'storage_onestore_v3\indexposition' => 'google/appengine/datastore/entity_pb.php',
+        'google\appengine\datastore\v4\error\errorcode' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\error' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\entityresult\resulttype' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\entityresult' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\query' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\kindexpression' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\propertyreference' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\propertyexpression\aggregationfunction' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\propertyexpression' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\propertyorder\direction' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\propertyorder' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\filter' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\compositefilter\operator' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\compositefilter' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\propertyfilter\operator' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\propertyfilter' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\gqlquery' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\gqlqueryarg' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\queryresultbatch\moreresultstype' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\queryresultbatch' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\mutation' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\mutationresult' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\readoptions\readconsistency' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\readoptions' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\lookuprequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\lookupresponse' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\runqueryrequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\runqueryresponse' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\continuequeryrequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\continuequeryresponse' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\begintransactionrequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\begintransactionresponse' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\rollbackrequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\rollbackresponse' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\commitrequest\mode' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\commitrequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\commitresponse' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\allocateidsrequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\allocateidsresponse' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine\datastore\v4\writerequest' => 'google/appengine/datastore/datastore_v4_pb.php',
+        'google\appengine_datastore_v3\transaction' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\query\hint' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\query\filter\operator' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\query\filter' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\query\order\direction' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\query\order' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\query' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compiledquery\primaryscan' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compiledquery\mergejoinscan' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compiledquery\entityfilter' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compiledquery' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compiledcursor\position\indexvalue' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compiledcursor\position' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compiledcursor' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\cursor' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\error\errorcode' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\error' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\cost\commitcost' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\cost' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\getrequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\getresponse\entity' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\getresponse' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\putrequest\autoidpolicy' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\putrequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\putresponse' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\touchrequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\touchresponse' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\deleterequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\deleteresponse' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\nextrequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\queryresult' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\allocateidsrequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\allocateidsresponse' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\compositeindices' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\addactionsrequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\addactionsresponse' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\begintransactionrequest' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\commitresponse\version' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\commitresponse' => 'google/appengine/datastore/datastore_v3_pb.php',
+        'google\appengine_datastore_v3\action' => 'google/appengine/datastore/action_pb.php',
+        'google\appengine\userserviceerror\errorcode' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\userserviceerror' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\createloginurlrequest' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\createloginurlresponse' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\createlogouturlrequest' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\createlogouturlresponse' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\getoauthuserrequest' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\getoauthuserresponse' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\checkoauthsignaturerequest' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\checkoauthsignatureresponse' => 'google/appengine/api/user_service_pb.php',
+        'google\appengine\urlfetchserviceerror\errorcode' => 'google/appengine/api/urlfetch_service_pb.php',
+        'google\appengine\urlfetchserviceerror' => 'google/appengine/api/urlfetch_service_pb.php',
+        'google\appengine\urlfetchrequest\requestmethod' => 'google/appengine/api/urlfetch_service_pb.php',
+        'google\appengine\urlfetchrequest\header' => 'google/appengine/api/urlfetch_service_pb.php',
+        'google\appengine\urlfetchrequest' => 'google/appengine/api/urlfetch_service_pb.php',
+        'google\appengine\urlfetchresponse\header' => 'google/appengine/api/urlfetch_service_pb.php',
+        'google\appengine\urlfetchresponse' => 'google/appengine/api/urlfetch_service_pb.php',
+        'google\appengine\mailserviceerror\errorcode' => 'google/appengine/api/mail_service_pb.php',
+        'google\appengine\mailserviceerror' => 'google/appengine/api/mail_service_pb.php',
+        'google\appengine\mailattachment' => 'google/appengine/api/mail_service_pb.php',
+        'google\appengine\mailheader' => 'google/appengine/api/mail_service_pb.php',
+        'google\appengine\mailmessage' => 'google/appengine/api/mail_service_pb.php',
+        'google\appengine\base\stringproto' => 'google/appengine/api/api_base_pb.php',
+        'google\appengine\base\integer32proto' => 'google/appengine/api/api_base_pb.php',
+        'google\appengine\base\integer64proto' => 'google/appengine/api/api_base_pb.php',
+        'google\appengine\base\boolproto' => 'google/appengine/api/api_base_pb.php',
+        'google\appengine\base\doubleproto' => 'google/appengine/api/api_base_pb.php',
+        'google\appengine\base\bytesproto' => 'google/appengine/api/api_base_pb.php',
+        'google\appengine\base\voidproto' => 'google/appengine/api/api_base_pb.php',
+        'google\appengine\api\users\usersexception' => 'google/appengine/api/users/UsersException.php',
+        'google\appengine\api\users\userservice' => 'google/appengine/api/users/UserService.php',
+        'google\appengine\api\users\user' => 'google/appengine/api/users/User.php',
+        'google\appengine\taskqueueserviceerror\errorcode' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueserviceerror' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskpayload' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueretryparameters' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueacl' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuehttpheader' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuemode\mode' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuemode' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueaddrequest\requestmethod' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueaddrequest\header' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueaddrequest\crontimetable' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueaddrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueaddresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuebulkaddrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuebulkaddresponse\taskresult' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuebulkaddresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuedeleterequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuedeleteresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueforcerunrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueforcerunresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueupdatequeuerequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueupdatequeueresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchqueuesrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchqueuesresponse\queue' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchqueuesresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchqueuestatsrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuescannerqueueinfo' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchqueuestatsresponse\queuestats' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchqueuestatsresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuepausequeuerequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuepausequeueresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuepurgequeuerequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuepurgequeueresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuedeletequeuerequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuedeletequeueresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuedeletegrouprequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuedeletegroupresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequerytasksrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequerytasksresponse\task\requestmethod' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequerytasksresponse\task\header' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequerytasksresponse\task\crontimetable' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequerytasksresponse\task\runlog' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequerytasksresponse\task' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequerytasksresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchtaskrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuefetchtaskresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueupdatestoragelimitrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueueupdatestoragelimitresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequeryandowntasksrequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequeryandowntasksresponse\task' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuequeryandowntasksresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuemodifytaskleaserequest' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\taskqueuemodifytaskleaseresponse' => 'google/appengine/api/taskqueue/taskqueue_service_pb.php',
+        'google\appengine\api\taskqueue\transienttaskqueueexception' => 'google/appengine/api/taskqueue/TransientTaskQueueException.php',
+        'google\appengine\api\taskqueue\taskqueueexception' => 'google/appengine/api/taskqueue/TaskQueueException.php',
+        'google\appengine\api\taskqueue\taskalreadyexistsexception' => 'google/appengine/api/taskqueue/TaskAlreadyExistsException.php',
+        'google\appengine\api\taskqueue\pushtask' => 'google/appengine/api/taskqueue/PushTask.php',
+        'google\appengine\api\taskqueue\pushqueue' => 'google/appengine/api/taskqueue/PushQueue.php',
+        'google\appengine\modulesserviceerror\errorcode' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\modulesserviceerror' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getmodulesrequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getmodulesresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getversionsrequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getversionsresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getdefaultversionrequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getdefaultversionresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getnuminstancesrequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\getnuminstancesresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\setnuminstancesrequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\setnuminstancesresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\startmodulerequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\startmoduleresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\stopmodulerequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\stopmoduleresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\gethostnamerequest' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\gethostnameresponse' => 'google/appengine/api/modules/modules_service_pb.php',
+        'google\appengine\api\modules\transientmodulesexception' => 'google/appengine/api/modules/TransientModulesException.php',
+        'google\appengine\api\modules\modulesservice' => 'google/appengine/api/modules/ModulesService.php',
+        'google\appengine\api\modules\modulesexception' => 'google/appengine/api/modules/ModulesException.php',
+        'google\appengine\api\modules\invalidmodulestateexception' => 'google/appengine/api/modules/InvalidModuleStateException.php',
+        'google\appengine\memcacheserviceerror\errorcode' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcacheserviceerror' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\appoverride' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachegetrequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachegetresponse\item' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachegetresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachesetrequest\setpolicy' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachesetrequest\item' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachesetrequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachesetresponse\setstatuscode' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachesetresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachedeleterequest\item' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachedeleterequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachedeleteresponse\deletestatuscode' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachedeleteresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcacheincrementrequest\direction' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcacheincrementrequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcacheincrementresponse\incrementstatuscode' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcacheincrementresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachebatchincrementrequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachebatchincrementresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcacheflushrequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcacheflushresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachestatsrequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\mergednamespacestats' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachestatsresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachegrabtailrequest' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachegrabtailresponse\item' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\memcachegrabtailresponse' => 'google/appengine/api/memcache/memcache_service_pb.php',
+        'google\appengine\api\mail\message' => 'google/appengine/api/mail/Message.php',
+        'google\appengine\api\mail\basemessage' => 'google/appengine/api/mail/BaseMessage.php',
+        'google\appengine\api\mail\adminmessage' => 'google/appengine/api/mail/AdminMessage.php',
+        'google\appengine\logserviceerror\errorcode' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logserviceerror' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\userapplogline' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\userapploggroup' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\flushrequest' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\setstatusrequest' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logoffset' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logline' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\requestlog' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logmoduleversion' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logreadrequest' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logreadresponse' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logusagerecord' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logusagerequest' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\logusageresponse' => 'google/appengine/api/logservice/log_service_pb.php',
+        'google\appengine\api\log\requestlogiterator' => 'google/appengine/api/log/RequestLogIterator.php',
+        'google\appengine\api\log\requestlog' => 'google/appengine/api/log/RequestLog.php',
+        'google\appengine\api\log\logservice' => 'google/appengine/api/log/LogService.php',
+        'google\appengine\api\log\apiproxyaccess' => 'google/appengine/api/log/LogService.php',
+        'google\appengine\api\log\logexception' => 'google/appengine/api/log/LogException.php',
+        'google\appengine\api\log\applogline' => 'google/appengine/api/log/AppLogLine.php',
+        'google\appengine\imagesserviceerror\errorcode' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagesserviceerror' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagesservicetransform\type' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagesservicetransform' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\transform' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagedata' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\inputsettings\orientation_correction_type' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\inputsettings' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\outputsettings\mime_type' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\outputsettings' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagestransformrequest' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagestransformresponse' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\compositeimageoptions\anchor' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\compositeimageoptions' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagescanvas' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagescompositerequest' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagescompositeresponse' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imageshistogramrequest' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imageshistogram' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imageshistogramresponse' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagesgeturlbaserequest' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagesgeturlbaseresponse' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagesdeleteurlbaserequest' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\imagesdeleteurlbaseresponse' => 'google/appengine/api/images/images_service_pb.php',
+        'google\appengine\files\fileserviceerrors\errorcode' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\fileserviceerrors' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\keyvalue' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\keyvalues' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\filecontenttype\contenttype' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\filecontenttype' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\createrequest\parameter' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\createrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\createresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\openrequest\openmode' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\openrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\openresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\closerequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\closeresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\filestat' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\statrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\statresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\appendrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\appendresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\deleterequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\deleteresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\readrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\readresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\readkeyvaluerequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\readkeyvalueresponse\keyvalue' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\readkeyvalueresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shuffleenums\inputformat' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shuffleenums\outputformat' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shuffleenums\status' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shuffleenums' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shuffleinputspecification' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shuffleoutputspecification' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shufflerequest\callback' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shufflerequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\shuffleresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\getshufflestatusrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\getshufflestatusresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\getcapabilitiesrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\getcapabilitiesresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\finalizerequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\finalizeresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\getdefaultgsbucketnamerequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\getdefaultgsbucketnameresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\listdirrequest' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\files\listdirresponse' => 'google/appengine/api/files/file_service_pb.php',
+        'google\appengine\api\cloud_storage\cloudstoragetools' => 'google/appengine/api/cloud_storage/CloudStorageTools.php',
+        'google\appengine\api\cloud_storage\cloudstorageexception' => 'google/appengine/api/cloud_storage/CloudStorageException.php',
+        'google\appengine\blobstoreserviceerror\errorcode' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\blobstoreserviceerror' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\createuploadurlrequest' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\createuploadurlresponse' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\deleteblobrequest' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\fetchdatarequest' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\fetchdataresponse' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\cloneblobrequest' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\cloneblobresponse' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\decodeblobkeyrequest' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\decodeblobkeyresponse' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\createencodedgooglestoragekeyrequest' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\createencodedgooglestoragekeyresponse' => 'google/appengine/api/blobstore/blobstore_service_pb.php',
+        'google\appengine\appidentityserviceerror\errorcode' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\appidentityserviceerror' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\signforapprequest' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\signforappresponse' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getpubliccertificateforapprequest' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\publiccertificate' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getpubliccertificateforappresponse' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getserviceaccountnamerequest' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getserviceaccountnameresponse' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getaccesstokenrequest' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getaccesstokenresponse' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getdefaultgcsbucketnamerequest' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\getdefaultgcsbucketnameresponse' => 'google/appengine/api/app_identity/app_identity_service_pb.php',
+        'google\appengine\api\app_identity\publiccertificate' => 'google/appengine/api/app_identity/PublicCertificate.php',
+        'google\appengine\api\app_identity\appidentityservice' => 'google/appengine/api/app_identity/AppIdentityService.php',
+        'google\appengine\api\app_identity\appidentityexception' => 'google/appengine/api/app_identity/AppIdentityException.php',
+      ];
+    }
+    $class_name = strtolower($class_name);
+    if (array_key_exists($class_name, self::$classmap)) {
+      require self::$classmap[$class_name];
+    }
+  }
+}
+
+spl_autoload_register(__NAMESPACE__ . '\ClassLoader::loadClass');
+
diff --git a/php/sdk/google/appengine/util/array_util.php b/php/sdk/google/appengine/util/array_util.php
index e0d28a6..731267e 100644
--- a/php/sdk/google/appengine/util/array_util.php
+++ b/php/sdk/google/appengine/util/array_util.php
@@ -20,19 +20,20 @@
  */
 namespace google\appengine\util;
 
-/**
- * Find an item in a hash table by a key value, or return null if not found.
- *
- * @param array $array - The array to search
- * @param mixed $key - The key to search for.
- *
- * @return mixed The value of the item in the array with the given key, or null
- * if not found.
- */
-function findByKeyOrNull($array, $key) {
-  if (array_key_exists($key, $array)) {
-    return $array[$key];
+final class ArrayUtil {
+  /**
+   * Find an item in a hash table by a key value, or return null if not found.
+   *
+   * @param array $array - The array to search
+   * @param mixed $key - The key to search for.
+   *
+   * @return mixed The value of the item in the array with the given key,
+   * or null if not found.
+   */
+  public static function findByKeyOrNull($array, $key) {
+    if (array_key_exists($key, $array)) {
+      return $array[$key];
+    }
+    return null;
   }
-  return null;
 }
-
diff --git a/php/sdk/google/appengine/util/string_util.php b/php/sdk/google/appengine/util/string_util.php
index 9a3fbb0..bd287f4 100644
--- a/php/sdk/google/appengine/util/string_util.php
+++ b/php/sdk/google/appengine/util/string_util.php
@@ -20,44 +20,47 @@
  */
 namespace google\appengine\util;
 
-/**
- * Return true if the first paramater contains the second parameter at the end.
- *
- * @param string $input The input string which may contain the suffix.
- * @param string $suffix The string to look for at the end of the input.
- *
- * @return boolean <code>true</code> iff the input contains the suffix at the
- * end.
- */
-function endsWith($input, $suffix) {
-  return substr($input, -strlen($suffix)) === $suffix;
-}
+final class StringUtil {
+  /**
+   * Return true if the first paramater contains the second parameter at the
+   * end.
+   *
+   * @param string $input The input string which may contain the suffix.
+   * @param string $suffix The string to look for at the end of the input.
+   *
+   * @return boolean <code>true</code> iff the input contains the suffix at the
+   * end.
+   */
+  public static function endsWith($input, $suffix) {
+    return substr($input, -strlen($suffix)) === $suffix;
+  }
 
-/**
- * @param string $input The string which may contain the prefix at the start.
- * @param string $prefix The string to look for at the start of the input.
- *
- * @return boolean <code>true</code> iff the input contains the prefix at the
- * start.
- */
-function startsWith($input, $prefix) {
-  return substr($input, 0, strlen($prefix)) === $prefix;
-}
+  /**
+   * @param string $input The string which may contain the prefix at the start.
+   * @param string $prefix The string to look for at the start of the input.
+   *
+   * @return boolean <code>true</code> iff the input contains the prefix at the
+   * start.
+   */
+  public static function startsWith($input, $prefix) {
+    return substr($input, 0, strlen($prefix)) === $prefix;
+  }
 
-/**
- * @param string $input The string which may not be url safe.
- *
- * @return string A Base64 encoded url safe string.
- */
-function base64UrlEncode($input) {
-  return strtr(base64_encode($input), '+/=', '-_,');
-}
+  /**
+   * @param string $input The string which may not be url safe.
+   *
+   * @return string A Base64 encoded url safe string.
+   */
+  public static function base64UrlEncode($input) {
+    return strtr(base64_encode($input), '+/=', '-_,');
+  }
 
-/**
- * @param string $input The url safe Base64 encoded string.
- *
- * @return string The original string which may not be url safe.
- */
-function base64UrlDecode($input) {
-  return base64_decode(strtr($input, '-_,', '+/='));
+  /**
+   * @param string $input The url safe Base64 encoded string.
+   *
+   * @return string The original string which may not be url safe.
+   */
+  public static function base64UrlDecode($input) {
+    return base64_decode(strtr($input, '-_,', '+/='));
+  }
 }
diff --git a/remote_api_shell.py b/remote_api_shell.py
index b15365d..2af168b 100644
--- a/remote_api_shell.py
+++ b/remote_api_shell.py
@@ -106,6 +106,9 @@
   os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.2'),
   os.path.join(DIR_PATH, 'lib', 'yaml', 'lib'),
   os.path.join(DIR_PATH, 'lib', 'simplejson'),
+  os.path.join(DIR_PATH, 'lib', 'rsa'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1'),
+  os.path.join(DIR_PATH, 'lib', 'pyasn1_modules'),
 ]
 
 API_SERVER_EXTRA_PATHS = [