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