# HG changeset patch # User Todd Larsen # Date 1221532113 0 # Node ID f2e327a7c5dead26682066a7b1e3fa0695a40377 # Parent 37505d64e57bcbfcbc5030cbb2baa8597e9d5f3b Load ../google_appengine into trunk/thirdparty/google_appengine. diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/README --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/RELEASE_NOTES --- 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 =============================== diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/VERSION --- 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'] diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/appcfg.py --- 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()) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/dev_appserver.py --- 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()) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/api_base_pb.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/appinfo.py --- 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'\.\.|^\./|\.$|/\./|^-') diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/datastore.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/datastore_admin.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/datastore_file_stub.py --- 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(): diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/datastore_types.py --- 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'%s %s' % (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)) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/images/images_service_pb.py --- 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_ diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/mail.py --- 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): diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/mail_service_pb.py --- 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): diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/memcache/__init__.py --- 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: diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/memcache/memcache_service_pb.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/memcache/memcache_stub.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/urlfetch.py --- 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: diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/urlfetch_service_pb.py --- 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): diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/urlfetch_stub.py --- 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' diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/user_service_pb.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/validation.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/api/yaml_errors.py --- 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)) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/datastore/datastore_pb.py --- 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): diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/datastore/entity_pb.py --- 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_ diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/admin/__init__.py --- 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, '', '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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/admin/templates/base.html --- 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 %} -
+
@@ -19,7 +19,7 @@
-

Development Console

+

{{ application_name }} Development Console

@@ -36,6 +36,7 @@
diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/admin/templates/css/ae.css --- 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 +} diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/admin/templates/css/memcache.css --- /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; +} diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/admin/templates/datastore.html --- 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 @@ {% endblock %} diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/admin/templates/memcache.html --- /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 %} + +{% endblock %} + +{% block breadcrumbs %} + Memcache Viewer +{% endblock %} + +{% block body %} +

Memcache Viewer

+ +{% if message %} +
+{{ message|escape }} +
+{% endif %} + +{% if show_stats %} +
+
    +
  • Hit ratio: {{ hitratio }}% ({{ stats.hits }} hit{{ stats.hits|pluralize }} and {{ stats.misses }} miss{{ stats.misses|pluralize:"es" }})
  • +
  • Size of cache: {{ stats.items }} item{{ stats.items|pluralize }}, {{ stats.bytes|filesizeformat }} +
    + +
    +
  • +
  • Cache contains items up to {{ oldest_item_age|timesince }} old.
  • +
+
+ + +{% endif %} + +{% if show_value %} +{% if key_exists %} +{% ifequal type "error" %} +
Error fetching {{ key|escape }}: {{ value|escape }}
+{% else %} +
+
"{{ key|escape }}" is a {{ type|escape }}:
+
{{ value|escape }}
+
+{% endifequal %} +{% else %} +
No such key: {{ key|escape }}
+{% endif %} +{% endif %} + +{% if show_valueform %} +
+
+ + + + + + + + + + + + + + + + + +
Key + + {{ key|escape }} +
Type + {% if key_exists %} + + {{ type|escape }} + {% else %} + + {% endif %} +
Value
+ +
  + {% if writable %} + + {% endif %} + +
+
+
+{% endif %} + +{% endblock %} + +{% block final %} + +{% endblock %} diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/bulkload/__init__.py --- 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. diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/db/__init__.py --- 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: diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/gql/__init__.py --- 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)) + diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/search/__init__.py --- 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 diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/webapp/__init__.py --- 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 diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/ext/zipserve/__init__.py --- /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() diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/runtime/__init__.py --- 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. + """ diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/tools/appcfg.py --- 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.") diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/tools/dev_appserver.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/tools/dev_appserver_main.py --- 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) diff -r 37505d64e57b -r f2e327a7c5de thirdparty/google_appengine/google/appengine/tools/os_compat.py --- /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.""" +